Add deploy/ — nginx vhost, systemd unit, runbook

Single-host deployment of the app at rfc.wiggleverse.org alongside
the existing Gitea instance. nginx reverse-proxies /api/* and
/auth/* to a single uvicorn process on 127.0.0.1:8000 and serves
the Vite build output as static files; certbot adds the TLS cert
in place; systemd supervises the process per §4.2's
single-process-with-WAL-SQLite contract (one worker; raising
--workers would break the invariant).

deploy/DEPLOY.md is the step-by-step runbook covering host prep,
Gitea bot + OAuth setup, .env shape, meta-repo seed, nginx +
certbot, systemd, smoke test, and the update/rollback shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-24 05:18:28 -07:00
parent c82328e9ad
commit 33d9d7a482
4 changed files with 400 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
# systemd unit for the FastAPI process.
#
# Install:
# sudo cp deploy/systemd/rfc-app.service /etc/systemd/system/
# sudo systemctl daemon-reload
# sudo systemctl enable --now rfc-app
# sudo systemctl status rfc-app
#
# Logs:
# sudo journalctl -u rfc-app -f
#
# Per §4.2 the app is intentionally single-process — one uvicorn
# worker, colocated SQLite. If the deployment ever needs more than one
# worker, the spec calls for a planned migration to Postgres first;
# raising `--workers` here would break the WAL-mode SQLite invariant.
[Unit]
Description=Wiggleverse RFC app
After=network.target
Documentation=https://git.wiggleverse.org/ben.stull/rfc-app
[Service]
Type=simple
User=rfc-app
Group=rfc-app
WorkingDirectory=/opt/rfc-app/backend
EnvironmentFile=/opt/rfc-app/backend/.env
ExecStart=/opt/rfc-app/backend/.venv/bin/uvicorn app.main:app \
--host 127.0.0.1 \
--port 8000 \
--proxy-headers \
--forwarded-allow-ips 127.0.0.1
Restart=on-failure
RestartSec=5s
# Hardening — modest defaults; tighten further if the host runs other
# services. The bot wrapper writes only to its own data dir; everything
# else is read-only.
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/rfc-app/backend/data
[Install]
WantedBy=multi-user.target