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.

Why pip refusespip install -rinto system PythonEXTERNALLY-MANAGEDmarker file presentpip stopsprotects apt packagesUse a venvisolated packagesPEP 668 blocks pip from modifying the OS Python; a virtualenv is the intended path.
  1. Install venv support: sudo apt install python3-venv (or python3.12-venv).
  2. Create a virtualenv for the project: python3 -m venv /srv/app/.venv.
  3. Install into it: /srv/app/.venv/bin/pip install -r requirements.txt.
  4. Point Gunicorn, Celery, and cron at /srv/app/.venv/bin/....
  5. Or use uv to create the venv and install much faster: uv venv && uv pip install -r requirements.txt.
  6. Do not use --break-system-packages or sudo pip on 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).

installing Python packages on Ubuntu 24.04UseVerdictDjango app depsvenv or uv✓standardCLI tools (certbot, ansible)pipx or apt✓isolated per toolOS-level librariesapt python3-xyz✓for system usesudo pip / break flag--break-system-packages✕can break the OSApplication dependencies always go in a project virtualenvdjango-deployment.com
Each kind of Python software has a home that does not touch the system Python.

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.

from fresh server to working venv1apt install python3-venvplus python3-dev, build tools2python3 -m venv /srv/app/.venvowned by the deploy user3.venv/bin/pip install -ror uv pip install4Services use .venv/bin pathsgunicorn, celery, timers5python -m django --versionconfirms the installAbsolute venv paths in services remove any need for activationdjango-deployment.com
This is the same layout whether the server runs Ubuntu, Debian, or Fedora.

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 --user is also blocked by the marker; use pipx or a venv instead.
  • System packages inside a venv. python3 -m venv --system-site-packages lets 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 pip module needs virtualenv: set or it hits the same error.

Next steps

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.

Keep going