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

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:

  1. Is it running outside a virtual environment (sys.prefix == sys.base_prefix)?

  2. Is there a file named EXTERNALLY-MANAGED in the directory returned by sysconfig.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".

The alternatives

Choice When How

A virtual environment per project

Developing or running a project.

python -m venv .venv, then use .venv/bin/python -m pip. See the introduction.

pip --python <env>

Managing an environment that has no pip of its own, from one pip installed elsewhere (pip 22.3).

python -m pip --python .venv install …

A Poetry-managed environment

A Poetry project: Poetry creates and selects the venv for you.

poetry install; settings virtualenvs.create, virtualenvs.in-project, virtualenvs.path.

pipx (third party)

Installing a command-line application such as Poetry itself, twine or pip-audit, each in its own venv.

pipx install poetry. See the ecosystem page.

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.

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/venv and put /opt/venv/bin first on PATH), 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-slim images, which build CPython from source under /usr/local and 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 python -m pip, or --python <env>

Automatic per project; poetry env use <python>

Environment in the project

python -m venv .venv

virtualenvs.in-project = true

Refuse to run outside a venv

--require-virtualenv / PIP_REQUIRE_VIRTUALENV=1

Default (unless virtualenvs.create = false)

Override PEP 668

--break-system-packages

Not applicable: Poetry installs into its own venvs

Install an application globally

pipx

poetry-plugin-bundle or pipx

Common mistakes

  • sudo pip install. Writes into directories owned by the operating system. Use a venv, or pipx for tools.

  • pip install --user to dodge the error. pip applies the check to --user installs too (the same error: externally-managed-environment appears), and user installs would shadow system packages for every program that user runs. Only --target and --prefix installs into a separate directory are exempt.

  • Calling the bare pip command. It may belong to a different Python than the python you think; use python -m pip.

  • Removing EXTERNALLY-MANAGED from an image. Use a venv or a non-distribution Python instead.