deploy: set restart: on-failure in docker-compose.yml — unless-stopped bypasses docker-host's boot pacing #306
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Raised from
Rhoving/iac-repo#279. One-line-per-service change; the reason is not obvious from this repo.Context
Quest-Board's stack runs on docker-host (VM 110), which sits on an 8-wide raidz1 of 7200 rpm HDDs — roughly single-disk random IOPS shared by ~16 Compose stacks. Starting them all at once at boot saturates the pool and stalls the ext4 journal; that is what took docker-host and Forgejo down on 2026-07-06.
The fix there is
staggered-stacks, a systemd unit that starts each stack one at a time, paced to disk I/O. It only works if dockerd starts nothing by itself — the unit must be the sole boot authority.A container with
restart: unless-stoppedoralwaysis restored by dockerd as soon as the daemon starts, before the paced sequence reaches it. The stack opts itself out of the pacing and races the mechanism meant to protect it.What's needed
Seven Quest-Board containers were on
unless-stopped:They have been flipped live with
docker update --restart on-failure, so the current boot is protected.docker updatedoes not touch the compose file, so the nextdocker compose up -dfrom this repo silently restoresunless-stopped.Change
restart: unless-stoppedtorestart: on-failurefor those services indocker-compose.yml/docker-compose.override.yml.Leave
quest-board-migrate-1alone — it is correctly onno, being a one-shot migration container. It should not restart at all.Does this lose anything?
Not on this host.
on-failurestill restarts a container that crashes. The only behaviour given up is "come back automatically when the Docker daemon starts", which is precisely what is being removed on purpose —staggered-stacksprovides it in a paced form instead.Verification
Rhoving/iac-repoPR #296 adds a check todocker-host-ansiblethat reads the staggered-stacks manifest and fails if any container in it is onalways/unless-stopped. Once merged, drift here surfaces as a red playbook run rather than a slow boot nobody attributes correctly.Refs Rhoving/iac-repo#279, Rhoving/iac-repo#84, Rhoving/iac-repo#296
Done in PR #481, with one deviation and one correction to this issue.
The drift had already happened
This issue says the containers "have been flipped live with
docker update --restart on-failure, so the current boot is protected". That was no longer true. All seven were back onunless-stoppedon docker-host when I checked — undone by acompose up -dat some point since 2026-08-12, exactly as this issue predicted would happen.So docker-host was not protected against the boot race, and had not been for some time. Worth knowing, because the issue reads as though only a future deploy was at risk.
Deviation: a variable, not a hardcoded
on-failureThe seven services are now
restart: ${RESTART_POLICY:-unless-stopped}.The reasoning here — shared spindles, staggered-stacks owning boot order — is specific to docker-host, but
docker-compose.ymlships to every install. Hardcodingon-failurewould mean any other deployment silently stops coming back after a reboot, and "safe for other people to run" is an explicit v4.x goal. Unset, the default isunless-stoppedand nothing changes for anyone; the host that needson-failureopts in through its own.env, which is where host-specific config belongs.iac-repo#296's check is unaffected either way — it reads the running containers, not this file.
Applied on the hosts
RESTART_POLICY=on-failureappended to.env(kept at mode 600), and the seven running containers re-flipped withdocker update.migrateleft on"no". Non-disruptive —docker compose psstill showed "Up 3 days (healthy)" across all seven afterwards, so nothing cycled. The nextcompose up -dnow preserves the policy rather than reverting it, which closes the loop.staggered-stacksunit — verified — so the rationale does not apply and it should keepunless-stoppedso it returns after a reboot.Also recorded in the file
This issue notes the reason "is not obvious from this repo", so the compose header and
.env.examplenow carry it: the disk topology, the 2026-07-06 outage, whyon-failurerather thanalways, and whymigratestays"no". Someone readingon-failureand thinking it looks wrong now has the answer in front of them instead of needing to find iac-repo#279.docker compose configverified to resolve correctly both ways: unset → 7 ×unless-stopped,RESTART_POLICY=on-failure→ 7 ×on-failure,migrate"no"in both.