Setting up CI

By: on May 8, 2018 for go

I was recently asked to set up a CI server for one of our go projects and decided to try out 0.8.

From my point of view, the two most attractive features of are that:

  • the build is defined through a single .drone.yml file in the root directory of the project
  • all build steps are executed in docker containers

Drone is primarily offered as software-as-a-service, but you can host and run your own instances via docker-compose.

Drone integrates with a number of source code repository providers and relies on their authentication / user management. Builds for all branches are triggered via webhooks.

Drone’s web UI is slick and minimal. A command line interface is also provided.

Documentation is clear and to the point. A number of useful plugins exist, for example for posting build results on a Slack channel.

Some details

To give you an impression of a simple drone setup, we’ll have a look at some key files.

Our project uses make and vgo. Our .drone.yml looks roughly like this:

  base: /go
  path: slick_project

    image: base:1
      - set -ex
      - mkdir -p /go/src/Go_external
      - export GOPATH=/go/src/Go_external:/go/slick_project/Go
      - go get -u
      - go get -u
      - export PATH=$PATH:/go/src/Go_external/bin:/opt/wkhtmltopdf/wkhtmltox/bin/
      - (cd Go/src/ed4/ && go get ./...)
      - make platform
    image: base:1
      - set -ex
      - export GOPATH=/go/src/Go_external:/go/slick_project/Go
      - export PATH=$PATH:/go/src/Go_external/bin:/opt/wkhtmltopdf/wkhtmltox/bin/
      - make test
    image: plugins/slack
    channel: slick_channel
      status: [ success, failure ]
    template: >
      {{#success build.status}}
        Success! Build {{build.number}} {{}} ({{build.commit}} on {{build.branch}} by {{}})
        Fail. Build {{build.number}} {{}} ({{build.commit}} on {{build.branch}} by {{}})

The workspace section defines a docker volume that will be shared by all docker containers in the build pipeline.

The build pipeline comprises three steps, which are executed sequentially and in individual containers. For the build and test steps, we use a custom local docker image that adds external dependencies (such as wkhtmltopdf and various libraries) on top of the official golang 1.10.x image. The command section for each step is translated into a bash script, which is then executed in the respective container. In our case, the scripts fetch go dependencies, set up appropriate paths and finally call make. Make’s exit code determines success/failure of the step. The third step demonstrates how you could post the build outcome on a slack channel. As you can see, drone provides various pieces of build information via variables, as well as conditional execution of build steps.

We host our internal drone instance on a server with a public DNS entry so that it is visible to our source code repository (bitbucket cloud). The server needs docker and docker-compose installed. The docker-compose.yml for our setup looks like this:

version: '2'

    image: drone/drone:0.8
      - 80:80
      - 443:443
      - 9000
      - /var/lib/drone:/var/lib/drone/
    restart: always
      - DRONE_OPEN=false
      - DRONE_ADMIN=superstar,unicorn,patrick
      - DRONE_BITBUCKET=true
      - DRONE_BITBUCKET_SECRET=some_secret
      - DRONE_HOST=
      - DRONE_SECRET=drone_secret

    image: drone/agent:0.8
    command: agent
    restart: always
      - drone-server
      - /var/run/docker.sock:/var/run/docker.sock
      - DRONE_SERVER=drone-server:9000
      - DRONE_SECRET=drone_secret

In this configuration, there is no public access to the web UI, and drone’s built-in support for Let’s Encrypt is used to automatically obtain certificates. A single agent for executing build pipelines is run on the same server.

We run drone’s docker-compose.yml via systemd; the drone.service file looks as follows:

Description=drone service with docker-compose

ExecStartPre=/usr/local/bin/docker-compose down -v
ExecStartPre=/usr/local/bin/docker-compose rm -fv
ExecStart=/usr/local/bin/docker-compose up
ExecStop=/usr/local/bin/docker-compose down -v


The drone user needs to be part of the docker group and is set up in a standard fashion.


It always takes a little while to set up a new piece of infrastructure. In the case of, team consensus is that it has been worth the effort.


Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>