Files
PassKeeper/scripts/passkeeper.service
T
nngo 0304095e53
CI / Python lint (flake8) (push) Has been cancelled
CI / Python syntax check (push) Has been cancelled
CI / Alembic migration chain (push) Has been cancelled
CI / JavaScript syntax check (push) Has been cancelled
CI / Build extension zip (push) Has been cancelled
Aug 26 - Enhance security
2026-08-26 10:37:00 -04:00

75 lines
2.5 KiB
Desktop File

# /etc/systemd/system/passkeeper.service
#
# Install / update:
# sudo cp scripts/passkeeper.service /etc/systemd/system/passkeeper.service
# sudo systemctl daemon-reload
# sudo systemctl enable passkeeper
# sudo systemctl restart passkeeper
# journalctl -xeu passkeeper.service
#
# Phase 5 additions vs original:
# - Restart=on-failure: systemd restarts Gunicorn if the master exits non-zero
# - Gunicorn --timeout: workers that don't respond within 25 s are replaced
# - Gunicorn --graceful-timeout: allows in-flight requests to finish on reload
# - PrivateTmp, NoNewPrivileges, ProtectSystem: basic systemd sandboxing
# - StartLimitIntervalSec / StartLimitBurst: caps restart storm
[Unit]
Description=PassKeeper Gunicorn daemon
After=network.target mysql.service
Wants=mysql.service
# Restart policy: cap to 5 restarts in 60 s to prevent restart storms
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
User=www-data
Group=www-data
WorkingDirectory=/home/spuser/PassKeeper
EnvironmentFile=/home/spuser/PassKeeper/.env
ExecStart=/home/spuser/.venv/bin/gunicorn \
--workers 4 \
--bind 127.0.0.1:5000 \
--timeout 25 \
--graceful-timeout 20 \
--keep-alive 5 \
--access-logfile /home/spuser/logs/access.log \
--error-logfile /home/spuser/logs/error.log \
--log-level warning \
wsgi:app
# Reload (zero-downtime): send USR2 to Gunicorn master
ExecReload=/bin/kill -s USR2 $MAINPID
# NO WatchdogSec here — deliberately.
#
# WatchdogSec requires the service to send WATCHDOG=1 keepalives over the sd_notify
# socket. Gunicorn only does that when systemd exports NOTIFY_SOCKET, which happens
# only under Type=notify (+ NotifyAccess=main). This unit is Type=simple (the
# default), so no keepalive was ever sent, systemd treated the service as hung, and
# SIGKILLed it every ~30 s. Restart=on-failure then brought it back after RestartSec,
# producing a repeating window of 502s from Nginx.
#
# Hung *workers* are already handled by Gunicorn's own --timeout above; a crashed
# *master* is already handled by Restart=on-failure below. The watchdog added no
# coverage, only outages.
#
# To re-enable it properly (optional), all three lines are required:
# Type=notify
# NotifyAccess=main
# WatchdogSec=30s
Restart=on-failure
RestartSec=5s
# Systemd sandboxing
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/home/spuser/PassKeeper /home/spuser/logs /home/spuser/backups
[Install]
WantedBy=multi-user.target