Tobias Brunner bio photo

Tobias Brunner

...(a.k.a. tobru) is a Network and Linux Engineer (LPIC-2), working at VSHN AG and blogs about DevOps, Networking, Linux and OpenSource topics on his techblog

Email Twitter XING Github Flattr this


Managing DNS zonefiles just got easier and more robust thanks to Gitlab CI. The presented worklow is based on a Git repository saved in a Gitlab instance and Gitlab CI which runs some scripts in a Docker image. This Docker image contains tools and scripts to build, check and deploy zonefiles to a Knot authoritative DNS server.

How it works

It all starts with a Git repository containing all zonefiles and the Gitlab CI configuration:

  • *.zone: BIND compatible zonefile
  • .gitlab-ci.yml: Configuration for Gitlab CI build

In this example Knot is configured with a hidden master which syncs to two slaves.


Each DNS zone is stored in a BIND compatible zonefile, f.e. Here is an example (really just a normal BIND zonefile):

$TTL    3600

@               IN      SOA (
                                    1 ; SERIALAUTOUPDATE

; NS
                IN      NS
                IN      NS

; Mail
@               IN      MX
@               IN      TXT     "v=spf1 a mx ?all"

; Host Resource Records
@               IN      A
@               IN      AAAA    2001:db8::10
www             IN      A
www             IN      AAAA    2001:db8::10
mail            IN      A
mail            IN      AAAA    2001:db8::11

Please note the special serial and it’s magic string SERIALAUTOUPDATE. This is used later in the CI/CD pipeline to automatically manage the zone serial.


Configuration of the Gitlab CI job happens in a file called .gitlab-ci.yml

image: communityrack/knotci

      - '*.zone'
      - .lasthash
      - .oldserials
    - master

This files is very well documented here: Configuration of your builds with .gitlab-ci.yml.

Docker Image communityrack/knotci

This image is based on the Alpine Linux image and contains some DNS tools and the worker scripts:

FROM gliderlabs/alpine:latest

RUN apk-install \
    bash \
    bind \
    bind-tools \
    openssh-client \
    git \

ADD *.sh /usr/local/bin/
ADD rsyncignore /etc/

WORKDIR /zones

The source can be found on Github: communityrack/knotci.

Build, Check and Deploy Scripts

The heart of the whole chain are the three scripts, and

All have the detection of which zones have changed in common: As there is no information available which files have changed since the last CI run, this is being detected by using some Git commands. If there is a file available called .lasthash, it’s content (a hash) is read and used to detect the changed files with git diff --name-only HEAD "$LASTHASH" -- '*.zone, otherwise it just compares to HEAD~1. To act on all zonefiles without change detection, all scripts accept the command line parameter allzones. At the end of the CI run the file .lasthash is cached (see .gitlab-ci.yml cache configuration above).

Configuration of the scripts happens in environment variables which can be defined in Gitlab under “My Project -> Settings -> Variables”. All configuration parameters are documented in the README.

Here we update the zone serial. This is done by replacing 1 ; SERIALAUTOUPDATE with the current unix timestamp. For zones which didn’t change we replace the magic string with the cached serial from .oldserials. During the script run the serials are all saved to .oldserials which is cached for the next CI run by Gitlab CI.

After the zones have been modified we check all zones before deploying them. The following checks are carried out:

  • named-checkzone -i local: This is done on all zones, no mather if they have changed or not
  • Comparing serial to the currently active on the hidden master for changed zones

If the zone does not yet exist on the hidden master, the check is skipped. The check also supports serial rollover, it doesn’t simply compare if the new serial it’s higher but takes the serial maths into consideration.

When all checks pass the zones are deployed to the hidden master by using rsync. For accessing the remote with SSH the script get’s the private SSH key from the environment variable $SSH_PRIVATE_KEY which is passed to a temporary started SSH agent. Only zonefiles are synced, all other files are excluded from the rsync process (see rsyncignore.

Knot DNS Server configuration

This is done using the Knot Puppet module, no special configuration is needed, except the zonefiles must be saved in a separate directory, f.e. in /var/lib/knot/zones. A sample zone template:

    - id: master_default
      file: /var/lib/knot/zones/
      serial-policy: unixtime
      storage: /var/lib/knot