2025 — present · Go · Ansible · cross-compiled binaries

glr

An open-source Ansible collection for managing GitLab Runners — with the modules written in Go, not Python.

Visit the site →

glr is an Ansible collection for the full GitLab Runner lifecycle: creating runner tokens through the GitLab API, registering and verifying runners, merging settings into config.toml idempotently, and installing or removing the system service. It's published on GitHub under my m0rthis handle, with a copy on GitLab. It used to live under my old arcticwraith handle.

Why Go instead of Python

Two reasons. The honest one: I'd grown to dislike writing Python. The better one: GitLab Runner is itself written in Go, so my modules can import the config structs straight from the gitlab-runner source code. When a module merges settings into config.toml, it's marshaling the runner's own types — not maintaining a hand-written shadow copy of the schema that drifts every time upstream adds a field.

Every module is idempotent in the Ansible sense — repeated runs only report changed when something actually changed — which is easy to guarantee when the module owns the config parsing itself.

The bigger idea it was chasing

I wanted automation where the platform didn't dictate the language — where Windows didn't automatically mean PowerShell and Linux didn't automatically mean Python modules. One Go implementation, compiled for both, behaving identically. glr mostly gets there: each release ships static binaries for Linux (amd64, arm64, arm, 386, ppc64le, s390x) and Windows (amd64, arm64, 386), with a thin wrapper dispatching to the right one.

Mostly. Ansible's assumptions pushed back — separate binaries per platform meant the Windows modules had to be named win_register and so on, splitting the very interface I was trying to unify.

The other thing I never made peace with is Ansible's execution model itself: modules get shipped to the destination host and interpreted there, so every target needs the right interpreter in the right state before automation can even begin. I much prefer a self-contained controller commanding the fleet — the model I already reach for everywhere else. It's why I like Kubernetes and ArgoCD, where a controller reconciles the fleet toward declared state, and why I'd pick Terraform over Ansible for provisioning. Both frictions feed an idea I keep coming back to: an Ansible-like platform written in Go from the ground up — cross-platform by default, driven by a controller instead of interpreted on the target.

Two companion repos came out of the work, both public: gansible, a lightweight Go helper for writing Ansible-compatible modules, and glrcommon, the shared code behind the runner modules.