salsa.debian.org renamed the packaging branch from master to
debian/latest, so the link 404s. The English guide was already updated;
the README and the translations were left behind.
Uses the same anchor as the English guide (#L60-L61), which is where the
two include directives now sit.
The demo image is built on nginx:latest, which ships no procps, so the
`pkill -TERM nginx-ui` in the shutdown handler was not a command at all —
it exited 127 straight into /dev/null, and `|| true` swallowed that too.
The consequences ran the whole way through. The three nginx-ui children
were never signalled, so the bare `wait` blocked on them forever and the
`exit 0` after it was never reached. nginx had already been stopped by the
line above, so the container went on living as PID 1 with nothing bound to
8080: Cloudflare kept reporting it healthy while every request got "The
container is not listening in the TCP address". Since the platform sends
SIGTERM on the sleepAfter idle expiry as well as on the daily restore and a
manual recycle, this fired on its own, without anyone touching the demo,
within twenty minutes of the container going quiet.
Signal the PIDs this script recorded when it started them, so shutdown
depends on nothing outside the shell. Bound the wait: nginx-ui holds an open
SQLite handle and deserves a chance to close it, but a child that will not
exit must not be able to keep the container alive, so anything still running
after ten seconds is killed.
Verified against the real base image: with the previous handler the
container is still running thirty seconds after SIGTERM with 8080 refusing
connections and "received termination signal" as its last log line — the
production symptom exactly. With this one it exits 0 in six seconds, or in
fifteen when the children ignore SIGTERM.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An instance sat 'running' for nineteen hours with nothing bound to 8080.
Every request got the platform's own "container is not listening" error,
getState() reported 'healthy' throughout, and the demo never came back.
ready() returned early on a healthy state, so the only path that starts a
container was unreachable exactly when the container needed starting. The
platform's view of a container is not the truth, and this code treated it
as if it were.
A container that reports healthy without serving for a full boot window is
now stopped so the next request starts a fresh one, escalating to a kill if
the stop changes nothing. Deliberately slow to trigger and rate limited:
stopping a container that is merely slow to boot is how an earlier version
of this file put the loading page into a permanent refresh loop, so the
decision lives in a pure function with tests that assert two recoveries can
never land closer together than one cooldown.
Also stops the same outage from generating its own load. The loading page
polled /__demo/status forever, and each poll probed the container twice —
once via status(), once via ready() — so every open tab added two platform
error events per second to a container that was already down. status() now
drives the start itself, probes are cached for a couple of seconds, and the
page stops polling after five minutes instead of never.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>