fix(headscale): bootstrap the TLS cert on a fresh node #65

Merged
jfraeys merged 2 commits from jfraeys/fix/headscale-fresh-node-cert into main 2026-10-11 14:53:15 -04:00
Owner

Staging run 457 got through 215 tasks of services.yml, then failed at headscale : Extract certificate from Traefik acme.json with Domain not found in acme.json. The role extracted the cert before starting Headscale, but Traefik only requests it once the Headscale router exists, so any fresh node (staging, and a DR rebuild of production) deadlocked. Production never hit it because its cert already exists.

Now: extraction is non-fatal; if no cert is installed, create a 3-day self-signed placeholder; after Headscale is healthy, retry the extraction (40 x 10s) and restart Headscale once the real cert arrives. Steady state (cert present) skips both new steps. The extraction script moved into one role var shared by both tasks. Tested the script for: no acme.json (rc 0), other domain only (rc 1), domain present (installs, modes 644/600); placeholder command output; ansible-lint, yamllint, syntax-check. Behaviour change: a missing domain in acme.json no longer fails the first task when a cert is already installed. 🤖 Generated with Claude Code

Staging run 457 got through 215 tasks of services.yml, then failed at `headscale : Extract certificate from Traefik acme.json` with `Domain not found in acme.json`. The role extracted the cert before starting Headscale, but Traefik only requests it once the Headscale router exists, so any fresh node (staging, and a DR rebuild of production) deadlocked. Production never hit it because its cert already exists. Now: extraction is non-fatal; if no cert is installed, create a 3-day self-signed placeholder; after Headscale is healthy, retry the extraction (40 x 10s) and restart Headscale once the real cert arrives. Steady state (cert present) skips both new steps. The extraction script moved into one role var shared by both tasks. Tested the script for: no acme.json (rc 0), other domain only (rc 1), domain present (installs, modes 644/600); placeholder command output; ansible-lint, yamllint, syntax-check. Behaviour change: a missing domain in acme.json no longer fails the first task when a cert is already installed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(headscale): bootstrap the TLS cert on a fresh node
All checks were successful
Lint / lint (pull_request) Successful in 1m23s
Secret scan / scan (pull_request) Successful in 12s
5e17fb5be7
The role extracted the vpn cert from Traefik's acme.json before starting Headscale, but Traefik only
requests it once the Headscale router exists, so a fresh node deadlocked (staging run 457). A short-lived
self-signed placeholder lets Headscale start; the role then waits for the real cert and restarts once.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fix(forgejo): make the OIDC auth-source task actually retry while the first DB migration runs
All checks were successful
Lint / lint (pull_request) Successful in 1m19s
Secret scan / scan (pull_request) Successful in 14s
28b3176fc5
retries/delay without `until` are ignored by Ansible, so the task never waited and failed with
"no such table: login_source" on a fresh node (masked by ignore_errors in staging, fatal in production).
Staging keeps a single attempt: its untrusted ACME CA means the task cannot succeed there anyway.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
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
jfraeys/infra!65
No description provided.