Dokku

Readying a Django project for deploying to Dokku.

This lists the things to add or change to easily deploy a Django application to Dokku.

It started out not trying to cover all of setting up a site on Dokku, only the parts relevant to a Django project – but it has grown. Still, you should read the Dokku getting started docs, then use this as a cheatsheet to quickly enable existing Django projects to deploy on Dokku.

Start with the pages in this list, in order, then come back to this page and continue reading:

Additional info (move to their own files as they’re ready)…

Environment variables

I can’t find docs on what environment variables Dokku sets globally when running apps. But just about any Django app is going to be linked to a database, so if you need to detect whether the app is running under Dokku or Heroku, looking for DATABASE_URL should be good enough.

Static files

We use whitenoise to serve static files from Django. If the site gets incredible amounts of traffic, throw a CDN in front, but honestly, very few sites actually need that. (If you have a philosophical objection to serving static files from Django, you can customize the nginx config through Dokku and probably manage to get nginx to do the static file serving, but I haven’t bothered figuring it out myself.)

Or put your static files on S3.

Django media

If your site requires uploaded files to be persisted, remember that the container running your code is ephemeral and any changes made to files inside it will vanish at the next deploy.

First, you can use S3 for your media files.

Or you can use persistent storage on Dokku, which is a way of mounting directories from the dokku server inside the running container where your site code can store and read files that will continue to exist past the lifetime of that particular container.

Simple hostnames

If the server is set up properly, assuming the server’s domain name is dokkuserver.domain, creating an app named foo will automatically foo.dokkuserver.domain resolve to the server’s address, and requests with host foo.dokkuserver.domain to be routed to that app.

If you want to get fancier, http://dokku.viewdocs.io/dokku/configuration/domains/.

Also, note that any requests simply addressed to dokku.me will get routed to the alphabetically first app on the server, but you can change that: http://dokku.viewdocs.io/dokku/configuration/domains/ or just set up a “00default” app.

Behind a load balancer

If requests are being terminated at a load balancer and then proxied to our dokku server, some nginx config customization will be needed so your app can see the actual origin of the requests: http://dokku.viewdocs.io/dokku/configuration/ssl/#running-behind-a-load-balancer

Run a command

Suppose you want to run something like python manage.py createsuperuser in the app environment?

$ ssh dokku run <appname> python manage.py createsuperuser

will do it.

Running other daemons (like Celery)

Suppose you need to run another instance of your app in another way, for example to run celery beat and celery worker.

Use the Procfile to tell Dokku what processes to run. E.g. if your Procfile is:

web: gunicorn path/to/file.wsgi

try editing it to:

web: gunicorn path/to/file.wsgi
beat: celery beat -A appname -linfo
worker: celery worker -A appname -linfo

With just that, the extra processes won’t run automatically. You can run them by telling Dokku to scale them up, e.g.:

$ ssh dokku ps:scale <appname> beat=1 worker=4

You can check the current scaling settings:

$ ssh dokku ps:scale <appname>
-----> Scaling for <appname>
-----> proctype           qty
-----> --------           ---
-----> web                1
-----> beat               1
-----> worker             4

and see what’s actually running (example from another app that only has one process):

$ ssh dokku ps:report <appname>
=====> <appname>
       Processes:           1
       Deployed:            true
       Running:             true
       Restore:             true
       Restart policy:      on-failure:10
       Status web.1         true       (CID: 03ea8977f37e)

Since we probably don’t want to have to remember to manually scale these things up and check that they’re running, we can add a DOKKU_SCALE file to our repo:

web=1
beat=1
worker=4

which is equivalent to running ps:scale web=1 beat=1 worker=4

Secrets

First, if possible, use Dokku plugin integrations for things like databases, Redis, cache, etc. They automatically set environment variables in each app that your settings can read, so you don’t have to manage different settings for each environment. See each plugin’s doc, of course, for more on that.

The way to handle other secrets for each environment is to set them as config on the app, which will add them as environment variables, again so your settings can read them at runtime.

The downside is that the secrets aren’t being managed/versioned etc for us… but I think we can handle the few we’ll need by manually keeping them in Lastpass or something.

Here are the docs for setting config.

Before setting any config, note that by default, making any change to the config triggers a new deploy. If you’re not ready for that, include --no-restart in the command, as these examples will do.

To set config variables:

$ ssh dokku config:set <appname> --no-restart VAR1=VAL1 VAR2=VAL2 ...

To remove a variable:

$ ssh dokku config>unset <appname> --no-restart VAR1

Check the value of some variable:

$ ssh dokku config:get <appname> VAR1
VAL1

Get all the settings in a single line, handy for use in shell commands or to set on another app:

$ ssh dokku config <appname> --shell
VAR1=VAL1 VAR2=VAL2
$ export $(ssh dokku config <appname> --shell)
$ ssh dokku config:set <appname1> $(ssh dokku config <appname2> --shell)

Get all the settings in a format handy to save in a file for later sourcing in a local shell:

$ ssh dokku config <appname> --export
export VAR1=VAL1
export VAR2=VAL2
$ ssh dokku config <appname> --export >appname.env
$ . appname.env

Note: you can also set config vals globally - just change <appname> to --global in any of these commands.

Logs

Dokku collects stdout from your processes and you can view it with the dokku logs command.

nginx logs are similarly stored on the server and can be accessed using dokku nginx:access-log <appname> or dokku nginx:error-log <appname>.

Dokku event logging can be enabled with dokku events:on and then viewed with dokku events. This shows things like deploy steps.

Deploying from private git repos

Note: this doesn’t apply to your main project repo. That can be private and Dokku doesn’t care, because you’re pushing it directly from your development system to the Dokku server.

But if your requirements include references to private git repos, then you’ll need to arrange for Dokku to get access to those repos when it’s pip installing your requirements.

Dokku docs, such as they are…

I think the upshot is:

  • Create a new ssh key for deploying
  • Add it to the repo on Github (or whatever) as an authorized deploy key (TODO: Link to github docs on that)
  • Drop a copy of the public key file into /home/dokku/.ssh/ on the Dokku system (with appropriate permissions)

Deploying non-master branch

The docs

By default, dokku deploys when the app’s master branch on the dokku server is updated. There are (at least) two ways to deploy a branch other than master.

  1. Push your non-master local branch to the master branch on the server:
$ git push dokku <local branch>:master

but that requires you to always remember that if you have apps that are always supposed to use a different branch than master.

  1. Configure your app so the default branch is different, by using the git:set command:
$ ssh dokku git:set appname deploy-branch SOME_BRANCH_NAME

This seems like a more useful approach. Just “git push dokku some-branch” every time you want to deploy your app.

Developing with multiple remote apps

Suppose you have local development and sometimes you want to push to staging and sometimes to production. Maybe you have other apps too.

The key is to set up a different git remote for each remote app. E.g.:

$ ssh dokku app:create staging
$ ssh dokku git:set staging deploy-branch develop

$ git remote add staging dokku@my-dokku-server.com:staging

$ ssh dokku app:create production
$ ssh dokku git:set production deploy-branch master

$ git remote add production dokku@my-dokku-server.com:production

Then to deploy, push the approprate branch to the appropriate branch:

$ git push staging develop
$ git push production master

Customizing nginx config

If you need to completely override the nginx config, you’ll need to provide an nginx config file template.

Luckily, much customization can be done just by providing snippets of configuration for nginx to include after it’s base config file.

To do this, arrange for the snippets to get copied to /home/dokku/<appname>/nginx.conf.d/ during deployment, probably in a pre- or post-deploy script.

Logging to papertrail

Use the logspout plugin.