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


  • 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
  • (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
$ 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

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 \

Domain configuration

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

$ dokku --no-restart config:set blog \
url= \
server__host= \

3. Download Ghost

Back to your local machine, head to the [Ghost developers’ website]( and download the zip

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

$ unzip ~/Downloads/ -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

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 blog

Once that is done, Dokku will automatically configure your app to be accessible at in addition to

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


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.