public mail vpn: reload caddy on a Caddyfile change instead of restarting it (nix-config#926) #1960

Merged
hermes merged 1 commit from kb/NCFG-926-caddy-reload into main 2026-10-13 05:27:39 -06:00
Collaborator

Card: nix-config#926 (board https://board.ymrtech.com)

What

services.caddy.enableReload = false on public, mail and vpn -> true.

With false, the nixpkgs caddy module puts the rendered Caddyfile in
restartTriggers, so any Caddyfile change (vhost, route, cert line, log format)
is a full systemctl restart caddy = whole-site outage. Measured on public:
56 restarts in 7 days, 4-15 s each (VictoriaLogs UNIT:"caddy.service" AND (_msg:"Stopping" OR _msg:"Started")).

With true the Caddyfile moves to reloadTriggers and the module adds
ExecReload = caddy reload --config /etc/caddy/caddy_config --adapter caddyfile --force,
which is graceful: the old process keeps serving until the new config is applied.
true is the nixpkgs module default; vpn-mx never overrode it, so these three
hosts were the odd ones out. All three already configure the loopback admin API
the reload path needs (admin localhost:2019 in globalConfig).

Evidence (evaluated from this branch)

For each of public, mail and vpn, systemd.services.caddy now reads:

ExecReload    = ["", "...caddy reload --config /etc/caddy/caddy_config --adapter caddyfile --force"]
reloadTriggers  = 1 entry
restartTriggers = 0 entries

and each host's system.build.toplevel.drvPath evaluates.

X-Reload-Triggers is handled by the rust switch-to-configuration shipped in
the pinned nixpkgs (rev 7b66dac): it prints would reload the following units:.

Note on the live check

The switch that MOVES the config from restart to reload triggers also changes the
unit definition, so it can still restart caddy once - that transition is
unavoidable. The DoD's "a deploy that changes the Caddyfile reloads" is therefore
verified with a second, config-content-only deploy on public (PR2), which changes
nothing but the Caddyfile.

Card: nix-config#926 (board https://board.ymrtech.com) ## What `services.caddy.enableReload = false` on public, mail and vpn -> `true`. With `false`, the nixpkgs caddy module puts the rendered Caddyfile in `restartTriggers`, so *any* Caddyfile change (vhost, route, cert line, log format) is a full `systemctl restart caddy` = whole-site outage. Measured on public: 56 restarts in 7 days, 4-15 s each (VictoriaLogs `UNIT:"caddy.service" AND (_msg:"Stopping" OR _msg:"Started")`). With `true` the Caddyfile moves to `reloadTriggers` and the module adds `ExecReload = caddy reload --config /etc/caddy/caddy_config --adapter caddyfile --force`, which is graceful: the old process keeps serving until the new config is applied. `true` is the nixpkgs module default; `vpn-mx` never overrode it, so these three hosts were the odd ones out. All three already configure the loopback admin API the reload path needs (`admin localhost:2019` in `globalConfig`). ## Evidence (evaluated from this branch) For each of public, mail and vpn, `systemd.services.caddy` now reads: ExecReload = ["", "...caddy reload --config /etc/caddy/caddy_config --adapter caddyfile --force"] reloadTriggers = 1 entry restartTriggers = 0 entries and each host's `system.build.toplevel.drvPath` evaluates. `X-Reload-Triggers` is handled by the rust `switch-to-configuration` shipped in the pinned nixpkgs (rev 7b66dac): it prints `would reload the following units:`. ## Note on the live check The switch that MOVES the config from restart to reload triggers also changes the unit definition, so it can still restart caddy once - that transition is unavoidable. The DoD's "a deploy that changes the Caddyfile reloads" is therefore verified with a second, config-content-only deploy on public (PR2), which changes nothing but the Caddyfile.
public mail vpn: reload caddy on a Caddyfile change instead of restarting it
All checks were successful
PR base guard / Base drift / conflict check (pull_request) Successful in 4s
NixOS Build Check / Flake input regression guard (pull_request) Successful in 5s
NixOS Build Check / Scope changed deployable hosts (pull_request) Successful in 7s
Secret Scan / gitleaks scan (pull_request) Successful in 11s
NixOS Build Check / Build changed host closures (native, one eval) (pull_request) Successful in 2m32s
foremerge intent (advisory) all 3 changed path(s) covered by a live foremerge intent
NixOS Build Check / Worker-scope reaper worker signature (mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / Kanban pool capacity knob (runtime override + clamp, mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / Sentinel pass budget contract (prompt forbids the full board walk, mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / CI-scratch reaper class table (mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / foremerge accept-order contract (real binary) (pull_request) Successful in 5s
NixOS Build Check / .forgejo/tests python suites (hermetic) (pull_request) Successful in 42s
NixOS Build Check / Repo gates (the folded selftest battery) (pull_request) Successful in 3m45s
NixOS Build Check / Hermes runtime gates (one eval of vpn's runtime) (pull_request) Successful in 4m23s
345cb952b2
services.caddy.enableReload was false on public, mail and vpn, so the nixpkgs
caddy module put the rendered Caddyfile in restartTriggers: every deploy that
touched a vhost, route, cert or log line did a full caddy restart, i.e. a
whole-site outage. Measured on public: 56 restarts in 7 days, 4-15 s each.

Set enableReload = true (the module default; vpn-mx never overrode it), so the
Caddyfile moves to reloadTriggers and ExecReload runs "caddy reload --config
/etc/caddy/caddy_config --adapter caddyfile --force", which needs the loopback
admin API all three hosts already configure (admin localhost:2019).
hermes merged commit 57746cc4b7 into main 2026-10-13 05:27:39 -06:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ymrtech/nix-config!1960
No description provided.