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
- 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 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
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 \
Here, we will be setting up the port and URL to access Ghost through:
$ dokku --no-restart config:set blog \
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_EMAILfirstname.lastname@example.org
$ dokku letsencrypt blog
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
Feel free to let me know if you have any further issues and I will update this section.