/ Tech

Dokku + Ghost + AWS S3 + Cloudflare = ♥

Like the seasons, if one thing is guaranteed to change every couple of months, it's how I deploy this site. This is my latest method - and I think it's my best.

I don't actually enjoy the web side of development, particularly the front-end, and more often than not I simply view it as a "means to an ends". This means that when I get the chance to write some content for here, the last thing I want to do is go through a long drawn out process - nor do I want to fight with a convulated deployment process if I need to make a fix.

Naturally, this lazy streak has led to me experimenting with various ways of running this little blog; I've built minimalistic little custom tools, I've used off-the-shelf CMSs, and I've also tinkered with static site generators like Jekyll. Each one has left me frustrated one way or another!

With that in mind, all I want at the moment is something (a) simple, (b) cheap, and (c) effective.

Enter Ghost

Ghost is an excellent blogging platform written on NodeJS. It ticks the box for simplicity, and even has a recently released cross-platform desktop application.

One of the main attractions of Ghost was it's choice of Markdown for editing; as a developer I spend a good chunk of my time using Markdown for documentation - so it made quite a natural choice.

Not to mention that unlike other blogging platforms, the default theme for Ghost ("Casper" - available on github) is good enough to use out-of-the-box too. I spent around 3 or 4 hours modifying this theme to achieve what you currently see here - in fact, my modifications are also available on github.


With the choice of blogging platform out of the way, it was time to decide how to run it. Whilst DigitalOcean - my chosen VPS provider - offer a "one click" Ghost instance, I opted against it.

Firstly, I wanted a nicer theme deployment method than manually copying files to the server (or uploading .zip files via the UI). Secondly, I simply get a little uncomfortable with the "one click apps"!

To satisfy the requirement for keeping things "simple", I opted for Dokku - a self-hosted Platform-As-A-Service (PaaS), powered by Docker and utilising Heroku Buildpacks. This provides a brilliant workflow centered around Git, allowing me to do a simple git push deploy master, whilst also giving me a playground for other projects.

An additional advantage of utilising Dokku was the rather useful LetsEncrypt plugin - which takes care of SSL certificates automatically. It even adds a cron job to ensure that the certificates are renewed on time!

Getting Dokku and Ghost to play nicely wasn't difficult, although it did take some "creativity". There were two issues:

  1. The Dokku MySQL plugin didn't provide MySQL credentials in the way of individual variables for Host, Username, Password and Database - instead, it only provided a MySQL connection string. Ghost doesn't accept database connection strings in it's configuration.

  2. Upon installation, or even subsequent updates, you may be required to run migrations. There was no way of doing this automatically, and Ghost would simply fail to run - thus Dokku would count it as a failed deployment and rollback.

To mitigate these issues, I installed Ghost as an NPM module, and wrote a simple initiation script that would do some housekeeping before using the Ghost API to programatically kick it in to shape. You can see the script here.

Considerations with Dokku Deployment

To ensure deployment works correctly, I had to open additional ports on the firewall covering my VPS; as the VPS was now functioning as a Git server too. (Of course, you do run all your boxes behind a firewall too.. right?)

As Dokku is built upon Docker, extra care also needs to be taken with regards to persistance. Dokku does have the ability to handle persistent storage itself, and my VPS provider - DigitalOcean - does provide block storage, I've instead opted for Amazon S3 though. This is in part to allow me to transfer VPS providers with relative ease, but also as I use Amazon S3 for backing up the MySQL instance anyway. (Note, obviously my backups don't go to the publicly accessible assets bucket!)

Even with these extra concerns, and the effort of writing a few deployment scripts to deal with the database issues, I'd argue that it only took around 2 hours or so to configure a dokku instance and have the deployment workflow configured.


As mentioned above, I use LetsEncrypt for SSL on the site itself. For assets served from S3 I use Cloudflare though, as not only does it provide HTTPS - it also allows me to save bandwidth and serve from a nicer URL.

I manage all my domains in Cloudflare, so this was as simple as configuring a new CNAME record to point assets.fergus.london to my S3 bucket, before enabling SSL and caching. Easy!


Contract Software Developer and DevSecOps Consultant, based out of London in England. Interests include information security, current affairs, and photography.