Files
template-tutoring-admin-das…/supervisord.conf
T

82 lines
3.0 KiB
Plaintext
Raw Normal View History

2026-09-14 23:53:29 +05:30
# Two processes in one container, and the rules that make that survivable.
#
# A container with two processes needs something that reaps them, restarts
# them, and — most importantly — dies when either one dies for good. Without
# that last part the container stays "up" with half an app in it, the
# orchestrator sees a healthy container, and the only symptom is that every
# request 502s.
[supervisord]
nodaemon=true
user=root
logfile=/dev/stdout
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
# Without these three, `supervisorctl` inside the container refuses to run at
# all — "ini file does not include supervisorctl section" — so the one tool for
# asking which half of the app is down, or restarting it without killing the
# container, is unavailable exactly when it is needed. The socket is a file in
# the container with no port and no network listener, so this costs nothing.
[unix_http_server]
file=/tmp/supervisor.sock
chmod=0700
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:backend]
command=uvicorn main:app --host 127.0.0.1 --port 8001 --root-path /api
directory=/app/backend
autostart=true
autorestart=true
# Three failures in a row is a real failure, not a slow start. Retrying forever
# hides a crash-looping app behind a container that never exits.
startretries=3
startsecs=5
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
# Python buffers stdout when it is a pipe, and supervisord gives it a pipe.
# Without this the app's logs appear only when the buffer fills — which for a
# crash means never, and the traceback that would explain it is lost.
environment=PYTHONUNBUFFERED="1"
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
startretries=3
# Started after the backend so the first proxied request has somewhere to go.
# It is only ordering, not waiting: nginx starts whether or not the backend is
# ready, which is correct — it serves the frontend meanwhile.
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[eventlistener:quit_on_failure]
# The part that matters. When either program enters FATAL — supervisord has
# given up restarting it — this stops supervisord, so the container exits and
# whatever supervises it can react. Otherwise the container lives on with one
# half of the app missing: healthy from outside, a total outage from a browser.
#
# A real script rather than an inline `sh -c`, because the listener protocol is
# a length-prefixed exchange and getting it wrong does not raise — it leaves a
# listener reading forever while the thing it watches stays broken. See the
# script's own docstring; the shell version of this genuinely did that.
command=python3 -u /app/quit_on_failure.py
events=PROCESS_STATE_FATAL
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0