Ubuntu 24.04 ships Python 3.12, but installing packages with plain pip now fails with an "externally-managed-environment" error. The fix is to isolate each project in a virtual environment. This guide shows how to prepare your VPS Linux Infrawire, manage dependencies properly and keep an application running as a systemd service.
📋 Prerequisites
- A VPS running Ubuntu 24.04 or Debian 12 with root SSH access (IP and root password are in your customer area)
- A Python project or a simple script to test with
- Basic command-line knowledge
1. Install Python 3, pip and venv
Python 3 is usually preinstalled, but pip and the venv module are separate packages.
Bashapt update apt install -y python3 python3-pip python3-venv python3-dev build-essential python3 --version pip3 --version
2. Understand the PEP 668 error
Running pip install requests as root on Ubuntu 24.04 or Debian 12 prints:
TEXTerror: externally-managed-environment
This protection (PEP 668) stops pip from overwriting files owned by apt and breaking system tools. Do not bypass it with --break-system-packages. Use one of the three clean options below.
3. Create a virtual environment
A virtual environment is a private copy of Python and pip for one project.
Bashmkdir -p /opt/myapp && cd /opt/myapp python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install requests flask gunicorn
The prompt now starts with (venv). Leave the environment with deactivate.
4. Manage dependencies with requirements.txt
Freeze the exact versions so the project can be rebuilt identically on another server.
Bashpip freeze > requirements.txt # On a fresh machine python3 -m venv venv source venv/bin/activate pip install -r requirements.txt
5. Install command-line tools with pipx
For standalone tools (httpie, yt-dlp, black...), pipx gives each one its own hidden virtual environment and exposes the command globally.
Bashapt install -y pipx pipx ensurepath pipx install httpie
Open a new shell afterwards so the updated PATH is loaded. For a library that has a Debian package, apt install python3-requests is also a valid choice.
6. Run the application as a systemd service
Create a dedicated unprivileged user, then a unit that starts the interpreter from the virtual environment (here Gunicorn serving a Flask object named app in app.py).
Bashuseradd --system --home /opt/myapp --shell /usr/sbin/nologin myapp chown -R myapp:myapp /opt/myapp
Create /etc/systemd/system/myapp.service:
INI1[Unit] 2Description=My Python application 3After=network.target 4 5[Service] 6User=myapp 7WorkingDirectory=/opt/myapp 8ExecStart=/opt/myapp/venv/bin/gunicorn --bind 127.0.0.1:8000 app:app 9Restart=on-failure 10RestartSec=5 11 12[Install] 13WantedBy=multi-user.target
Bashsystemctl daemon-reload systemctl enable --now myapp
Put a reverse proxy such as nginx in front of port 8000 to expose it on the Internet.
✅ Verify
Bashwhich python # must point inside /opt/myapp/venv when activated pip list systemctl status myapp --no-pager journalctl -u myapp -n 50 --no-pager curl -I http://127.0.0.1:8000
🔧 Troubleshooting
The virtual environment was not created successfully because ensurepip is not available : the venv package is missing. Install it with apt install python3-venv (or python3.12-venv) and run the command again.
status=203/EXEC in systemd : the path in ExecStart is wrong or the file is not executable by the service user. Check it with ls -l /opt/myapp/venv/bin/gunicorn and the ownership set earlier.
ModuleNotFoundError only under systemd : the service uses the system Python instead of the virtual environment. Always call /opt/myapp/venv/bin/python or the binary inside venv/bin in ExecStart.
pip: command not found : use python3 -m pip or activate the virtual environment first.
🎉 Your Python projects are now isolated, reproducible and running as a reliable service!