Pin NixOS service versions via nixpkgs-services overlay (#321)

## Summary

- Add `nixpkgs-services` flake input pinned to a specific nixpkgs commit, with an overlay that pulls `forgejo-runner`, `snowflake`, and `k3s` from it instead of the rolling `nixpkgs`
- Dagger `flake-update` pipeline now excludes `nixpkgs-services` via `--exclude`
- Fix stale nix-container-builder version in service-versions.yaml (was 12.6.4, actually running 12.7.2)
- Add k3s and minikube to service-versions.yaml tracking
- Document the pinning approach in review-services how-to and ringtail reference

## Motivation

During service review, discovered that flake updates had silently upgraded forgejo-runner from 12.6.4 → 12.7.2 without updating service-versions.yaml. This "sneak-in upgrade" bypasses the service review process. The overlay ensures these three services only change versions deliberately.

## Test plan

- [ ] Verify `nix flake update` from `nixos/ringtail/` does not change `nixpkgs-services` lock entry
- [ ] Verify `mise run provision-ringtail` builds successfully with the overlay
- [ ] Confirm running service versions unchanged after deploy

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #321
This commit is contained in:
Erich Blume 2026-04-01 21:37:57 -07:00
commit a18a424866
9 changed files with 91 additions and 10 deletions

View file

@ -258,7 +258,11 @@ class BlumeopsCi:
async def flake_update( async def flake_update(
self, src: dagger.Directory, flake_path: str = "nixos/ringtail" self, src: dagger.Directory, flake_path: str = "nixos/ringtail"
) -> dagger.File: ) -> dagger.File:
"""Update all flake inputs to latest and return updated flake.lock.""" """Update rolling flake inputs to latest and return updated flake.lock.
Skips nixpkgs-services, which is pinned to a specific commit and should
only be updated deliberately during service reviews.
"""
return await ( return await (
dag.container() dag.container()
.from_(NIX_IMAGE) .from_(NIX_IMAGE)
@ -271,6 +275,8 @@ class BlumeopsCi:
"nix-command flakes", "nix-command flakes",
"flake", "flake",
"update", "update",
"--exclude",
"nixpkgs-services",
"--accept-flake-config", "--accept-flake-config",
] ]
) )

View file

@ -82,6 +82,7 @@ caddy_services:
- name: authentik - name: authentik
host: "authentik.{{ caddy_domain }}" host: "authentik.{{ caddy_domain }}"
backend: "https://authentik.tail8d86e.ts.net" backend: "https://authentik.tail8d86e.ts.net"
cache_policy: spa
- name: ntfy - name: ntfy
host: "ntfy.{{ caddy_domain }}" host: "ntfy.{{ caddy_domain }}"
backend: "https://ntfy.tail8d86e.ts.net" backend: "https://ntfy.tail8d86e.ts.net"

View file

