For running your own PaaS, Dokku is great. I could write a whole post on what you can do with Dokku, but in brief it works as a sort-of self-hosted Heroku – you create an app, push your code to it with Git and it’ll deploy it. I’m also a big fan of GitLab CE. I use it for my personal projects, mostly for things I don’t want to publish on GitHub for various reasons.

Wouldn’t it be nice if I could make the two of them work together, so that when I push some code to a repo in GitLab, it in turn pushes it to Dokku for deployment?

Well, you can.

CONTINUOUS INTEGRATION, BABY

Overview

To make this work, you will need:-

  • A Dokku app
  • A GitLab install, with GitLab Runner configured
  • A GitLab repo
  • A brand new SSH key
  • A way to tell GitLab to do things when a repo is updated

Prerequisites

Creating a new Dokku app and a new GitLab repo is out of the scope of this post, so let’s assume:-

  • your Dokku app is called awesome-sauce, it can be found at https://awesome-sauce.dokku.example.com/, and the Git remote for it is dokku@dokku.example.com:awesome-sauce.
  • your GitLab repo is also called awesome-sauce, and the Git remote for it is git@gitlab.example.com:awesome-sauce.git.

The first thing we need to do is to create a new SSH key for GitLab to use to push to Dokku:-

workstation$ ssh-keygen -P '' -C 'GitLab/Dokku Integration' -f gitlab
Generating public/private rsa key pair.
Your identification has been saved in gitlab.
Your public key has been saved in gitlab.pub.

Copy the public key (in our example, gitlab.pub) to your Dokku host, and tell Dokku to accept it:-

dokku-host$ sudo dokku ssh-keys:add "GitLab/Dokku Integration" /tmp/gitlab.pub
SHA256:<...SHA256 hash...>

Next, go to the CI/CD settings in GitLab for your repo (for example, https://gitlab.example.com/awesome-sauce/settings/ci_cd), and expand the Environment variables section. Create a new entry named SSH_PRIVATE_KEY, and for the value paste in the contents of the gitlab file:-

Now that this is done, we can start tying things together.

Integrating, continuously

The .gitlab-ci.yml file is used by GitLab to determine what tasks it should run when the repo is updated. It lets you do some pretty complex things, but for this example we’re going to keep it simple. In the local copy of the repo, create a .gitlab-ci.yml file with the following:-

before_script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H 'dokku.example.com' >> ~/.ssh/known_hosts
stages:
- deploy

deploy_to_dokku:
stage: deploy
script:
- git checkout master
- git pull
- git push dokku@dokku.example.com:awesome-sauce master

Let’s break this down:-

  • The before_script section does some housework before we start. It creates an .ssh directory, adds our new SSH key (that we added through the GitLab web UI), sets some permissions and then pre-populates the known_hosts file with the public key of the Dokku server so that we don’t get prompted for it later.
  • The stages section groups together different deployment sections. In this example we’ve only got the one stage, named deploy.
  • Finally, the deploy_to_dokku section is what pushes our code to Dokku. It ensures we’re on the master branch (git checkout master), makes sure we’re up to date (git pull) and then pushes it to Dokku (git push). We mark it as being part of the deploy stage.

The git checkout and git pull steps seem at first glance to be redundant, but I found when setting this up that GitLab seems to check out the branch in a detached state, which confuses the Dokku server (which is expecting the master branch). Explicitly switching to the master branch and making sure it’s up to date overcomes this.

Of course, this assumes that the master branch is the one that you’re working on – you can adjust this to suit if needed.

Push it real good

All that’s left to do is to push our code to our GitLab repo:-

$ git remote add origin git@gitlab.example.com:awesome-sauce.git
$ git push origin master
Enumerating objects: 25, done.
Counting objects: 100% (25/25), done.
Delta compression using up to 4 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (17/17), 1.68 KiB | 1.68 MiB/s, done.
Total 17 (delta 10), reused 0 (delta 0)
To git@gitlab.example.com:awesome-sauce.git
de8e665..cb34843 master -> master

Go back to the GitLab web UI, and go to the CI/CD -> Jobs page (https://gitlab.example.com/awesome-sauce/-/jobs), and you should see a job running, pushing your code to Dokku!

What next?

This is a really simple example which pretty much just re-pushes the contents of the repo to Dokku. You’ll probably want to run tests before pushing to Dokku, and it’s definitely worth taking a look at the GitLab CI/CD Pipeline Configuration Reference to see what else you can do with the .gitlab-ci.yml file.

Leave a Reply

Your email address will not be published. Required fields are marked *