Running Ghost on Dokku PaaS

If you’re reading this, then you probably know Ghost’s development team never intended for Ghost to run on Heroku, and by extension, other Heroku-like PaaS implementations.

Version 1.x of Ghost makes several online guides for workarounds obsolete, and this post in particular aims to bring you an up-to-date workaround to getting Ghost installed in your own Dokku setup.

Before we start

This guide is very much based on Berge Greg’s post on getting Ghost 1.x to run on Heroku

Prerequisites:

  • Working Dokku installation
  • A database plugin for dokku (e.g. dokku-mariadb or dokku-mysql)
  • SSH access to the Dokku host (assumed to be of hostname dokku)
  • A domain pointing to your dokku host IP address (assumed to be blog.me)
  • (Optional) Let’s Encrypt plugin for easy HTTPS

Getting Ghost installed on Dokku

1. Prepare Dokku

SSH into your Dokku host, then create a new app with accompanying database services and domain.

In this guide, we will use “blog” for the app and “blog-db” for the database service.

$ dokku apps:create blog
$ dokku domains:add blog blog.me
$ dokku mariadb:create blog-db
$ dokku mariadb:link blog-db blog

At this point, Dokku might inform you that the blog app has not been deployed. Do not worry, because we haven’t.

Now, we will need to disable checks as the checks will fail upon our first deployment due to the lack of database connection credentials

$ dokku checks:disable blog

2. Environment Variables

We will be configuring Ghost via environment variables as opposed to a config file. The following commands are done on the Dokku host

Database Configuration

First up, we need to configure the database credentials. Retrieve the login details through the DATABASE_URL environment variable.

$ dokku config:get blog DATABASE_URL
mysql://mariadb:abcdefg@dokku-mariadb-blog-db:3306/blog-db

Based on the above, you will need to set the following:

$ dokku config:set --no-restart blog \
database__connection__user=mariadb \
database__connection__password=abcdefg \
database__connection__host=dokku-mariadb-blog-database \
database__connection__database=blog-database

Domain configuration

Here, we will be setting up the port and URL to access Ghost through:

$ dokku --no-restart config:set blog \
url=https://blog.me \
server__host=0.0.0.0 \
server__port=5000

3. Download Ghost

Back to your local machine, head to the [Ghost developers’ website](https://ghost.org/developers/) and download the zip

Then, unzip the files into a directory to something you are comfortable with (e.g. “my-blog”)

$ unzip ~/Downloads/Ghost-1.7.1.zip -d ~/projects/my-blog
$ cd ~/projects/my-blog

4. Initialise a git repo

As with Heroku, Dokku works with the basis that projects are git repos, and that is how you will be deploying your Ghost app

$ git init
$ git add -A
$ git commit -m "Initialize repository"

Now, we will have to add our Dokku host as a remote to push to, and then push it.

$ git remote add dokku dokku@dokku:blog
$ git push dokku

5. Initialising the Database

At this point, the app should be deployed. What’s left is to initialise the database on the Dokku host:

$ dokku run blog knex-migrator init

Your output should be similar to:

[2017–12–06 08:07:33] INFO Creating table: posts
[2017–12–06 08:07:33] INFO Creating table: users
[2017–12–06 08:07:35] INFO Creating table: roles
[2017–12–06 08:07:35] INFO Creating table: roles_users
[2017–12–06 08:07:36] INFO Creating table: permissions
[2017–12–06 08:07:38] INFO Creating table: permissions_users
[2017–12–06 08:07:39] INFO Creating table: permissions_roles
[2017–12–06 08:07:39] INFO Creating table: permissions_apps
[2017–12–06 08:07:41] INFO Creating table: settings
[2017–12–06 08:07:42] INFO Creating table: tags
[2017–12–06 08:07:43] INFO Creating table: posts_tags
[2017–12–06 08:07:45] INFO Creating table: apps
[2017–12–06 08:07:46] INFO Creating table: app_settings
[2017–12–06 08:07:48] INFO Creating table: app_fields
[2017–12–06 08:07:49] INFO Creating table: clients
[2017–12–06 08:07:50] INFO Creating table: client_trusted_domains
[2017–12–06 08:07:51] INFO Creating table: accesstokens
[2017–12–06 08:07:54] INFO Creating table: refreshtokens
[2017–12–06 08:07:57] INFO Creating table: subscribers
[2017–12–06 08:07:57] INFO Creating table: invites
[2017–12–06 08:07:58] INFO Creating table: brute
[2017–12–06 08:07:59] INFO Creating table: webhooks
[2017–12–06 08:07:59] INFO Model: Post
[2017–12–06 08:08:00] INFO Model: Tag
[2017–12–06 08:08:00] INFO Model: Client
[2017–12–06 08:08:00] INFO Model: Role
[2017–12–06 08:08:00] INFO Model: Permission
[2017–12–06 08:08:03] INFO Model: User
[2017–12–06 08:08:04] INFO Relation: Role to Permission
[2017–12–06 08:08:09] INFO Relation: Post to Tag
[2017–12–06 08:08:09] INFO Relation: User to Role
[2017–12–06 08:08:10] INFO Finished database init!

6. Final Configuration

All that’s left is to re-enable the checks upon deployment

$ dokku checks:enable blog

And then your app should be all ready to set up at https://blog.me/ghost

7. (Optional) Let’s Encrypt!

Thanks to the Dokku Let’s Encrypt plugin, enabling encryption on the domain is a simple matter of setting up an environment variable (DOKKU_LETSENCRYPT_EMAIL), and then running a command.

(Replace the email with your own)

$ dokku config:set --no-restart blog DOKKU_LETSENCRYPT_EMAIL=letsencrypt@blog.me
$ dokku letsencrypt blog

Once that is done, Dokku will automatically configure your app to be accessible at https://blog.me in addition to http://blog.me

8. (Optional) Enable persistent storage

Dokku, like Heroku, does not have persistent storage by default. Any re-deploys will risk having any files that were written to the directories and not checked into git being removed.

Dokku provides a persistent storage plugin using dokku storage. As per the documentation, mount points are recommended to reside within /var/lib/dokku/data/storage/. We will first create the folder within this directory, then mount the content/images path of the ghost container onto that folder.

$ sudo mkdir /var/lib/dokku/data/storage/blog
$ dokku storage:mount blog /var/lib/dokku/data/storage/blog:/app/content/images
$ dokku ps:restart blog

Troubleshooting

Before you embark on any troubleshooting steps, restart your dokku app to ensure that all environment variables you have configured are in effect.

dokku ps:restart blog

Dokku may have configured a different port to serve your app at. If so, check on which port this is by checking the DOKKU_PROXY_PORT_MAP environment variable:

$ dokku config:get blog
DOKKU_PROXY_PORT_MAP http:80:5000 https:443:5000

In the above, the port number is 5000.

Feel free to let me know if you have any further issues and I will update this section.