Files
OpenFUT/scripts/systemd/openfut-staging-host.service
T
funman300 8ae432223a ops: systemd supervision for Core and the FIFA17 host (staging-proven)
Replaces the detached `setsid nohup … nsenter …` launch, which had no restart
policy, no boot persistence and no supervisor-visible logs. Staging units are
installed and proven; production units are TEMPLATES and are not installed.

Three decisions, each measured rather than assumed:

* `Wants=`, not `Requires=`, from host to Core. With `Requires`, stopping Core
  stopped the host AND a later Core start did not bring it back -- a routine
  Core restart would leave the client with no server. With `Wants` the host
  survives a Core outage, answers 503 core_unavailable, never falls back to
  Python, and resumes the moment Core returns with no intervention. Both halves
  tested.
* Readiness is a bounded ExecStartPre TCP gate, because ordering proves nothing
  about readiness and Type=exec only proves the binary exec'd. Core binds its
  listener after migrations and content load, so "port open" is a real signal.
  The gate FAILS rather than blocking: a host that waits forever looks healthy
  while serving nobody.
* The netns is resolved by container NAME every start. The container is
  restart=unless-stopped and its netns inode CHANGES on restart (measured:
  4026539938 -> 4026540033), so a hardcoded pid is wrong by construction and
  anything left in the old namespace serves nobody. Proven equivalent to today's
  nsenter against a scratch container, never production's namespace.

`systemd-analyze verify` caught two real defects before deployment:
StartLimitIntervalSec/StartLimitBurst sat in [Service], where systemd 252
silently ignores them, so the crash-loop ceiling was not taking effect; and a
Documentation URL containing %20 parsed as a specifier. Both fixed and the
effective properties re-confirmed from the running units.

Staging evidence: Core-first ordering, host refused when Core is absent or
merely not listening, outage survival, automatic recovery, restart, graceful
stop with no strays, boot simulated via multi-user.target, 3x SIGKILL contained
at ~5s spacing, journald logs, and economy state byte-identical throughout
(integrity ok, fk 0).
2026-08-22 20:46:15 +00:00

56 lines
2.1 KiB
Desktop File

[Unit]
Description=OpenFUT FIFA17 UTAS host (STAGING) — client-facing, Core-dependent
Documentation=file:///home/alex/OpenFUT/scripts/systemd/README.md
# ORDERING AND ADMISSION. `Wants` + `After` order the host after Core and pull
# Core in when the host is started; neither gives READINESS, because Type=exec
# only proves the binary exec'd. The ExecStartPre below is what actually admits
# traffic: the host cannot reach "active" while Core is not listening.
Wants=openfut-staging-core.service
After=openfut-staging-core.service
# `Wants`, deliberately NOT `Requires`/`BindsTo`/`PartOf`. Those propagate a
# Core stop into a host stop, and — measured, not assumed — a later Core start
# does NOT bring the host back, so a routine Core restart would silently leave
# the client with no server at all.
#
# With `Wants` the host survives a Core outage and answers 503
# `core_unavailable`, never falling back to Python. That is the behaviour
# production already exhibited on 2026-08-22 when Core was SIGHUP'd out from
# under a live host. Because the host holds no Core state between requests, it
# resumes serving the moment Core returns, with no supervisor intervention.
# StartLimit* MUST live in [Unit]: systemd 252 silently IGNORES them in
# [Service] (`systemd-analyze verify` flags it), which would have left the
# crash-loop ceiling at the 10s/5 default instead of the intended 60s window.
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=exec
User=alex
Group=alex
WorkingDirectory=/home/alex/openfut-sold-staging
EnvironmentFile=/home/alex/openfut-sold-staging/systemd/host.env
# Readiness gate. Bounded, and FAILS rather than blocking forever: a host that
# waits indefinitely looks healthy to the supervisor while serving nothing.
ExecStartPre=/home/alex/OpenFUT/scripts/systemd/openfut-wait-tcp.sh 127.0.0.1 18081 30
ExecStart=/home/alex/openfut-sold-staging/bin/openfut-utas-host
KillSignal=SIGTERM
KillMode=mixed
TimeoutStopSec=20
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=openfut-staging-host
[Install]
WantedBy=multi-user.target