Fix: error: externally-managed-environment (pip install)

The Error

You run pip install and get this:

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.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using, e.g.:

    $ python3 -m venv path/to/venv

    Then install packages within the virtual environment:

    $ path/to/venv/bin/pip install xyz

note: If you believe this is a mistake, please contact your
Python installation provider.

The exact wording varies by distro. On Fedora, it says dnf install instead of apt install. On macOS with Homebrew, the message references brew install.

Why This Happens

Your Linux distribution (or Homebrew on macOS) is protecting its system Python from pip.

This is caused by PEP 668, which was adopted in Python 3.11. Distributions that have implemented it include Ubuntu 23.04+, Debian 12+, Fedora 38+, Arch Linux, and macOS Homebrew.

The reason: Linux distributions ship dozens of system tools written in Python (apt, dnf, cloud-init, firewalld, and others). These tools depend on specific Python packages installed at specific versions through the system package manager. When you run pip install system-wide, pip can overwrite or remove those packages, breaking system tools in ways that are hard to diagnose.

Before PEP 668, a careless pip install --upgrade requests could break apt or dnf. Fixing it often meant reinstalling the OS. PEP 668 prevents this by marking the system Python as “externally managed.” When pip sees the marker file (EXTERNALLY-MANAGED in the Python installation directory, e.g., /usr/lib/python3.12/), it refuses to install packages.

This is not a bug. It is working as intended. The fix is to stop installing packages into the system Python.

This is the correct fix for almost every case. A virtual environment gives you an isolated Python where you can install whatever you want without affecting the system.

Create and activate a virtual environment:

python3 -m venv .venv
source .venv/bin/activate

Now pip works normally:

pip install requests flask pandas

Your shell prompt will show (.venv) when the environment is active. All packages you install go into .venv/lib/ and won’t touch the system Python.

When you’re done working, deactivate:

deactivate

Make it a habit

Create a venv for every project. This is standard practice in Python development and solves far more problems than just this error.

mkdir my-project && cd my-project
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

If python3 -m venv fails

On Debian/Ubuntu, the venv module may not be installed by default:

Error: Command '[...] ensurepip' returned non-zero exit status 1.

Install it:

sudo apt install python3-venv

On older Debian systems, you may also need:

sudo apt install python3-pip python3-venv

Fix 2: Use pipx for CLI Tools

If you’re installing a command-line tool like black, ruff, httpie, poetry, or ansible, use pipx. It installs each tool into its own isolated virtual environment automatically, so the tool is available globally without touching the system Python.

Install pipx:

# Ubuntu/Debian
sudo apt install pipx
pipx ensurepath

# Fedora
sudo dnf install pipx
pipx ensurepath

# macOS
brew install pipx
pipx ensurepath

Restart your shell, then install tools with pipx instead of pip:

pipx install black
pipx install ruff
pipx install httpie
pipx install poetry
pipx install ansible

Now you can run black, ruff, etc. from anywhere. Each tool lives in its own venv under ~/.local/share/pipx/venvs/.

When to use pipx vs venv: Use pipx for tools you run from the command line. Use a venv for project dependencies (libraries you import in your code).

You can force pip to install into the system Python by passing --break-system-packages:

pip install --break-system-packages requests

Or set it permanently in your pip config:

mkdir -p ~/.config/pip
cat > ~/.config/pip/pip.conf << 'EOF'
[global]
break-system-packages = true
EOF

Why you shouldn’t do this: This flag exists for the exact reason the name implies. You are telling pip to break system packages. If you install a package that conflicts with a system dependency, you can break apt, dnf, or other system tools. Fixing this can require reinstalling Python system packages or, in severe cases, the entire OS.

The only time this is acceptable is in throwaway environments (Docker containers, CI runners, disposable VMs) where you control the entire system and don’t care if system tools break.

Fix 4: Use Your Distro’s Package Manager

If you need a common Python library for a system-wide script, install it through your distribution’s package manager:

# Ubuntu/Debian
sudo apt install python3-requests
sudo apt install python3-flask
sudo apt install python3-numpy

# Fedora
sudo dnf install python3-requests
sudo dnf install python3-flask
sudo dnf install python3-numpy

