mkdocs is a static site generator that I use for several documentation sites both public and private. Up until recently I've relied on Github actions to automate deployments to VPS hosts (eschewing Github pages).
Todays post will discuss the steps required to deploy a fully self-hosted, automatically updating documentation stack using Gitea, mkdocs, nginx, traefik and Drone CI.
Pre-requisites
For the purposes of this guide we should assume you are familiar with the basics of DNS. However we'll need to make sure that you have created a record which points your chosen domain to the IP of the system running traefik and mkdocs.
There are lots of ways to achieve this end result but the way that I do it is by hosting the DNS for domain on Cloudflare. I give Traefik my Cloudflare API key which it uses to verify my ownership of the domain in question via dnsChallenge
and once successful automatically generates the required TLS certificates.
For a full overview of everything related to Traefik see my other site where I wrote up a getting started "Traefik 101" type post at perfectmediaserver.com/remote-access/traefik101.html.
As CI/CD is a fairly advanced topic I assume familiarity with docker, docker-compose and managing that stack. I've written (as have others) about it many times before - for example.
Overview
First, let's take a look at a picture attempting to explain what we're trying to put together.
From now on, whatever you push to git will get built and deployed automatically. There are lots of nuances to this approach and if you're doing this in production you might wish to read into promotions to control what appears in which environment and when.
Next, let's take a look at the docker-compose.yml
file needed in its entirety (it's quite long but we're defining traefik, gitea, drone, drone-docker-runner, and the nginx container running the wiki itself all in one file):
If you're struggling to find some of these values then review the Gitea configuration section coming up shortly.
For some reason, Drone CI recommend against running Gitea and Drone on the same instance especially when using docker-compose due to "network complications". We're using Traefik and all the routing is handled internally or via DNS, so there are no port conflicts.
Whatever Drone's reasoning for this disclaimer is, laziness or otherwise, you should be fine to configure your system using the one-file docker-compose approach outlined above. In my case I actually wanted everything co-located on one node so that drone-docker-runner
had access to the filesystem for spitting out my statically generated mkdocs site to disk.
Gitea configuration
The drone CI documentation does a good job of providing an overview of the configuration you need to undertake in Gitea.
Create an OAuth Application -> Create a Shared Secret with openssl
-> Start Drone CI (as a docker container in our case) -> Start some runners
Next, obviously, you will need an mkdocs repository. If you don't have one ready, you can use the repo behind perfectmediaserver.com as an example.
Configure a build
We can finally move on to actually configuring a build now that we have everything up and running.
This file lives in the root of your Git repo and tells Drone what to do. We're using the squidfunk/mkdocs-material:7.1.9
docker image (it is automatically pulled from docker hub by Drone) and performing the build entirely within the context of that container. No dependencies or other mess is put onto the host system which is clean AF and really nice.
The only real gotcha here is to make sure that the volume path matches that of the nginx container you're using to serve the wiki itself - /opt/appdata/mkdocswiki
in this case.
Connecting Gitea and Drone
When you first launch Drone in your browser you will be greeted with the following screen.
Once you click continue you will be asked to authenticate with Gitea and then will be automatically redirected back to the Drone interface below.
The dashboard of Drone is quite pretty to look at but is also pretty functional. Click SYNC
in the top right to ensure that the list of repos in Drone is synchronised with Gitea. Once sync'd, use the filter to find the repo you're interested in and activate it by clicking on it.
You'll need to ensure you enable Trusted
in the settings page for the build so that the container can access host volumes.
After this, we can trigger build either by performing a commit + push to your repository with git or by clicking + NEW BUILD
.
Like most CI history graphs, there are plenty of failures as you tinker and improve stuff. For this reason I have created a dedicated Drone CI testing repo - in my case to test Ansible plays.
Running an Ansible Playbook
This is a bit more advanced as it makes use of secrets in Drone. These are configured in the web interface but the file below should give you a good idea of what's possible outside of just a simple static site deployment.
The Ansible plugin for Drone is documented here.
kind: pipeline
name: default
steps:
- name: check ansible syntax
image: plugins/ansible:3
settings:
playbook: run.yaml
galaxy: requirements.yaml
inventory: hosts.ini
vault_password:
from_secret: ansible_vault_password
syntax_check: true
when:
event:
- push
- name: apply ansible playbook
image: plugins/ansible:3
# environment:
# additional_var:
# from_secret: additional_var
# another_var: foo
settings:
playbook: run.yaml
galaxy: requirements.yaml
inventory: hosts.ini
private_key:
from_secret: ansible_private_key
vault_password:
from_secret: ansible_vault_password
when:
event:
- push
- tag