boot: stop stacks through compose at shutdown so just up orders the start #407

Merged
mysticalsoap merged 1 commit from boot/ordered-stop into trunk 2026-10-08 13:40:33 -04:00
Owner

Stacked on #406 (base is its branch); retarget to trunk once that merges.

Problem

docker-compose-boot.service was meant to apply the compose files' depends_on order at boot, but dockerd replays restart: unless-stopped for every container first, in parallel. At today's boot the unit started four seconds after dockerd and found everything already running; Authelia had beaten LDAP up and been restarted by the policy. The Home-Server page claimed the unit solved this.

Fix

  • just stop: compose stop per stack in reverse order, containers kept.
  • docker-compose-stop.service: RemainAfterExit, ExecStop=just stop, after docker.service so it runs before dockerd goes down. CLI-stopped containers are flagged manually stopped and the restart policy skips them at the next daemon start, so just up is the only starter.
  • PartOf=docker.service on both units: systemctl restart docker goes through stop-then-up instead of leaving the stacks down.
  • Home-Server page rewritten for the pair, with the install lines.

Interim until the Quadlet move.

Verification

Live, 2026-10-08:

  • systemd-analyze verify on both units: clean.
  • just stop dagu: four containers stopped in 11s, kept (docker ps -a shows them Exited); just up dagu back to healthy in 6s. dagu, dagu-tools and dagu-scanner exit 137, i.e. they sit out the 10s grace and get killed, same as at every shutdown today; not changed here.
  • Not yet verified on the host, needs root: unit install and systemctl restart docker (full stop-then-up, ~2 min of downtime), then the next reboot's journal should show just up doing the starting with no Authelia startup-check line.

🤖 Generated with Claude Code

Stacked on #406 (base is its branch); retarget to trunk once that merges. ## Problem `docker-compose-boot.service` was meant to apply the compose files' `depends_on` order at boot, but dockerd replays `restart: unless-stopped` for every container first, in parallel. At today's boot the unit started four seconds after dockerd and found everything already running; Authelia had beaten LDAP up and been restarted by the policy. The Home-Server page claimed the unit solved this. ## Fix - `just stop`: compose `stop` per stack in reverse order, containers kept. - `docker-compose-stop.service`: `RemainAfterExit`, `ExecStop=just stop`, after `docker.service` so it runs before dockerd goes down. CLI-stopped containers are flagged manually stopped and the restart policy skips them at the next daemon start, so `just up` is the only starter. - `PartOf=docker.service` on both units: `systemctl restart docker` goes through stop-then-up instead of leaving the stacks down. - Home-Server page rewritten for the pair, with the install lines. Interim until the Quadlet move. ## Verification Live, 2026-10-08: - `systemd-analyze verify` on both units: clean. - `just stop dagu`: four containers stopped in 11s, kept (`docker ps -a` shows them Exited); `just up dagu` back to healthy in 6s. `dagu`, `dagu-tools` and `dagu-scanner` exit 137, i.e. they sit out the 10s grace and get killed, same as at every shutdown today; not changed here. - Not yet verified on the host, needs root: unit install and `systemctl restart docker` (full stop-then-up, ~2 min of downtime), then the next reboot's journal should show `just up` doing the starting with no Authelia startup-check line. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
boot: stop the stacks through compose at shutdown so just up orders the start
All checks were successful
validate / validate (pull_request) Successful in 19s
44e3b227f8
docker-compose-boot runs just up after docker.service, but dockerd has
already replayed restart: unless-stopped for every container by then,
flat and in parallel, and Compose orders only what it has to start. At
this boot the unit started four seconds after dockerd and found every
long-lived container running; Authelia had already beaten LDAP up,
failed its startup check and been restarted by the policy.

A container stopped through the Compose CLI is flagged as manually
stopped and the policy skips it at the next daemon start, so a unit
that runs just stop before docker.service goes down leaves just up as
the only starter. PartOf=docker.service on both units gives a plain
systemctl restart docker the same stop-then-up; without it the stacks
would stay down after one. The boot unit keeps Restart=on-failure, so
the stop step is a second unit: RemainAfterExit and Restart are
mutually exclusive.

Interim until the Podman Quadlet move, where each container is a unit
and the ordering is systemd's own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mysticalsoap changed target branch from alerts/404-findings to trunk 2026-10-08 13:40:25 -04:00
mysticalsoap deleted branch boot/ordered-stop 2026-10-08 13:40:33 -04:00
Sign in to join this conversation.
No description provided.