No better way to break my way into the world of blogging with GitLab Pages than with a post about how I set it up.

As a slight introduction, I manage a small-to-medium sized GitLab instance for MasonSRCT. I wanted to provide this service to allow students to easily set up their own homepages under and to even setup a custom domain and TLS certificate for their homepage. This site that you’re reading is an implementation of that! In this post, I’ll be sharing the complete administrative setup process as well as a sample user implementation.

Administrative Guide

To get started, our GitLab instance is an omnibus self-hosted GitLab Community Edition running on a standard Ubuntu Digital Ocean droplet (c2.r4.d50). Also, we run our own bind DNS server and have a wildcard domain

The Basics

Getting a basic GitLab Pages implementation is fairly straight-foward following the documentation.


pages_external_url ""
gitlab_pages['enable'] = true


@ IN A
@ IN AAAA 2604:a880:800:a1::cd9:9001
* IN A
* IN AAAA 2604:a880:800:a1::cd9:9001

This will provide you a standard non-TLS setup. Let’s kick this up a notch.


We can take advantage of Let’s Encrypt’s new-ish wildcard certificate program. As of this writing, only the dns-01 verification worked for these types of certificates. Additionally, we must utilize the beta Acme v2 protocol servers.

This is easy enough to do with manual verification:

certbot certonly --manual -d * -d --agree-tos --preferred-challenges dns-01 --server

Then update your bind zone to contain the challenge that given from the above command: IN TXT "xxxx_xxxxxxxxxxxxxxx_xxxxxxxxx"

But I’m lazy and don’t want to have to worry about this 4 times a year, so let’s try to see if we can automate this!

Certbot offers a variety of DNS Plugins to help automate the dns-01 veriification method. In my case, I utilized the rfc2136 plugin since I was using a self-hosted Bind server.

Bind Configuration

RFC2136 is, to quote the RFC, “Dynamic Updates in the Domain Name System”. Your standard bind server implements the protocol, so now we just need to configure it.

To start, from your bind server, you will need to create the symmetric shared key that will be used to authenticate our RFC2136 client to the bind server.

Run the following command:

dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST -r /dev/urandom

This will drop two files in your current working directory named something like and Despite the naming, we generated a symmetric key, so both files contain the same key, just in different formats.

Inspect the contents of the .key file, and you’ll find something like: IN KEY 512 3 165 pEx3VSFcEUdLCu8JQKtr7crpuvA3lFvhnEupdxAcg0Y5i24mtXKAkZNJ xTZeKf6W1E3PYZ4vX+hi5pBNCe4V+A==

We’re interested in the last two sections:

pEx3VSFcEUdLCu8JQKtr7crpuvA3lFvhnEupdxAcg0Y5i24mtXKAkZNJ xTZeKf6W1E3PYZ4vX+hi5pBNCe4V+A==

Next, we’ll need to modify our Bind config to add our key and grant permissions to updates from our key.


key "" {
    algorithm hmac-sha512;
    secret "pEx3VSFcEUdLCu8JQKtr7crpuvA3lFvhnEupdxAcg0Y5i24mtXKAkZNJ xTZeKf6W1E3PYZ4vX+hi5pBNCe4V+A==";

zone "" IN {
    type master;
    file "/var/cache/bind/";
    // Push updates to our slave nameserver
    notify yes;
    also-notify {; };
    // Allow transfer to our slave nameserver
    allow-transfer {; };
    allow-update { key; };

The allow-update config I used in this example is fairly permissive. Ideally, you would further lock it down to just the operations certbot needs, such as:

    allow-update { key name txt; };

Certbot Configuration

The official documentation suggests using the provided Docker containers for running the verification. Unfortuantely, I ran into a few quirks throughout the process.

First note, --dry-run is not compatible with --server, and the standard server will reject wildcart certificate requests.

… To be continued…