Separate application and administrator privileges
A long-running application should use a dedicated system account. Prepare the project directory, virtual environment and entry point. Run the program manually as its intended account, using the same interpreter that the service will use. Restarting repeatedly does not fix an import error.
Define the unit
This example assumes a project in /opt/my-bot and an existing account named botapp. Save it as /etc/systemd/system/my-bot.service, adjusting the paths.
[Unit]
Description=My Python bot
After=network.target
[Service]
Type=simple
User=botapp
WorkingDirectory=/opt/my-bot
ExecStart=/opt/my-bot/venv/bin/python -u main.py
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetnetwork.target does not guarantee Internet connectivity. The application must handle disconnections and retry with a delay. Python’s -u option makes output available promptly in the journal.
Start and verify
sudo systemctl daemon-reload
sudo systemctl enable --now my-bot.service
systemctl status my-bot.service
journalctl -u my-bot.service -n 50 --no-pagerAn “active” status indicates a running process, not that every feature works. Test the external connection and application response separately. Read the first relevant error before increasing restart limits when something fails.
Handle secrets and updates separately
Do not place tokens in ExecStart or in a repository. Choose a secret-delivery method compatible with your systemd version and application, and restrict configuration-file permissions. Test an update separately and retain the previous code and dependency set. A process supervisor does not replace database backups or application monitoring.
Sources and documentation
Source links are retained for checking dates, availability and original context.