Fix pip "externally-managed-environment" on a Django Server
Fix pip error externally-managed-environment on Ubuntu 24.04, Debian 12, and other PEP 668 systems when installing Django: create a virtualenv or use uv, use pipx for tools, and avoid --break-system-packages on servers.
Problem statement
On a fresh Ubuntu 24.04 or Debian 12 server, installing your project's requirements fails immediately:
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to install.
...
note: If you believe this is a mistake, please contact your Python installation or OS
distribution provider. You can override this, at the risk of breaking your Python
installation or OS, by passing --break-system-packages.
Modern distributions mark their system Python as managed by the package manager (PEP 668). pip refuses to install into it, because mixing pip packages with apt packages can break system tools that depend on specific versions. The fix is the practice you should follow anyway: install Django into a virtual environment.
- Install venv support:
sudo apt install python3-venv(orpython3.12-venv). - Create a virtualenv for the project:
python3 -m venv /srv/app/.venv. - Install into it:
/srv/app/.venv/bin/pip install -r requirements.txt. - Point Gunicorn, Celery, and cron at
/srv/app/.venv/bin/.... - Or use
uvto create the venv and install much faster:uv venv && uv pip install -r requirements.txt. - Do not use
--break-system-packagesorsudo pipon a server.
Step-by-step solution
1. Create a virtualenv
sudo apt update
sudo apt install -y python3-venv python3-dev build-essential
sudo mkdir -p /srv/app && sudo chown deploy:deploy /srv/app
cd /srv/app
python3 -m venv .venv
If python3 -m venv complains that ensurepip is not available, the python3-venv package (or the version-specific one, such as python3.12-venv) is missing.
2. Install requirements into it
/srv/app/.venv/bin/pip install --upgrade pip
/srv/app/.venv/bin/pip install -r requirements.txt
/srv/app/.venv/bin/python -m django --version
Activation (source .venv/bin/activate) is a convenience for interactive shells. Services do not need it; calling the venv's binaries by absolute path is enough.
3. Point every process at the venv
# /etc/systemd/system/gunicorn.service
[Service]
User=deploy
WorkingDirectory=/srv/app
EnvironmentFile=/etc/myapp/env
ExecStart=/srv/app/.venv/bin/gunicorn config.wsgi:application --bind unix:/run/gunicorn/gunicorn.sock
Do the same for Celery (/srv/app/.venv/bin/celery), management commands in timers or cron, and deploy scripts (/srv/app/.venv/bin/python manage.py migrate).
4. Or use uv
uv creates environments and installs packages from a lock file in seconds, and can also install the Python version itself:
curl -LsSf https://astral.sh/uv/install.sh | sh
cd /srv/app
uv venv --python 3.12 .venv
uv pip install -r requirements.txt # or: uv sync --frozen with pyproject + uv.lock
See Deploy Django With uv for a full workflow.
5. Install tools with pipx, not pip
Tools you run from the command line but do not import in Django, such as httpie, glances, or ansible, belong in their own isolated environments:
sudo apt install -y pipx
pipx install ansible-core
6. Why not --break-system-packages?
The flag works, and in a throwaway container it is mostly harmless. On a server it can upgrade a library that apt-installed tools depend on (for example cryptography used by Certbot or requests used by cloud agents), breaking them on the next run or the next apt upgrade. It also makes your app's dependencies depend on the OS release. A venv costs nothing and avoids both problems.
In Docker images based on python:3.x, the official images do not set the marker, so pip works without a venv; images based on debian or ubuntu with distro Python do set it, so create a venv in the image as well.
Verification check:
/srv/app/.venv/bin/python -c "import django, sys; print(django.get_version(), sys.prefix)"
systemctl status gunicorn --no-pager | head -5
sys.prefix should be the venv directory, and the service should be active.
Explanation
PEP 668 lets a distribution place an EXTERNALLY-MANAGED file in the standard library directory. pip 23 and newer check for it and refuse to install into that environment unless overridden. Debian 12, Ubuntu 23.04+, Fedora 38+, and others ship the marker because users installing packages with sudo pip regularly broke system utilities written in Python. Virtual environments are unaffected, since they have their own site-packages and no marker.
Edge cases / notes
pip install --useris also blocked by the marker; use pipx or a venv instead.- System packages inside a venv.
python3 -m venv --system-site-packageslets a venv see apt-installed packages, occasionally useful for large compiled libraries such as GDAL bindings; pin carefully. - Python version. The venv uses the Python it was created with; after an OS upgrade that changes the Python version, recreate it.
- Ansible
pipmodule needsvirtualenv:set or it hits the same error.
Next steps
- Recreate environments cleanly after OS upgrades with Fix a Broken Django Virtualenv After an OS Upgrade.
- Lock dependencies reproducibly with Pin Django Dependencies With pip-tools and Hashes.
FAQ
What does externally-managed-environment mean?
The system Python is managed by the OS package manager, and pip refuses to modify it. Use a virtual environment.
Is --break-system-packages safe?
Not on servers. It can break OS tools that rely on apt-installed Python packages.
Do I need to activate the venv for Gunicorn?
No. Use the absolute path to the venv's gunicorn binary in the service file.
Does this affect Docker images?
Official python: images do not set the marker. Images using distro Python do, so use a venv there too.