[Unit] Description=Thermograph (FastAPI/uvicorn) After=network-online.target Wants=network-online.target [Service] Type=exec User=deploy Group=deploy # The repo checkout. uvicorn is run from backend/ so `app:app` resolves. WorkingDirectory=/opt/thermograph/backend # Defaults; /etc/thermograph.env (below) overrides. WORKERS is how many uvicorn # worker processes to run — prod sets WORKERS=3 there; a single-worker box can leave # it. Multiple workers each keep their own in-process state, so metrics go to a shared # SQLite DB (THERMOGRAPH_METRICS_DB) that every worker tallies into — otherwise the ops # dashboard, polling one random worker, would see only a fraction of the traffic. Environment=WORKERS=1 Environment=THERMOGRAPH_METRICS_DB=/opt/thermograph/data/metrics.db # THERMOGRAPH_BASE, PORT, WORKERS, etc. See deploy/thermograph.env.example. EnvironmentFile=/etc/thermograph.env # Clear the shared metrics DB on each (re)start so the dashboard's "since start" tallies # reflect the running workers, matching how the old in-process counters behaved. ExecStartPre=/usr/bin/rm -f /opt/thermograph/data/metrics.db /opt/thermograph/data/metrics.db-wal /opt/thermograph/data/metrics.db-shm # Bind loopback only — Caddy terminates TLS and reverse-proxies to us. ExecStart=/opt/thermograph/.venv/bin/uvicorn app:app --host 127.0.0.1 --port ${PORT} --workers ${WORKERS} Restart=on-failure RestartSec=2 # Hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=read-only # The parquet cache must stay writable across deploys. ReadWritePaths=/opt/thermograph/data /opt/thermograph/logs [Install] WantedBy=multi-user.target