@ -31,6 +31,14 @@
{% for service in caddy_services %} {% for service in caddy_services %}
@{{ service.name }} host {{ service.host }} @{{ service.name }} host {{ service.host }}
handle @{{ service.name }} { handle @{{ service.name }} {
{% if service.cache_policy | default('') == 'spa' %}
# SPA cache policy: hashed static assets are immutable, HTML must revalidate.
# Prevents stale HTML from referencing chunk hashes that no longer exist.
@{{ service.name }}_static path /static/dist/*
header @{{ service.name }}_static Cache-Control "public, max-age=31536000, immutable"
@{{ service.name }}_html path /if/*
header @{{ service.name }}_html Cache-Control "no-cache"
{% endif %}
{% if service.backend.startswith('https://') %} {% if service.backend.startswith('https://') %}
reverse_proxy {{ service.backend }} { reverse_proxy {{ service.backend }} {
# Caddy v2.11+ rewrites Host to upstream for HTTPS backends. # Caddy v2.11+ rewrites Host to upstream for HTTPS backends.

View file

@ -0,0 +1 @@
Pin NixOS service versions (forgejo-runner, snowflake, k3s) via `nixpkgs-services` overlay in ringtail flake, preventing silent upgrades from `nix flake update`. Add k3s and minikube to service-versions.yaml tracking. Fix stale nix-container-builder version (was 12.6.4, actually running 12.7.2).

View file

@ -57,9 +57,13 @@ For all service types, start by reading the service's reference card (`docs/refe
### NixOS Services (`type: nixos`) ### NixOS Services (`type: nixos`)
Versioned NixOS services (forgejo-runner, snowflake, k3s) are pinned via a `nixpkgs-services` overlay in `nixos/ringtail/flake.nix`. This prevents `nix flake update` from silently upgrading them — they only change when the `nixpkgs-services` input is deliberately updated.
1. Check the upstream project for new releases 1. Check the upstream project for new releases
2. Review the Nix derivation or flake input for version pins 2. Check what version nixpkgs has: `ssh ringtail 'nix eval nixpkgs#<pkg>.version'`
3. If upgrading, update and deploy via `mise run provision-ringtail` 3. To upgrade, update the `nixpkgs-services` rev in `flake.nix` to a nixpkgs commit that includes the desired version, then run `nix flake update nixpkgs-services` from `nixos/ringtail/`
4. Deploy via `mise run provision-ringtail`
5. Update `service-versions.yaml` with the new version
### Private Forge Repos (`upstream-source` under `forge.eblu.me/eblume/`) ### Private Forge Repos (`upstream-source` under `forge.eblu.me/eblume/`)

View file

@ -108,6 +108,10 @@ A native Forgejo Actions runner (`ringtail-nix-builder`) runs as a systemd servi
The runner resolves `<nixpkgs>` from the flake registry at build time. Container trust policy (`/etc/containers/policy.json`) and registry search order (`/etc/containers/registries.conf`) are configured minimally in `configuration.nix` for skopeo — no full `virtualisation.containers` module needed. The runner resolves `<nixpkgs>` from the flake registry at build time. Container trust policy (`/etc/containers/policy.json`) and registry search order (`/etc/containers/registries.conf`) are configured minimally in `configuration.nix` for skopeo — no full `virtualisation.containers` module needed.
## Pinned Service Versions
Versioned services (forgejo-runner, snowflake, k3s) are pinned via a `nixpkgs-services` overlay in `flake.nix`, separate from the rolling `nixpkgs` input. This prevents `nix flake update` from silently upgrading them. The Dagger `flake-update` pipeline excludes `nixpkgs-services` automatically. See [[review-services]] for the upgrade procedure.
## Maintenance Notes ## Maintenance Notes
**1Password:** Desktop app must be running for `op` CLI. Use `$mod+Shift+minus` to send to scratchpad. **1Password:** Desktop app must be running for `op` CLI. Use `$mod+Shift+minus` to send to scratchpad.

View file

@ -57,11 +57,28 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-services": {
"locked": {
"lastModified": 1774388614,
"narHash": "sha256-tFwzTI0DdDzovdE9+Ras6CUss0yn8P9XV4Ja6RjA+nU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"disko": "disko", "disko": "disko",
"home-manager": "home-manager", "home-manager": "home-manager",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs",
"nixpkgs-services": "nixpkgs-services"
} }
} }
}, },

View file

@ -3,6 +3,12 @@
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
# Pinned nixpkgs for versioned services (forgejo-runner, snowflake, k3s).
# Update this deliberately during service reviews, not via `nix flake update`.
# Current versions: forgejo-runner 12.7.2, snowflake 2.11.0, k3s 1.34.5+k3s1
nixpkgs-services.url = "github:NixOS/nixpkgs/1073dad219cb244572b74da2b20c7fe39cb3fa9e";
disko = { disko = {
url = "github:nix-community/disko"; url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@ -13,7 +19,7 @@
}; };
}; };
outputs = { nixpkgs, disko, home-manager, ... }: { outputs = { nixpkgs, nixpkgs-services, disko, home-manager, ... }: {
nixosConfigurations.ringtail = nixpkgs.lib.nixosSystem { nixosConfigurations.ringtail = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
modules = [ modules = [
@ -22,6 +28,18 @@
./disk-config.nix ./disk-config.nix
./hardware-configuration.nix ./hardware-configuration.nix
./configuration.nix ./configuration.nix
# Pin versioned services to nixpkgs-services instead of the rolling nixpkgs.
# This prevents `nix flake update nixpkgs` from silently upgrading them.
# Bump nixpkgs-services explicitly during service reviews.
({ ... }: {
nixpkgs.overlays = [
(final: prev: let svcPkgs = nixpkgs-services.legacyPackages.x86_64-linux; in {
forgejo-runner = svcPkgs.forgejo-runner;
snowflake = svcPkgs.snowflake;
k3s = svcPkgs.k3s;
})
];
})
]; ];
}; };
}; };

View file

@ -252,17 +252,39 @@ services:
- name: nix-container-builder - name: nix-container-builder
type: nixos type: nixos
last-reviewed: 2026-02-22 last-reviewed: 2026-04-01
current-version: "12.6.4" current-version: "12.7.2"
upstream-source: https://code.forgejo.org/forgejo/runner/releases upstream-source: https://code.forgejo.org/forgejo/runner/releases
notes: Forgejo runner on ringtail via nixpkgs; version tracks flake.lock notes: >-
Forgejo runner on ringtail; pinned via nixpkgs-services overlay in flake.nix.
Update nixpkgs-services rev during service reviews, not via nix flake update.
- name: snowflake-proxy - name: snowflake-proxy
type: nixos type: nixos
last-reviewed: 2026-03-24 last-reviewed: 2026-04-01
current-version: "2.11.0" current-version: "2.11.0"
upstream-source: https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/releases upstream-source: https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/releases
notes: Tor Snowflake proxy on ringtail; anti-censorship bridge, not an exit node notes: >-
Tor Snowflake proxy on ringtail; pinned via nixpkgs-services overlay in flake.nix.
Anti-censorship bridge, not an exit node.
- name: k3s
type: nixos
last-reviewed: 2026-04-01
current-version: "1.34.5+k3s1"
upstream-source: https://github.com/k3s-io/k3s/releases
notes: >-
Single-node k3s cluster on ringtail; pinned via nixpkgs-services overlay in flake.nix.
Update nixpkgs-services rev during service reviews.
- name: minikube
type: ansible
last-reviewed: 2026-04-01
current-version: "1.38.0"
upstream-source: https://github.com/kubernetes/minikube/releases
notes: >-
Single-node minikube on indri; installed via homebrew (not version-pinned).
Homebrew may silently upgrade on brew update/upgrade.
- name: mealie - name: mealie
type: argocd type: argocd