Ansible+Git: My search for a build server setup that can last years

Lately I’ve been searching for a good way to deploy small projects of mine.

Thanks to this guide I was able to create a deployment setup for my blog that uses a bare git repository. Prior to this project, I had no idea how easy it was to create a bare git folder on a remote server that one can push and pull from.

When I now run git push live, I push code updates to my build server running as a droplet on Digital Ocean. This push triggers a post-receive script on my server which builds my blog using Hugo, before uploading it to a different server. This happens synchronously which is advantageous since I get to follow the remote build logs in the terminal.

To configure the build server I played around with Ansible. Ansible distinguishes itself by only needing ssh access to be able to configure remote servers. Moreover, it works idempotently meaning that an Ansible playbook script can be safely run multiple times. Finally, I would like to try and document the server setup with an Ansible playbook script instead of some README file I might forget to update.

Here’s the Ansible playbook I have thus far:

- hosts: services
  become: yes
  become_user: root
  tasks:
    - name: Install dependencies
      apt:
        name: "{{ packages }}"
        update_cache: true
      vars:
        packages:
          - git
          - hugo
    - name: Prepare git project folders
      file:
        path: "/home/nilsnh/{{ item }}"
        state: directory
        owner: nilsnh
        group: nilsnh
      loop:
        - nilsnh-no.git
    - name: Initialise bare git projects
      become_user: nilsnh
      shell: |
        if [ -n "$(find "{{item}}" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
            cd {{item}}
            git init --bare
        else
            exit 11
        fi        
      register: init_result
      changed_when: "init_result.rc != 11"
      failed_when: "init_result.rc > 0 and init_result.rc != 11"
      loop:
        - nilsnh-no.git
    - name: nilsnh.no: Add post-commit hook
      copy:
        src: ./nilsnh.no/post-receive
        dest: /home/nilsnh/nilsnh-no.git/hooks/post-receive
        owner: nilsnh
        group: nilsnh
        mode: 0744

Notice the loop declarations. As I was writing this script I was thinking about how to make this scalable. With the loop construct I can quickly add more git project folders in the future, which I can then push to and deploy from.

This script is still a bit rough, and can be tidied by splitting it. I had configured unattended upgrades and email functionality before I started to use Ansible, so not everything about the server is described in this playbook. Ideally, I should be able to destroy this server and quickly provision a new one if need be.

With this build server I get two benefits:

  1. A server for hosting my git repositories, as an alternative to Bitbucket or Github.
  2. A server for building/deploying/running code.

Things that needs to be improved about this setup:

  1. Currently, I must add new commits in order to re-trigger failed deploys. I should not have to do that.
  2. Deployment scripts should fail fast. My single post-receive script does not do that presently.

Why not use service X or product Y? #

There are lots of good product offerings out there for continuous integration and continuous deployment. But wanted to find a deployment setup that could last some years without requiring large changes.

I can hear the Twitter rumble:

“Just use Netlify or Now.”

Services like Netlify or Now are impressive services, and their free offerings might very well stay free forever. But they might not. As is the nature of business companies behind awesome services might be bought, sold or go bankrupt. They might also develop their product in some direction that doesn’t fit with what I need from the service.

So, I really wanted to setup a server that could build and deploy mini-projects.

To that the Twitters might thunder:

“Just use Dokku, Gitlab or Kubernetes.”

I started looking at Gitlab for hosting my projects and deploying them. But I was daunted by the pace of their development. Gitlab is a fast moving, ambitious project that does a new release on the 22nd of every month. I don’t need a ticketing system, a project browser nor user login and role based access control. I just want to do a git push and see a deploy happen.

I also ruled out Kubernetes because it’s too complex for my taste (see my argument here). Dokku on the other hand is much simpler.

I could certainly use Dokku. But for now I want to see if this Git+Ansible approach can be made user friendly enough, for one person to efficiently build services.