Automatically deploy rust applications to Dokku using Travis CI

Photo by Greg Rakozy on Unsplash
Photo by Greg Rakozy on Unsplash

Lately I’ve been playing around with the idea of having a small Dokku instance so I can deploy my side projects to. Having such an infrastructure is rather cheap (5$/month in DigitalOcean) and pretty easy to set up.

From past experience, I know I need my side projects to be automatically deployed, because I don’t make changes in them quite often and when I do, I don’t want to start thinking “How the hell am I deploying this? 🤔”

I started looking around for guides on how to automatically deploy to Dokku and found this great article on how to do that using Travis CI. A lot of what I eventually did is based on that article.

I wrote this post so I don’t forget how to set it up for future projects and to share the knowledge with others.

Prerequisites

This guide assumes you have a:

  1. GitHub account
  2. Travis CI account (free for public GitHub repos)
  3. Travis CLI tool installed
  4. Dokku server you can access to using ssh

Preparing SSH keys for deployment

First, on your local machine:

ssh-keygen -t rsa -b 4096 -f dokku-deploy.key

This should create both a private key named dokku-deploy.key and a public key named dokku-deploy.key.pub.

Copy the public key to your Dokku host by:

scp ./dokku-deploy.key.pub root@dokku-host.com:/tmp/.

Add this key as an SSH key to Dokku so you can deploy using that key and then remove it. On your Dokku host:

dokku ssh-keys:add travis /tmp/dokku-deploy.key.pub
rm /tmp/dokku-deploy.key.pub

We will use the private key later on. In the meanwhile, it’s worth mentioning that the private key should never be shared with anyone!

Setting up the Dokku app

The first step is creating the Dokku app. On your Dokku host:

dokku apps:create rust-dokku-deploy

Then, you’ll need to add a rust buildpack. Buildpacks tell Dokku how it should turn your app from a code base to an actual running container. In the case of rust, it needs to install rustup, rustc and cargo and then compile your code. I’ll be using this buildpack:

dokku buildpacks:add rust-dokku-deploy https://github.com/emk/heroku-buildpack-rust

That’s it! You now have a Dokku app ready to have some code pushed into.

Preparing your repository to be deployed

Encrypt the private key

Travis logs are open and so you don’t want sensitive information (such as the private key) to appear there. Luckily, Travis allows encrypting sensitive information to be used later on in your build.

First, let’s make a .travis directory and copy the keys we created earlier into it:

mkdir .travis
cp /path/to/dokku-deploy.key .travis/.

In order to not mistakenly add the private key to your repository, let’s add it to .gitignore:

echo ".travis/*.key" >> .gitignore

Then, you need to log in to Travis CLI, in order to encrypt the key. On your local machine, navigate to your project folder and then:

travis login

This will for your GitHub credentials. After a successful login, you can encrypt the key and add it to your .travis.yml (this action will create .travis.yml if it doesn’t already exist):

travis encrypt-file .travis/dokku-deploy.key --add

This will do the following:

  1. Create a dokku-deploy.key.enc file in your repository root
  2. Add a before_install section to your .travis.yml file.

Then, you need to move the newly created file to .travis directory:

mv dokku-deploy.key.enc .travis/.

Adding deployment files to the repository

The first file we will add is deploy.sh. This is script that will be in charge of deploying the code to Dokku. I’m creating this file in .travis folder for convenience:

As you might notice, I’m using two environment variables in the script (DOKKU_HOST and DOKKU_APP) since I prefer hiding these values. In order to do that, I use Travis’s encryption feature once more:

travis encrypt DOKKU_HOST=dokku-host.com --add
travis encrypt DOKKU_APP=rust-dokku-deploy --add

This will add an env section to your .travis.yml file, with a global section nested under it, with the two encrypted variables in that section.

In order to run deploy.sh on every merge to master you need to do two things:

Add another line to the before_install section to make deploy.sh executable:

chmod +x .travis/deploy.sh

Add an after_success section to run the script after the build succeeded:

after_success:
- |
  if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
    bash .travis/deploy.sh;
  fi;

I wanted my application to be deployed only when there’s a push to master (either directly or by merging a PR), and this is the reason for the if clause.

So eventually, your .travis.yml file should be similar to this:

For the buildpack to know how to compile and run your rust application, you need to add two more files to your repository root:

Procfile tells the buildpack how to run your application. You can read more about it here: http://dokku.viewdocs.io/dokku/deployment/methods/dockerfiles/#procfiles-and-multiple-processes

rust-toolchain tells the buildpack which rust version it should use in order to compile your app.

That’s it! It might seem a bit complicated at first glance but I believe it’s worth the effort. From now on, I don’t need to think about how to deploy my app to Dokku, it happens automatically!

Originally posted on Medium.