Skip to content

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

FixDevs · (Updated: )

Part of:  Python Errors

Quick Answer

How to fix the 'error: externally-managed-environment' and 'This environment is externally managed' error when running pip install on Python 3.11+ on Ubuntu, Debian, Fedora, and macOS.

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.

Version history: where this error came from and which distros enforce it

PEP 668 was authored in 2021 and accepted in 2022. The mechanism itself is simple: a plain text file named EXTERNALLY-MANAGED placed in a Python installation’s stdlib directory makes pip refuse to install or uninstall packages in that interpreter unless --break-system-packages is passed. The file can include a custom error message that the distro maintainers provide, which is why the wording differs between Ubuntu, Debian, Fedora, and Homebrew.

Adoption rolled out over the next two years and is what made this error appear “suddenly” for many developers:

  • Python 3.11 was the first interpreter version where PEP 668 was supported in pip itself (pip 23.0+).
  • Debian 12 (“bookworm”), released June 2023, was the first major Debian release to ship the marker file with system Python.
  • Ubuntu 23.04 (“Lunar Lobster”), April 2023, was the first Ubuntu to enforce PEP 668. Ubuntu 22.04 LTS did not, which is why developers upgrading from 22.04 to 24.04 LTS see the error appear out of nowhere.
  • Fedora 38, May 2023, added the marker file for Fedora’s system Python.
  • Arch Linux added the marker around the same time, though Arch’s rolling-release model means the exact date depends on when you ran pacman -Syu.
  • Homebrew on macOS added the marker in early 2023. Homebrew is not a distro, but its Python install is treated as “externally managed” because Homebrew owns the formulae and brewed packages depend on specific Python versions. The marker file in Homebrew also serves a second purpose: it nudges users toward pipx for CLI tools, which Homebrew maintainers explicitly recommend.

Older distros (Ubuntu 22.04 LTS, Debian 11, Fedora 37, RHEL/CentOS 9 with the default Python 3.9) do not enforce PEP 668. Code that worked on those systems may break the moment a CI runner image is upgraded to a newer base, even if your pip install command is unchanged. Pinning the runner image (e.g., ubuntu-22.04 instead of ubuntu-latest) buys time, but the long-term answer is still to use a virtual environment or pipx.

If you maintain a fleet of machines, you can detect which ones enforce PEP 668 by checking for the marker file: ls /usr/lib/python3.*/EXTERNALLY-MANAGED. The presence of that file means pip is locked. Conversely, the official python:3.x Docker images deliberately omit the file because those images exist to run pip — a useful detail when debugging “works in CI, fails locally” or vice versa.

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

Why this matters: Before PEP 668, a careless pip install --upgrade requests could silently overwrite the version of requests that your system’s apt or dnf depends on, breaking system tools like package managers and cloud-init. PEP 668 prevents this by requiring you to use virtual environments, which is better practice anyway.

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

Real-world scenario: Your Docker build uses FROM ubuntu:24.04 and runs pip install -r requirements.txt, which suddenly fails after upgrading the base image. The fix is either to switch to FROM python:3.12-slim (which does not have PEP 668 restrictions) or to create a venv inside the container.

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.

Homebrew Python with --break-system-packages ignored

You may see --break-system-packages accepted by Homebrew’s pip yet pip still refuses to install. The cause is usually that you have multiple Python installs on PATH and the actual pip resolving the command is not the Homebrew one. Run which -a python3 pip3 and inspect each path. A common culprit is an old /Library/Frameworks/Python.framework/Versions/3.x/bin/pip3 installed by the python.org installer years ago. Remove it or reorder PATH so the Homebrew version wins. If you also see “no module named” errors after switching interpreters, see Fix: ModuleNotFoundError after activating venv.

pip install inside a venv still errors out

If you activated .venv but still see “externally-managed-environment,” the venv was not created against the system Python you expect. The venv inherits the marker file when it copies the standard library symlinks if you used --copies against a marked Python. Recreate it:

rm -rf .venv
python3 -m venv .venv
ls .venv/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null && echo "Marker copied — remove and recreate"

A correctly-created venv does not contain EXTERNALLY-MANAGED. If the marker is present inside .venv, your python3 -m venv is reading from a system path the venv module did not strip. Pin a specific Python: python3.12 -m venv .venv. If pip itself is missing inside the venv, see Fix: pip-no-such-option-break-system-packages for related diagnosis steps.

Anaconda / Miniconda environments mis-detected

If you have both Conda and a system Python, calls to pip may resolve to whichever interpreter is first on PATH. Inside an active Conda env, prefer python -m pip install ... over bare pip install ... so you always install into the active interpreter rather than a system pip that happens to win the PATH race. For broader Conda issues, see Fix: conda command not working.


Related: Fix: ModuleNotFoundError: No module named

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles