[Unit] Description=TrumpSignal API + Truth Social poller After=network-online.target postgresql.service Wants=network-online.target [Service] Type=simple User=trumpsignal Group=trumpsignal WorkingDirectory=/opt/trumpsignal/backend EnvironmentFile=/opt/trumpsignal/backend/.env # MUST stay --workers 1. The backend is single-process BY DESIGN: APScheduler, # scanner_state, the signed-request replay cache, tp_sl_monitor's open-trade # table and price_store are all in-memory and NOT shared across processes. # With >1 worker the scheduler runs in every worker (duplicate scans / ingest / # trades), the scanner toggle split-brains, and TP/SL only sees same-worker # trades. A runtime singleton lock in app.main now refuses to start background # tasks in extra workers as a backstop, but the correct config is one worker. # To scale horizontally, move scheduler leadership + scanner state + replay # cache to Redis/DB FIRST. (Mirrors deploy/supervisor.conf.) ExecStart=/opt/trumpsignal/backend/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 1 # Auto-restart on any exit (crash, OOM, segfault, manual kill). # RestartSec=10 = wait 10s between restart attempts so we don't hammer a broken state. Restart=always RestartSec=10 # Burst protection: if we crash > 5 times in 60s, give up (likely a real config bug, # not transient). Reset by `systemctl reset-failed trumpsignal-api`. StartLimitIntervalSec=60 StartLimitBurst=5 # Hardening NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/opt/trumpsignal/backend PrivateTmp=yes # Logging — journald captures stdout/stderr; view with `journalctl -u trumpsignal-api -f` StandardOutput=journal StandardError=journal SyslogIdentifier=trumpsignal-api [Install] WantedBy=multi-user.target