fix: the image brings its own PID 1 instead of asking for init: true (4295)
CI / lint (push) Successful in 3s
CI / extension-version (push) Successful in 3s
Build images / sign-extension (push) Successful in 4s
Build images / build-agent (push) Successful in 6s
extension / lint (push) Successful in 19s
CI / frontend-build (push) Successful in 22s
CI / backend-lint-and-test (push) Successful in 33s
CI / integration (push) Successful in 2m15s
Build images / build-web (push) Successful in 2m52s
Build images / smoke-web (push) Successful in 1m16s
Build images / promote (push) Skipped

Operator, 2026-09-23: *"it is out of the norm to require this init call we
need to fix this."* Correct, and it is the same mistake as declaring the
healthcheck per service — the image needing a deployment to remember a flag
before it behaves correctly.

PID 1 carries a duty no other process has: every orphaned process in the
container reparents to it and must be reaped or it stays a zombie holding a
PID slot. This app makes orphans in normal operation — six service modules
shell out (gallery_dl, thumbnailer, backup_service, external_fetch,
download_service, download_backends) and celery's prefork pool forks children
that spawn them.

Whatever the role, something that is not an init ends up as PID 1:
supervisord for `all`, hypercorn for `web`, celery for a worker. `init: true`
covered that, and cost correctness the moment it was forgotten or silently
dropped — an older Swarm, a plain `docker run`, a compose file someone
copied. No signal either way.

So tini goes in the image and is the ENTRYPOINT. `docker run <image>` is
correct on its own now, `init: true` comes out of docker-compose.single.yml,
and nothing downstream has to know. The smoke asserts /proc/1/comm is tini,
read from /proc because the runtime stage installs no `ps`.

## A correction to what I told the operator

I justified `init: true` by saying supervisord "has no idea about orphans it
never started". That is very likely wrong: supervisord's reaper calls
waitpid(-1) and logs "reaped unknown pid" for children it did not spawn, so
it does reap orphans. I asserted the mechanism without checking it, and could
not check it here — `supervisor` is not installed in this environment.

It does not change this commit. tini is correct whichever way that lands, and
it covers the single-role containers too, where celery or hypercorn is PID 1
and the subprocess-spawning is heaviest. But the reason I gave was not a
verified one and should not have been stated as fact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
2026-09-23 10:18:47 -04:00
co-authored by Claude Opus 5
parent 22dcbcfb74
commit b74a4c964b
3 changed files with 37 additions and 7 deletions
+13
View File
@@ -1553,6 +1553,19 @@ jobs:
exit 1
fi
echo "smoke: every lane answered"
# PID 1 is an init, and the IMAGE provides it — no `init: true` in
# whatever runs this. Read from /proc rather than `ps`, which the
# runtime stage does not install.
PID1=$(docker exec "$CID_ALL" cat /proc/1/comm)
echo "smoke: pid 1 is $PID1"
if [ "$PID1" != "tini" ]; then
echo "smoke: FAILED — pid 1 is '$PID1', not an init." >&2
echo "smoke: orphaned gallery-dl/ffmpeg/pg_dump processes would" >&2
echo "smoke: accumulate as zombies, and the image would be back" >&2
echo "smoke: to needing init:true from every deployment." >&2
exit 1
fi
# Say WHICH processes supervisord is running, so a lane that is
# merely restart-looping is visible rather than inferred.
#