Why pip suddenly refuses
On Debian 12, on Ubuntu 24.04 and later, and in Homebrew, the system Python carries a small file called EXTERNALLY-MANAGED. It means "apt (or brew) owns the libraries in here". If pip also changed them, the two package managers could quietly break each other, and some of the tools that break are ones the operating system needs. So pip stops, and prints the distribution's advice:
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.
The rule comes from a Python standard, PEP 668. pip refuses only when both are true: you are not in a virtual environment, and that marker file exists. That second condition is why the same command works in some places and not others:
1. You run pip install. Into whichever Python that pip belongs to.
The three fixes that hold up
- A project: give it a virtual environment.
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python app.py
On Debian and Ubuntu you may need sudo apt install python3-venv first.
- A command-line tool you want everywhere (ansible, pre-commit and so on):
pipx install NAME. Each tool gets its own environment, so tools cannot break each other either. - A library the OS already packages:
sudo apt install python3-NAME.
In a Dockerfile, make the venv part of the image so every later pip uses it:
RUN python3 -m venv /opt/venv
ENV PATH=/opt/venv/bin:$PATH
RUN pip install -r requirements.txt
Avoid sudo pip install entirely. And note that pip install --user is refused as well; the guard covers it.
When the override is acceptable
--break-system-packages is reasonable in a throwaway image where nothing else uses the system Python. On a server or a laptop it is how you end up with an apt-installed tool that stops working after an upgrade.
The full diagnosis, including a one-line check for which Python you are actually using, is on the externally-managed-environment page. If the failure only happens in CI, passes locally, fails in CI covers the other usual suspects.
Enjoyed this article?
Get more DevOps insights delivered to your inbox.
Get new posts by email
Subscribe to get an email when a new blog post is published. Skip anytime.
No spam, unsubscribe anytime.
Related Posts
Discussion
0 comments
Sign in to join the conversation.
Be the first to comment
Start a conversation about this post



