Environments & Externally Managed Pythons
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the Python Reference bibliography. |
This page explains where pip and Poetry put the packages they install, and why the Python that came with your operating system refuses them. It was written against pip 26.2.2 and Poetry 2.5.1 and run on 2026-10-22 with a CPython 3.14.6 installed by uv, which, like Debian, Ubuntu, Fedora and Homebrew Pythons, ships the PEP 668 marker file. How to create and activate a virtual environment is covered in the introduction to virtual environments; this page is about the rules that make you need one.
Why installs go into an environment
A Python environment is an interpreter plus the directories it imports third-party packages from (its site-packages). Every
installer writes into exactly one environment. If that environment is the interpreter your operating system uses for its own
tools, two package managers now own the same directory: apt or brew installed requests 2.31 for a system tool, and
pip install -U requests replaces it with a version the system tool was never tested with. Neither manager knows what the other
did, and uninstalling with one can delete files the other still lists.
A virtual environment (PEP 405, created by python -m venv or virtualenv) avoids this by giving each project its own
site-packages on top of a shared base interpreter. Inside a venv sys.prefix points at the venv while sys.base_prefix still
points at the base installation, which is how tools tell the two apart:
python3.14 -m venv .venv --without-pip
.venv/bin/python -c "import sys; print(sys.prefix != sys.base_prefix)"
True
Source: PyPA: Python virtual environments.
PEP 668 and the EXTERNALLY-MANAGED marker
PEP 668 (Final, 2021) gives distributors a way to say "do not install into this interpreter". Before installing, an installer checks two things:
-
Is it running outside a virtual environment (
sys.prefix == sys.base_prefix)? -
Is there a file named
EXTERNALLY-MANAGEDin the directory returned bysysconfig.get_path("stdlib", sysconfig.get_default_scheme())?
If both are true it stops with an error. The file is INI, read with configparser. Its [externally-managed] section may carry
an Error key with the message to print, and localised variants such as Error-de or Error-pt_BR chosen from the user’s
locale. The uv-managed CPython used for this page ships this marker:
cat "$(python3.14 -c 'import sysconfig; print(sysconfig.get_path("stdlib"))')/EXTERNALLY-MANAGED"
[externally-managed]
Error=This Python installation is managed by uv and should not be modified.
Debian’s version (from /usr/lib/python3.12/EXTERNALLY-MANAGED on the same machine) is longer and tells the user what to do
instead:
[externally-managed]
Error=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 python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.
See /usr/share/doc/python3.12/README.venv for more information.
Homebrew’s Python 3.14 and the Fedora and Ubuntu Pythons follow the same pattern with their own wording. pip has honoured the marker since 23.0, and since 25.0 it also skips its "a new version of pip is available" self-check in an externally managed environment, where upgrading pip is the distributor’s job.
The error
Pointing pip 26.2.2 at that interpreter:
python -m pip --python python3.14 install rich
error: externally-managed-environment
× This environment is externally managed
╰─> This Python installation is managed by uv and should not be modified.
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.
hint: See PEP 668 for the detailed specification.
The exit code is 1. The text between ╰─> and note: is the Error value from the marker file.
--break-system-packages
--break-system-packages (environment variable PIP_BREAK_SYSTEM_PACKAGES=1, added in pip 23.0.1) disables the check:
python -m pip --python python3.14 install --dry-run --break-system-packages tomli-w
…
Would install tomli_w-1.2.0
Use it only where nothing else uses that interpreter, for example a throw-away CI container whose base image has a marker. On a
workstation it does exactly what the name says: the next apt upgrade or brew upgrade may overwrite what pip installed, or a
system tool may break because pip upgraded one of its dependencies. sudo pip install is the same mistake with worse
consequences, because it also writes as root into directories the package manager owns. Do not put break-system-packages =
true into a global pip.conf "to make the error go away".
Source: pip install: --break-system-packages.
The alternatives
| Choice | When | How |
|---|---|---|
A virtual environment per project |
Developing or running a project. |
|
|
Managing an environment that has no pip of its own, from one pip installed elsewhere (pip 22.3). |
|
A Poetry-managed environment |
A Poetry project: Poetry creates and selects the venv for you. |
|
pipx (third party) |
Installing a command-line application such as Poetry itself, twine or pip-audit, each in its own venv. |
|
A container |
Deploying an application. |
A venv inside the image, or an image whose Python has no marker. Distributors are asked to keep the marker in container images. |
pip --python: one pip, many environments
--python (PIP_PYTHON) takes a path to an interpreter or to a virtual environment and makes pip run "exactly as if it had been
invoked from that Python environment". A venv created with --without-pip has no pip at all, and the pip 26.2.2 from the
scratchpad tool environment still manages it:
python3.14 -m venv .venv --without-pip
python -m pip --python .venv install tomli-w
python -m pip --python .venv list
Collecting tomli-w
Using cached tomli_w-1.2.0-py3-none-any.whl.metadata (5.7 kB)
Using cached tomli_w-1.2.0-py3-none-any.whl (6.7 kB)
Installing collected packages: tomli-w
Successfully installed tomli-w-1.2.0
Package Version
------- -------
tomli_w 1.2.0
A side effect of --python is that the PEP 668 check applies to the target, which is why the error above appeared even though
the pip running it lives in a venv.
Poetry’s environments
Poetry never installs into the interpreter that runs Poetry. For each project it creates a virtual environment, by default in its
cache directory (virtualenvs.path), or in .venv inside the project when virtualenvs.in-project is true (the setting used for
every Poetry example in this section, through POETRY_VIRTUALENVS_IN_PROJECT=true). If an environment is already active, Poetry
uses it instead. poetry env info shows both sides:
poetry env info
Virtualenv
Python: 3.14.6
Implementation: CPython
Path: …/work/poetry-flavour/birdtally/.venv
Executable: …/work/poetry-flavour/birdtally/.venv/bin/python
Valid: True
Base
Platform: linux
OS: posix
Python: 3.14.6
Path: /root/.local/share/uv/python/cpython-3.14.6-linux-x86_64-gnu
Executable: /root/.local/share/uv/python/cpython-3.14.6-linux-x86_64-gnu/bin/python3.14
The base interpreter is externally managed, the venv is not, so poetry install works. Setting virtualenvs.create = false makes
Poetry install into whatever Python it finds, which on a distribution Python hits the same PEP 668 error; it is meant for
containers where the image already provides an isolated interpreter. All the virtualenvs.* settings and the poetry env
commands are on Poetry: Managing Environments.
Source: Poetry: Managing environments.
Containers
The specification asks distributors to keep the marker file in container images, preferably so that apt-get dist-upgrade
inside the image does not remove it. Inside an image you therefore either:
-
create a venv in the image (
python -m venv /opt/venvand put/opt/venv/binfirst onPATH), which works with any base image, or -
use a base image whose Python is not managed by the distribution, such as the official
python:3.14-slimimages, which build CPython from source under/usr/localand have no marker.
Do not delete the marker in a RUN step; that hides the problem from pip but not from apt. The Docker patterns for pip and Poetry
are on CI Caching & Containers.
pip and Poetry side by side
| Task | pip | Poetry |
|---|---|---|
Choose the environment |
Run the venv’s own |
Automatic per project; |
Environment in the project |
|
|
Refuse to run outside a venv |
|
Default (unless |
Override PEP 668 |
|
Not applicable: Poetry installs into its own venvs |
Install an application globally |
pipx |
|
Common mistakes
-
sudo pip install. Writes into directories owned by the operating system. Use a venv, or pipx for tools. -
pip install --userto dodge the error. pip applies the check to--userinstalls too (the sameerror: externally-managed-environmentappears), and user installs would shadow system packages for every program that user runs. Only--targetand--prefixinstalls into a separate directory are exempt. -
Calling the bare
pipcommand. It may belong to a different Python than thepythonyou think; usepython -m pip. -
Removing
EXTERNALLY-MANAGEDfrom an image. Use a venv or a non-distribution Python instead.