asxpio-services is to move from Debian with Docker to NixOS with Podman. This is the target system; nothing is switched over yet. One flake holds what was spread over cloud-init, Ansible roles and hand-made units: network and WireGuard, users and SSH restrictions, firewall, the weekly image prune, and the four container stacks (Paper and Fossils Minecraft, SillyTavern, the Forgejo runner), each as a systemd unit under Podman. The runner keeps running as a container and starts jobs through Podman's Docker-compatible socket. Secrets (WireGuard keys, RCON and CurseForge keys) are encrypted with sops for the admin's age key and the server's SSH host key, so the repository holds no plaintext secret and CI needs none of them. Containers that own data wait for /opt/.restored. On a freshly installed disk a game server would otherwise generate a new world exactly where the restore is about to land. Tried on a throwaway server (.#rehearsal): install with nixos-anywhere keeping a given host key, secret decryption, all stacks, a real Paper server stopping with a full save, what CI jobs need from the engine (short image names, the nix-store volume, service containers by name, a kaniko build, the socket inside a job), an update over SSH and a reboot. migration/ has the scripts that move the data through object storage, and a workflow builds the system on every push and switches the host on main. Assisted-by: Claude:fable-5-2
93 lines
2.9 KiB
Nix
93 lines
2.9 KiB
Nix
# Forgejo Actions runner. It runs as a container and starts job containers
|
|
# through Podman's Docker-compatible socket. Its registration lives in
|
|
# /opt/forgejo-runner/data/.runner and is state, not configuration.
|
|
{ pkgs, ... }:
|
|
let
|
|
# Image behind the nix-latest label. Jobs mount a nix-store volume over
|
|
# /nix, seeded from this image on first use; the image's /bin/sh and
|
|
# /etc/nix/nix.conf link into that store, so a volume from another
|
|
# version breaks them. Bumping this also switches to a fresh volume.
|
|
nixVersion = "2.35.3";
|
|
|
|
settings = {
|
|
log = {
|
|
level = "info";
|
|
job_level = "info";
|
|
};
|
|
runner = {
|
|
file = "/data/.runner";
|
|
capacity = 4;
|
|
envs = { };
|
|
env_file = ".env";
|
|
timeout = "3h";
|
|
shutdown_timeout = "3h";
|
|
insecure = false;
|
|
fetch_timeout = "5s";
|
|
fetch_interval = "2s";
|
|
report_interval = "1s";
|
|
labels = [
|
|
"ubuntu-latest:docker://node:20-bookworm"
|
|
"ubuntu-22.04:docker://node:20-bookworm"
|
|
"ubuntu-24.04:docker://node:22-bookworm"
|
|
"debian-latest:docker://debian:bookworm"
|
|
"debian-12:docker://debian:bookworm"
|
|
"debian-11:docker://debian:bullseye"
|
|
"arch-latest:docker://archlinux:latest"
|
|
"nginx-latest:docker://nginx:latest"
|
|
"nginx-alpine:docker://nginx:alpine"
|
|
"node-20:docker://node:20"
|
|
"node-22:docker://node:22"
|
|
"python-3.11:docker://python:3.11"
|
|
"python-3.12:docker://python:3.12"
|
|
"alpine-latest:docker://alpine:latest"
|
|
"nix-latest:docker://nixos/nix:${nixVersion}"
|
|
];
|
|
};
|
|
cache = {
|
|
enabled = true;
|
|
port = 0;
|
|
dir = "";
|
|
};
|
|
container = {
|
|
# "" so each workflow gets a bridge network; services (Postgres) need it
|
|
network = "";
|
|
enable_ipv6 = false;
|
|
privileged = false;
|
|
options = "-v nix-store-${nixVersion}:/nix";
|
|
valid_volumes = [ "**" ];
|
|
docker_host = "automount";
|
|
force_pull = false;
|
|
force_rebuild = false;
|
|
};
|
|
};
|
|
|
|
configFile = (pkgs.formats.yaml { }).generate "forgejo-runner.yaml" settings;
|
|
in
|
|
{
|
|
virtualisation.oci-containers.containers.forgejo-runner = {
|
|
image = "code.forgejo.org/forgejo/runner:11";
|
|
user = "0:0"; # needs the container socket
|
|
cmd = [
|
|
"forgejo-runner"
|
|
"daemon"
|
|
"--config"
|
|
"/config.yaml"
|
|
];
|
|
environment.CONFIG_FILE = "/config.yaml";
|
|
volumes = [
|
|
"/opt/forgejo-runner/data:/data"
|
|
"${configFile}:/config.yaml:ro"
|
|
"/run/podman/podman.sock:/var/run/docker.sock"
|
|
];
|
|
extraOptions = [ "--network=host" ];
|
|
};
|
|
|
|
systemd.services.podman-forgejo-runner = {
|
|
after = [ "podman.socket" ];
|
|
requires = [ "podman.socket" ];
|
|
# Without its registration the runner only crash-loops
|
|
unitConfig.ConditionPathExists = "/opt/forgejo-runner/data/.runner";
|
|
};
|
|
|
|
systemd.tmpfiles.rules = [ "d /opt/forgejo-runner/data 0777 root root -" ];
|
|
}
|