Python, venv and services
Python 3 with a virtual environment and pip, and a bot or script as a systemd service.
4 min read
On this page
Applies to Debian 12/13, Ubuntu 22.04/24.04, AlmaLinux 9 and Rocky Linux 9. In the end your Python program runs in its own environment as a service, with logs in the journal.
1. Install Python
Python 3 is already on every system. Usually only venv and pip are missing.
Debian and Ubuntu:
apt install -y python3 python3-venv python3-pipAlmaLinux and Rocky Linux:
dnf install -y python3 python3-pipWhich version you have:
| System | python3 |
|---|---|
| Debian 12 | 3.11 |
| Debian 13 | 3.13 |
| Ubuntu 22.04 | 3.10 |
| Ubuntu 24.04 | 3.12 |
| AlmaLinux/Rocky 9 | 3.9 |
If your program needs a newer version on AlmaLinux or Rocky, install it alongside. python3 stays 3.9 because the system relies on it:
dnf install -y python3.12 python3.12-pipThen use python3.12 instead of python3 everywhere below.
python3 --version2. User and folder
useradd --system --create-home --home-dir /opt/pybot --shell /usr/sbin/nologin pybotPut your code in /opt/pybot/app, via Git or SFTP, and give it to the user:
chown -R pybot:pybot /opt/pybot3. Virtual environment and pip
A virtual environment (venv) is a folder with its own Python and packages just for this program. Programs don't get in each other's way, and you don't need root for pip.
sudo -u pybot python3 -m venv /opt/pybot/venv
sudo -u pybot /opt/pybot/venv/bin/pip install --upgrade pip
sudo -u pybot /opt/pybot/venv/bin/pip install -r /opt/pybot/app/requirements.txtWithout a requirements.txt, install packages one by one, e.g. …/pip install discord.py.
To work by hand, activate the environment:
source /opt/pybot/venv/bin/activate
python --version
deactivate4. Secrets in an env file
Tokens and passwords don't belong in your code:
install -d -m 0750 /etc/pybot
nano /etc/pybot/pybot.envDISCORD_TOKEN=your-tokenchmod 600 /etc/pybot/pybot.envIn your code:
import os
token = os.environ["DISCORD_TOKEN"]5. systemd service
nano /etc/systemd/system/pybot.service[Unit]
Description=Python bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=pybot
WorkingDirectory=/opt/pybot/app
EnvironmentFile=/etc/pybot/pybot.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/pybot/venv/bin/python main.py
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetReplace main.py with your entry file. PYTHONUNBUFFERED=1 makes print() output reach the journal right away instead of later.
systemctl daemon-reload
systemctl enable --now pybotRun a script on a schedule
For a script that doesn't run permanently but, say, hourly, use a timer instead of Restart. In the service, set Type=oneshot and drop the Restart lines and the [Install] section, then:
nano /etc/systemd/system/pybot.timer[Unit]
Description=Python script hourly
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.targetsystemctl daemon-reload
systemctl enable --now pybot.timer
systemctl list-timers pybot.timerCheck that it runs
systemctl status pybot
journalctl -u pybot -factive (running) and your output in the journal. Ctrl+C only stops the display.
Updating
cd /opt/pybot/app
sudo -u pybot git pull
sudo -u pybot /opt/pybot/venv/bin/pip install -r requirements.txt
systemctl restart pybotAfter upgrading the distribution to a new Python version (e.g. Debian 12 to 13), recreate the venv: delete the folder and repeat step 3.
Common errors
| Message | Cause and fix |
|---|---|
error: externally-managed-environment | pip install outside a venv. The system protects its own packages. Use the venv from step 3. |
The virtual environment was not created successfully because ensurepip is not available | python3-venv is missing (Debian/Ubuntu): apt install python3-venv. |
ModuleNotFoundError in the service, works by hand | The service uses a different Python. ExecStart must point to /opt/pybot/venv/bin/python. |
status=203/EXEC | Wrong path in ExecStart or the venv is missing. |
KeyError: 'DISCORD_TOKEN' | The variable is missing from the env file or EnvironmentFile is misspelt. |
| No output in the journal | PYTHONUNBUFFERED=1 is missing. |
What next
- Secure your server.
- Several services with a database: Docker.
- Help: ticket.