Three of five deploy-test runs on master today failed the ipv4-full smoke test with 443 never answering (runs 29503622136, 29505653710, and the SSM-enabled dispatch 29507690672). The stack reaches CREATE_COMPLETE every time; the box just never serves.
The enable_ssm=true diagnostics (shipped in PR 79 for exactly this) finally showed why:
systemctl --failed → 0 loaded units listed
Unit caddy.service could not be found.
amazon-init: /nix/store/.../amazon-init-start: line 17: /etc/ec2-metadata/user-data: No such file or directory
amazon-init: no user data is available
The instance is a vanilla NixOS AMI — the first-boot nixos-rebuild never ran, so no caddy/ttyd/anything. Root cause is in the stock AMI's fetch-ec2-metadata.service (ec2-metadata-fetcher.sh):
- The IMDSv2 token fetch uses
--connect-timeout 1 with a handful of 1s retries (~10-15s total window). If IMDS is slow to respond at boot, it logs failed to fetch an IMDSv2 token. and continues with an empty token.
- The subsequent user-data fetch 401s and is swallowed by
|| true — no file written, unit exits 0 (hence zero failed units).
amazon-init finds no file, prints "no user data is available", also exits 0.
Net effect: a transient IMDS hiccup at first boot silently yields a permanently unprovisioned box. This is not CI-only — 1-click users hit the same dice roll, and for them it looks like a dead WebURL with a green CloudFormation stack. Upstream awareness: nixpkgs issue 449166 (open) is adjacent; nixpkgs PR/issue 447266 added the retries but kept the fail-open behavior.
Also: the deploy-test step "Assert amazon-init provisioned the box (serial console)" passed on this unprovisioned box — it greps for Finished ... amazon-init, and a no-user-data boot finishes "successfully" having done nothing.
Proposed fixes, in order of leverage:
- CI fail-fast (cheap, done first):
fetch-ec2-metadata logs with StandardOutput=journal+console, so failed to fetch an IMDSv2 token. IS visible in get-console-output. Make the serial-console assertion fail immediately on that marker (and on no user data is available if it proves console-visible) instead of burning 10 minutes of curl 000 and misattributing the failure.
- Product-level visibility: add a
CreationPolicy/WaitCondition to the template, signaled (plain curl to the presigned URL, no cfn tools needed) at the end of our user-data activation. An unprovisioned box then turns into a visible CREATE_FAILED instead of a silently dead URL. Design call: rollback vs. keep-for-debug, timeout length.
- Self-healing: our user-data can't fix this (it's precisely what never runs). Real options are a custom AMI, or fixing upstream (make the fetcher fail the unit +
Restart=on-failure, or make amazon-init require the user-data file when the instance has user data). Worth an upstream issue/PR referencing this evidence.
Three of five deploy-test runs on master today failed the ipv4-full smoke test with 443 never answering (runs 29503622136, 29505653710, and the SSM-enabled dispatch 29507690672). The stack reaches CREATE_COMPLETE every time; the box just never serves.
The
enable_ssm=truediagnostics (shipped in PR 79 for exactly this) finally showed why:The instance is a vanilla NixOS AMI — the first-boot
nixos-rebuildnever ran, so no caddy/ttyd/anything. Root cause is in the stock AMI'sfetch-ec2-metadata.service(ec2-metadata-fetcher.sh):--connect-timeout 1with a handful of 1s retries (~10-15s total window). If IMDS is slow to respond at boot, it logsfailed to fetch an IMDSv2 token.and continues with an empty token.|| true— no file written, unit exits 0 (hence zero failed units).amazon-initfinds no file, prints "no user data is available", also exits 0.Net effect: a transient IMDS hiccup at first boot silently yields a permanently unprovisioned box. This is not CI-only — 1-click users hit the same dice roll, and for them it looks like a dead WebURL with a green CloudFormation stack. Upstream awareness: nixpkgs issue 449166 (open) is adjacent; nixpkgs PR/issue 447266 added the retries but kept the fail-open behavior.
Also: the deploy-test step "Assert amazon-init provisioned the box (serial console)" passed on this unprovisioned box — it greps for
Finished ... amazon-init, and a no-user-data boot finishes "successfully" having done nothing.Proposed fixes, in order of leverage:
fetch-ec2-metadatalogs withStandardOutput=journal+console, sofailed to fetch an IMDSv2 token.IS visible inget-console-output. Make the serial-console assertion fail immediately on that marker (and onno user data is availableif it proves console-visible) instead of burning 10 minutes of curl 000 and misattributing the failure.CreationPolicy/WaitCondition to the template, signaled (plaincurlto the presigned URL, no cfn tools needed) at the end of our user-data activation. An unprovisioned box then turns into a visible CREATE_FAILED instead of a silently dead URL. Design call: rollback vs. keep-for-debug, timeout length.Restart=on-failure, or makeamazon-initrequire the user-data file when the instance has user data). Worth an upstream issue/PR referencing this evidence.