Fix: error: externally-managed-environment (pip install)
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
pipxfor 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.
Fix 1: Use a Virtual Environment (Recommended)
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/activateNow pip works normally:
pip install requests flask pandasYour 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:
deactivateMake 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.txtIf 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-venvOn older Debian systems, you may also need:
sudo apt install python3-pip python3-venvWhy this matters: Before PEP 668, a careless
pip install --upgrade requestscould silently overwrite the version ofrequeststhat your system’saptordnfdepends 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 ensurepathRestart your shell, then install tools with pipx instead of pip:
pipx install black
pipx install ruff
pipx install httpie
pipx install poetry
pipx install ansibleNow 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).
Fix 3: Use the —break-system-packages Flag (Not Recommended)
You can force pip to install into the system Python by passing --break-system-packages:
pip install --break-system-packages requestsOr set it permanently in your pip config:
mkdir -p ~/.config/pip
cat > ~/.config/pip/pip.conf << 'EOF'
[global]
break-system-packages = true
EOFWhy 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-numpyThe 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 requestsConda (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 requestsWhen 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.txtWait—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 requestsOption 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-environmentThe 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 requestsIf python3-venv is missing:
sudo apt update && sudo apt install python3-venvCI/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 fineGitLab CI: Use a Python Docker image:
image: python:3.12-slim
script:
- pip install -r requirements.txtGeneric 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.txtReal-world scenario: Your Docker build uses
FROM ubuntu:24.04and runspip install -r requirements.txt, which suddenly fails after upgrading the base image. The fix is either to switch toFROM 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-MANAGEDThis 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.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: pip error: no such option: --break-system-packages
How to fix pip error no such option --break-system-packages caused by old pip versions, PEP 668 externally-managed environments, and virtual environment setup on modern Linux distributions.
Fix: ERROR: Could not build wheels / Failed building wheel (pip)
How to fix pip 'ERROR: Could not build wheels', 'Failed building wheel', 'No matching distribution found', and 'error: subprocess-exited-with-error'. Covers missing C compilers, build tools, system libraries, Python version issues, pre-built wheels, and platform-specific fixes for Linux, macOS, and Windows.
Fix: pip SSL: CERTIFICATE_VERIFY_FAILED certificate verify failed
How to fix the pip SSL CERTIFICATE_VERIFY_FAILED error when installing Python packages, covering corporate proxies, trusted-host flags, certifi, pip.conf, macOS, Windows, and Docker environments.
Fix: Python PermissionError: [Errno 13] Permission denied
How to fix Python PermissionError Errno 13 Permission denied when reading, writing, or executing files, covering file permissions, ownership, virtual environments, Windows locks, and SELinux.