# Arch
sudo pacman -S python-requests
sudo pacman -S python-flask
sudo pacman -S python-numpy

The naming convention is python3-<package> on Debian/Ubuntu and Fedora, and python-<package> on Arch.

Limitations: Distro repositories carry a limited selection of packages, and the versions are often older than what’s on PyPI. If you need a specific version or a niche package, this won’t work. Use a venv instead.

To search for available Python packages:

# Ubuntu/Debian
apt search python3- | grep <package>

# Fedora
dnf search python3- | grep <package>

Fix 5: Use pyenv or Conda for a Separate Python

If you need a Python installation that’s entirely yours—not managed by the OS—install one with pyenv or Conda. These install Python to your home directory, outside the system’s control, so pip works without restrictions.

pyenv

# Install pyenv
curl https://pyenv.run | bash

# Add to ~/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

# Restart shell, then install Python
pyenv install 3.12
pyenv global 3.12

# pip works without restrictions
pip install requests

Conda (Miniconda)

# Install Miniconda
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh

# Create an environment
conda create -n myenv python=3.12
conda activate myenv

# pip works
pip install requests

When to use this: When you need a different Python version than what your distro ships, or when you work on multiple projects requiring different Python versions. pyenv is lighter weight. Conda is better if you also need non-Python dependencies (C libraries, CUDA, etc.).

Still Not Working?

Virtual environment inside Docker

If you’re building a Docker image based on Debian 12+ or Ubuntu 23.04+ and hitting this error in your Dockerfile, you have two options:

Option A: Use —break-system-packages (fine in Docker):

FROM python:3.12-slim

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

Wait—the official python:3.12-slim image does not trigger this error, because it’s built specifically for running pip. You only hit this when using a general-purpose base image:

# This WILL trigger the error:
FROM ubuntu:24.04
RUN apt update && apt install -y python3-pip
RUN pip install requests  # error: externally-managed-environment

# Fix: use --break-system-packages (acceptable in Docker)
FROM ubuntu:24.04
RUN apt update && apt install -y python3-pip
RUN pip install --break-system-packages --no-cache-dir requests

# Better fix: use a venv even in Docker
FROM ubuntu:24.04
RUN apt update && apt install -y python3-pip python3-venv
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir requests

Option B: Use the official Python image. Switch your base image to python:3.12-slim or python:3.12 and avoid the problem entirely.

Homebrew Python on macOS

Homebrew’s Python also implements PEP 668. You’ll see the same error:

error: externally-managed-environment

The fixes are identical: use a venv, pipx, or pyenv. Unlike Linux distros, Homebrew does not package individual Python libraries, so brew install python-requests won’t work. A venv is the most practical fix on macOS.

WSL2 on Windows

WSL2 runs a full Linux distribution, so you hit this error the same way you would on a native Linux install. The Ubuntu image in WSL2 is Ubuntu 24.04 by default, which enforces PEP 668.

All the fixes above work in WSL2. Use a venv:

python3 -m venv .venv
source .venv/bin/activate
pip install requests

If python3-venv is missing:

sudo apt update && sudo apt install python3-venv

CI/CD pipelines (GitHub Actions, GitLab CI)

If your CI pipeline uses a system Python on a modern runner image, you may hit this error. Fixes:

GitHub Actions: Use the setup-python action, which installs a standalone Python that doesn’t have PEP 668 restrictions:

- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
- run: pip install -r requirements.txt  # works fine

GitLab CI: Use a Python Docker image:

image: python:3.12-slim
script:
  - pip install -r requirements.txt

Generic CI: If you must use the system Python, either create a venv or use --break-system-packages:

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Removing the EXTERNALLY-MANAGED file (don’t)

You may see advice to delete the marker file:

sudo rm /usr/lib/python3.*/EXTERNALLY-MANAGED

This removes the protection and makes pip work system-wide again. Don’t do this. It defeats the purpose of PEP 668 and puts your system packages at risk. If a future system update restores the file, you’ll be back where you started. Use a venv.


Related: Fix: ModuleNotFoundError: No module named

Related Articles