Skip to content

Latest commit

 

History

15 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitLab Runner appliance for Hyper-V

A NixOS image (built on foundrix) that runs a GitLab Runner as a Hyper-V Gen2 VM. Credentials are passed in via a one-time setup dialog over SSH or the VM console — the var-disk-manager pattern: a daemon owns the state, every connected session mirrors it, the first one to touch the form drives it.

How it works

  • Image: ephemeral tmpfs root, writable btrfs nix store, UEFI systemd-boot (Secure Boot off). Built by foundrix's repart machinery and converted to a dynamic VHDX.
  • Disks: OS VHDX at SCSI LUN 0 (swappable for updates), data VHDX at LUN 1 holding /var — runner registration, CI caches, docker images. The data disk is found at runtime by LUN (Hyper-V has no stable by-id names) and partitioned on first boot.
  • Runners: registered at runtime with gitlab-runner register using GitLab ≥17 runner authentication tokens (glrt-…); the dialog supports several runners, each either the shell executor (native Nix on the host — the writable store is the build cache) or the docker executor (isolated containers, nothing mounted from the host; an image that needs Nix brings its own).
  • State: everything imperative lives in /var/lib/gitlab-runner/ (config.toml, .provisioned sentinel, chosen hostname). Reboots and OS-disk swaps never re-prompt.

Build

# Raw image + all foundrix outputs
nix flake show path:.

# The Hyper-V deliverable (VHDX + VM setup script)
nix build 'path:.#hyperv-vhdx'

Before building a production image, put your admin SSH key into configuration.nix (adminKeys) — without it the appliance is unreachable over SSH.

Deploy

On the Hyper-V host (elevated PowerShell):

.\setup-hyperv-vm.ps1 -VMName gitlab-runner

This creates a Gen2 VM (Secure Boot off), attaches the OS disk and a 256 GB dynamic data disk, and connects it to an external switch.

VM files (config, OS disk, data disk) default to C:\ProgramData\GitLabRunner\HyperV\VMs\<VMName>\. To put them on another volume, or to place a single disk somewhere specific:

# everything on the D: VM storage volume
.\setup-hyperv-vm.ps1 -VMName gitlab-runner -VMStorageRoot D:\HyperV\VMs

# or per-disk
.\setup-hyperv-vm.ps1 -VMName gitlab-runner `
  -OSDiskPath   D:\vm\runner-os.vhdx `
  -DataDiskPath E:\ci-cache\runner-data.vhdx

Other knobs: -DataDiskSizeBytes (default 256 GB), -ProcessorCount (4), -MemoryStartupBytes (8 GB), -SwitchName.

To update the OS later while keeping registration and caches — pass the same path options you used originally:

.\setup-hyperv-vm.ps1 -VMName gitlab-runner -Update -FinalImagePath .\new\gitlab-runner-os.vhdx

First boot: the setup dialog

Start the VM and open its console — or ssh root@<ip>. Both land in the same fullscreen dialog:

GitLab URL:    https://gitlab.example.com
── runner 1 ──
  token:       glrt-…            (create under Group/Project → CI/CD → Runners)
  executor:    ◂ shell (native Nix on host) ▸
[ + add another runner ]
concurrency:   4
hostname:      ci-runner-01
[ Register and start runner ]

On success the runner service starts immediately and the session becomes a normal shell; every later login is a plain shell too. Registration failures (bad token, unreachable URL) are shown in place — fix and resubmit.

To redo setup later: runner-reprovision (unregisters the runners, clears the state, brings the dialog back).

Updating a deployed runner

The store is writable and nixos-rebuild is present, so an appliance is updated in place — no image swap, no reimage:

nixos-rebuild switch --flake .#gitlab-runner --target-host root@<appliance>

Registration, caches and the data disk are untouched.

Local runner in QEMU

One command starts the appliance as a throwaway VM whose console is the terminal it was started from. No admin SSH key is needed for this:

nix run 'path:.#qemu-run'

The kernel and systemd output scroll by, then the setup dialog appears in the terminal. Register a runner as on Hyper-V; the dialog quits into a root shell (no password, TERM=xterm-256color) and the runner takes jobs until the VM is shut down.

  • Clean exit: poweroff in the guest shell. On the way down the VM unregisters the session's runner from GitLab, while the network is still up, and the call is printed on the console.
  • Key bindings: Ctrl-a c toggles the QEMU monitor, Ctrl-a x kills the VM on the spot. A hard kill does not unregister the runner; remove it in GitLab by hand. Ctrl-C goes to the guest like any other key.
  • Nothing persists. The OS disk is a copy-on-write overlay over the image in the Nix store and the data disk is created fresh; both are removed when the launcher exits (also on error and on SIGINT/SIGTERM). The next launch shows the setup dialog again.
  • State directory: $(dirname "$PWD")/.cache/gitlab-runner-qemu, one level above the checkout (not /tmp, which may be RAM; not inside the checkout, which a path: flake copies into the store). Override with RUNNER_VM_DIR. The per-run disks live in a subdirectory of it.
  • Journal: the guest journal is streamed to journal.log in the state directory, overwritten on every launch and kept after exit. It is the only record of a session once the data disk is gone.
  • Size: RUNNER_VM_CPUS (default 4), RUNNER_VM_MEMORY (QEMU size string, default 8G), RUNNER_VM_OS_SIZE (qemu-img size string, default 200G, sparse; the store partition grows into it on first boot, so Nix builds have room) and RUNNER_VM_DATA_SIZE (default 64G, sparse). The data disk must be at least 9G: /var has an 8G minimum and a smaller disk fails the first boot into emergency mode.
  • Networking: QEMU user-mode NAT, outbound only. The host is 10.0.2.2 from inside the guest. KVM is used when /dev/kvm is writable, otherwise software emulation; the launcher prints which.

Design notes and acceptance criteria are in docs/qemu-run.md.

Layout

Path Purpose
configuration.nix device-agnostic system config (admin keys go here)
devices/hyperv/ Hyper-V device: guest support, kernel, networking
devices/qemu/ local QEMU device: serial console, autologin, journal on hvc0, unregister on shutdown
modules/appliance-disks.nix LUN 0 / LUN 1 storage model, image modules, runtime repart
modules/appliance-filesystems.nix mounts: tmpfs root, ESP, /nix, /var, /home
modules/gitlab-runner.nix runner service, hostname re-apply, runner-reprovision
modules/runner-provision.nix setup daemon + console/SSH dialog wiring
packages/runner-provisioner/ the Rust daemon + ratatui TUI
packages/qemu-run/ the local VM launcher
hyperv/setup-hyperv-vm.ps1 VM creation/update on the Hyper-V host
docs/qemu-run.md design of the local QEMU runner

About

OS image with GL runner preinstalled and runtime-configurable

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages