How I Created (and subsequently destroyed it)

on johanv's blog

Update 2022-08-05

I have since moved to, read about it here. Keep reading if you want, but most of the links in this article are old.

The Backstory

A few weeks ago, I was bemoaning the fact that my website URL was too long (, so I went on the search for a shorter (and more trendy ;) domain name. I found on namesilo for only a few dollars a year. Then I decided to use DigitalOcean to host the new website since it's only $5 a month and it provides a lot of flexibility for setting up web services.

As I was creating a droplet (that's what DigitalOcean calls their cloud virtual machines), I noticed a "Marketplace" tab with dozens of preconfigured options! After doing my reading on the ones that seemed interesting, I chose to go with Dokku because it advertised easy installation of separate web apps. And little did I know that I had found my new favorite software project!

As I started to build the site, I was amazed at the awesome features of Dokku. Deploying an app is really easy - just create some files, initialize them as a git repo, and push the repo to Dokku, which takes care of the rest. Each app is on a separate subdomain (e.g.,,, etc.), and Dokku manages each one, keeping track of the virtual hosts, docker containers, etc. behind the scenes. It even has a Let's Encrypt module to add https with only two commands!

I haven't been this impressed with a software project since I discovered that you can install almost anything in the Arch User Repository (AUR). Oh wait, turns out Dokku is in the AUR as well - a match made in heaven! Maybe I'll migrate to an Arch server in the future... or install it on Manjaro locally to easily test web apps.

The Update

The first thing I did was log in and update the system:

apt update
apt dist-upgrade
apt-get install -qq -y dokku herokuish sshcommand plugn

The Main Site

For the main site, I just copied my old site over and pushed it to Dokku. Most of the links don't work, and I plan to rework it in the future anyway (adding mobile support as well).

mkdir main-site
cd main-site
git init
git remote add dokku
cp /path/to/old-site/{index,header,footer}.php .
git add --all
git commit -m "importing php from old site"
git push dokku master

Let's Encrypt!

To enable HTTPS, I had to install a plugin, which is super easy and well documented:

ssh dokku plugin:install
ssh letsencrypt:cron-job --add #auto renew certs

Then I could set the "main-site" app's url to and enable HTTPS:

ssh domains:set main-site
ssh config:set --no-restart main-site DOKKU_LETSENCRYPT_EMAIL=my@email.address
ssh letsencrypt main-site

Messing Around

I added and removed some subdomains to get more familiar with Dokku. Here's the process for a subdomain at serving a static index.html page:

mkdir test1
cd test1
touch .static
nano index.html #add anything
git init
git remote add dokku
git add --all
git commit -m "test static site"
git push dokku master
ssh config:set --no-restart test1 DOKKU_LETSENCRYPT_EMAIL=my@email.address
ssh letsencrypt test1

Adding an SSH Key

At a certain point I wanted to switch to another local computer to continue working on the site, so I had to add another ssh key to Dokku. First, I generated the key:

[new computer] ~$ ssh-keygen -b 4096

Then I logged in as root from the old computer and added the public key to the root account by opening the file in nano and pasting the key as a new line:

[old computer] ~$ ssh
[] ~$ nano /root/.ssh/authorized_keys

Then I still had to add it to the dokku user, which stores it differently, with this command:

[new computer] ~$ cat ~/.ssh/ | ssh "sudo sshcommand acl-add dokku `hostname`"


After looking around for things that would run easily on Dokku that I could use to post content to my new website (wordpress, ghost, grav, etc.), I settled on WriteFreely since there was an awesome tutorial by Evan Walsh on how to set it up.

First I created a new directory and some config files:

mkdir blog
cd blog
cat <<EOF >Dockerfile
FROM writeas/writefreely

COPY config.ini .


CMD ["bin/writefreely"]

cat <<EOF >config.ini
hidden_host          = 
port                 = 8080
bind                 =
tls_cert_path        = 
tls_key_path         = 
templates_parent_dir = 
static_parent_dir    = 
pages_parent_dir     = 
keys_parent_dir      = 

type     = sqlite3
filename = db/writefreely.db
username = 
password = 
database = 
host     = localhost
port     = 3306

site_name         = blog
site_description  = 
host              =
theme             = write
disable_js        = false
webfonts          = true
single_user       = true
open_registration = false
min_username_len  = 3
max_blogs         = 3
federation        = true
public_stats      = true
private           = false
local_timeline    = true
user_invites      = user

These were mostly copied from the tutorial, but I changed the site's name, the site's URL, and I set it to a single user instance. Then I created the app:

ssh apps:create blog

And set up persistent storage to preserve the database and keys when rebuilding the app:

ssh mkdir -p /var/lib/dokku/data/storage/writefreely/{db,keys}
ssh chown -R bin:bin /var/lib/dokku/data/storage/writefreely/

ssh storage:mount blog /var/lib/dokku/data/storage/writefreely/keys:/go/keys
ssh storage:mount blog /var/lib/dokku/data/storage/writefreely/db:/go/db
ssh proxy:ports-set blog http:80:8080

Now it was time to push the app to Dokku:

git init
git add --all
git commit -m "init"
git remote add dokku
git push dokku master #this build fails

After that, the site was visible, but it displayed an internal error. Next I initialized the WriteFreely instance, and viewed the list of commands because I was curious how to manage users:

ssh run blog --help
ssh run blog --init-db
ssh run blog --gen-keys

With the app initialized, I rebuilt it and the internal error was replaced with an empty blog site:

ssh ps:rebuild blog

Since I set it up as a single user instance, I had to initialize an admin account to be able to log in (Of course I put a real password instead of just "password" :):

ssh run blog -create-admin johanv:password

Lastly, I enabled HTTPS (otherwise anyone could sniff my creds when logging in) and it worked without a hitch!

ssh config:set --no-restart blog DOKKU_LETSENCRYPT_EMAIL=my@email.address
ssh letsencrypt blog


So, that's how I got this site to its current state, thanks to DigitalOcean, WriteFreely, and most of all, Dokku. I have much migration from the old site to do, and many more subdomains planned. :)


Comprehensive Dokku Tutorial by Max Schmidt Let's Encrypt Cert for Main Domain WriteFreely for Dokku Permissions Issue in WriteFreely Adding a New SSH Key