OneLinersCommand workbench
Guides
Packages & Updates / Software Engineering

Fix error: externally-managed-environment on Ubuntu safely

The error: externally-managed-environment means pip has refused to modify an Ubuntu-managed Python installation. This guide shows how to identify the interpreter you are using, preserve operating-system package ownership, and choose the correct safe route: apt for Ubuntu packages, a project virtual environment for libraries, pipx for command-line applications, or an isolated container for disposable work.

25 min8 stepsChanges system stateRevision 1
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Finish with the requested Python package or application installed in an explicitly owned environment while /usr/bin/python3 and Ubuntu-managed packages remain untouched, then prove which interpreter, pip, and package location will be used.

Supported environments
  • Ubuntu 23.04 or newer, 24.04 LTS
  • Python 3.11 or newer
  • pip 23.0 or newer
Prerequisites
  • A normal user account Work from a non-root shell. Use sudo only for apt operations after reviewing the exact package name; project and pipx installations should remain owned by your user.id; test "$(id -u)" -ne 0
  • A clear installation goal Know whether the requested name is an importable project dependency, a standalone command-line application, or an Ubuntu package. The correct isolation boundary depends on that intent.printf 'Need a library, CLI app, or distro package?\n'
  • Current package metadata Refresh apt metadata before selecting an Ubuntu package, but do not remove the EXTERNALLY-MANAGED marker or replace the system interpreter.apt-cache policy python3 python3-full python3-venv pipx
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A documented package-ownership decision that maps an Ubuntu package to apt, a project dependency to .venv, a command-line application to pipx, or a disposable workload to a restricted container.
  • A working isolated Python environment with concrete version and location evidence, plus a rollback path that does not delete or bypass Ubuntu's EXTERNALLY-MANAGED marker.
Observable outcome
  • The requested package or command works from its intended runtime, and its package location identifies the selected owner.
  • The Ubuntu base interpreter remains managed by apt, the PEP 668 marker remains present, and future operators can reproduce or remove the isolated environment.

Architecture

How the parts fit together

Ubuntu owns the base Python interpreter and its distribution packages. Project virtual environments, pipx application environments, and container filesystems form separate ownership domains. The shell or service must invoke the executable from the domain selected for that workload.

Ubuntu package managerOwns /usr/bin/python3, distribution packages, security updates, dependency metadata, and removal records.
PEP 668 markerSignals that pip must not mutate the default interpreter environment without a deliberate unsafe override.
Project virtual environmentContains libraries and entry points for one application while remaining disposable and reproducible.
pipx application environmentContains one command-line application and exposes its reviewed commands on the user's PATH.
Isolated containerSeparates a disposable runtime from the host and makes image, mount, network, and lifecycle choices explicit.
  1. An operator captures interpreter ownership and the literal error: externally-managed-environment before making changes.
  2. The intended use selects one owner: apt, project venv, pipx, or container.
  3. The selected owner installs the package and records exact version and filesystem location.
  4. Verification checks the real application path and confirms Ubuntu's base interpreter and marker are unchanged.

Assumptions

  • The host runs Ubuntu 23.04 or newer with a distribution-managed Python interpreter and apt repositories configured by the organization.
  • The operator has normal user access and can request or use sudo for a reviewed apt transaction, but does not work from a root shell.
  • The package name, source, license, version requirements, and intended runtime have been reviewed before downloading third-party code.
  • Production services have a documented restart and rollback process; this guide does not silently replace their interpreter or dependency set.

Key concepts

Externally managed environment
A Python installation whose default package context is owned by another package manager. The marker asks tools such as pip to avoid modifying that context and direct the user to isolation.
Distribution package
A .deb package tracked by apt and dpkg, including its files, dependencies, configured repository, candidate version, upgrades, and removal state.
Virtual environment
A directory with an independent Python prefix and site-packages location. It can be removed and recreated without deleting packages from the base interpreter.
pipx application
A Python command-line application installed into its own virtual environment with selected entry points linked into a user command directory.
--break-system-packages
An explicit pip override that accepts the risk of modifying an externally managed interpreter. It is not the normal fix and is intentionally excluded from the remediation steps.
Set guide values0/8 ready

Fill these once. Every matching command and configuration block updates immediately; values stay in this page only.

Security and production boundaries

  • Do not remove, rename, or empty the EXTERNALLY-MANAGED marker. Doing so hides the ownership boundary from tools but does not transfer ownership away from apt.
  • Do not solve the refusal with sudo pip install. Root privileges increase the damage radius and can overwrite files or import precedence relied upon by Ubuntu utilities.
  • Treat PyPI packages and container images as software supply-chain inputs. Pin reviewed versions, verify provenance according to organizational policy, and keep credentials out of commands and logs.
  • A virtual environment isolates Python packages, not operating-system privileges, network access, or malicious code. Run untrusted packages only inside a stronger sandbox with bounded mounts and egress.

Stop before continuing if

  • Stop if python3 resolves to an interpreter managed by Conda, pyenv, an application bundle, or another owner not covered by the Ubuntu procedure.
  • Stop if apt simulation proposes removal of essential packages, a repository is unexpected, or the exact package name cannot be verified.
  • Stop if the only proposed workaround is sudo pip, deleting EXTERNALLY-MANAGED, or adding --break-system-packages without an approved exception and tested recovery plan.
  • Stop before changing a running service until its interpreter path, dependency lock, restart window, health check, and rollback owner are documented.
01

command

Capture the exact error and identify the managed interpreter

read-only

Do not retry with sudo or --break-system-packages. First record the executable paths, Python prefixes, pip location, active virtual-environment state, and the PEP 668 marker path. On Ubuntu, /usr/bin/python3 is part of the operating system and may be used by package-management and administrative tools.

Why this step matters

The refusal is a protection boundary, not evidence that pip itself is broken. Establishing interpreter ownership prevents an attempted fix from overwriting files tracked by apt or changing imports used by system utilities.

What to understand

Equal sys.prefix and sys.base_prefix mean the command is outside a virtual environment. A different prefix normally means pip can install into the active isolated environment.

The marker lives in the interpreter standard-library directory. Its presence tells Python package installers to avoid the default global installation context unless the operator deliberately overrides the policy.

Use python3 -m pip instead of a bare pip command so the displayed pip belongs to the same interpreter you are diagnosing.

System changes

  • None. The commands only read executable paths, version metadata, Python configuration, and marker existence.

Syntax explained

command -v python3
Shows which interpreter the shell resolves before any package operation.
sys.prefix != sys.base_prefix
Detects whether Python is currently running inside a virtual environment.
sysconfig.get_path('stdlib')
Finds the standard-library directory where the PEP 668 marker is defined.
Command
set -eu
printf 'python3: '; command -v python3
python3 --version
python3 -m pip --version
python3 - <<'PY'
import pathlib, sys, sysconfig
print(f'sys.prefix={sys.prefix}')
print(f'sys.base_prefix={sys.base_prefix}')
marker = pathlib.Path(sysconfig.get_path('stdlib')) / 'EXTERNALLY-MANAGED'
print(f'marker={marker}')
print(f'marker_exists={marker.exists()}')
print(f'in_virtualenv={sys.prefix != sys.base_prefix}')
PY
Example output / evidence
python3: /usr/bin/python3
Python 3.12.3
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
sys.prefix=/usr
sys.base_prefix=/usr
marker=/usr/lib/python3.12/EXTERNALLY-MANAGED
marker_exists=True
in_virtualenv=False

Checkpoint: Confirm the refusal comes from the Ubuntu base interpreter

python3 -c "import sys; print(sys.prefix, sys.base_prefix)"

Continue whenBoth prefixes are /usr, and the preceding evidence reports marker_exists=True.

Stop whenStop if python3 resolves into an application directory, Conda environment, pyenv installation, or another interpreter you do not intend to modify.

If this step fails

python3: No module named pip

Likely causeThe Ubuntu interpreter is present but the distro pip package is not installed.

Safe checks
  • apt-cache policy python3-pip
  • python3 --version

ResolutionInstall python3-pip through apt only if you actually need pip inside a virtual environment; python3-full is the simpler supported development baseline on Ubuntu.

Security notes

  • Never paste secrets, private index credentials, or complete environment variables into public diagnostics.

Alternatives

  • If the application ships its own interpreter, use its documented environment instead of Ubuntu system Python.

Stop conditions

  • Stop if the interpreter path or package owner differs from the Ubuntu environment described by this guide.
02

decision

Choose apt, venv, pipx, or a container before installing

Classify the requested package by how it will be used. Choose apt when Ubuntu provides a suitable system-integrated package. Choose venv for libraries imported by one project. Choose pipx for a Python command-line application that should be available on your user PATH. Choose a container when the workload is disposable, has conflicting native dependencies, or must be isolated from the host.

Why this step matters

PEP 668 separates ownership rather than banning Python packages. Selecting the owner before installation makes upgrades, removal, rollback, and incident investigation predictable.

What to understand

An import name, distribution name, and Ubuntu package name may differ. Confirm the upstream project name and inspect apt search results instead of guessing a python3-* package.

pipx is for applications that expose commands, not libraries imported by your project. Each pipx application gets a separate virtual environment.

A container is explicit operational overhead: pin the image, decide how files are mounted, and avoid assuming packages survive removal of an ephemeral container.

System changes

  • None. This decision records the intended owner and isolation boundary before any write occurs.

Syntax explained

apt
Ubuntu owns files, upgrades, dependencies, and removal.
venv
One project owns a directory containing its interpreter links and Python packages.
pipx
Your user owns an isolated environment for a command-line application.
container
A pinned image and container filesystem own the runtime environment.
Example output / evidence
Decision: project library -> create .venv under /srv/example-api; do not modify /usr/lib/python3/dist-packages.

Checkpoint: Write down the selected owner

Continue whenThe package has exactly one primary owner: apt, a named project venv, pipx, or a pinned container image.

Stop whenStop if the same package would be installed both through apt and pip into the same interpreter context.

Alternatives

  • Use an upstream self-contained binary only when its official installation and update process is documented and verified.

Stop conditions

  • Do not continue until you know whether the requested artifact is a library or an executable application.
03

command

Inspect the Ubuntu package route without changing the host

read-only

Search Ubuntu metadata and simulate the installation before using apt. This route is best for software that integrates with system services or when the Ubuntu version meets your needs. The dry run shows dependency and removal decisions without applying them.

Why this step matters

A simulation catches surprising dependency changes and verifies that the guessed Ubuntu package exists before privileged installation. It also preserves apt as the sole owner of system Python files.

What to understand

apt-cache search is broader than Python package indexes and may return unrelated names; read the description and policy output.

The Candidate line identifies the version apt would select from configured repositories. A value of (none) means this route is unavailable without changing repositories.

The --simulate operation calculates the transaction but does not download or install packages.

System changes

  • No packages are installed. sudo may refresh cached credentials, but apt-get runs in simulation mode.

Syntax explained

--names-only
Matches the search term against package names instead of every description field.
policy
Displays installed and candidate versions and their configured repository priorities.
--simulate
Prints the planned apt transaction without changing packages.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

apt-cache search --names-only '{{aptSearchTerm}}' | head -20
apt-cache policy '{{aptPackage}}'
sudo apt-get --simulate install '{{aptPackage}}'
Example output / evidence
python3-requests - elegant and simple HTTP library for Python3, built for human beings
python3-requests:
  Candidate: 2.31.0+dfsg-1ubuntu1
Inst python3-requests (2.31.0+dfsg-1ubuntu1 Ubuntu:24.04/noble-updates [all])
Conf python3-requests (2.31.0+dfsg-1ubuntu1 Ubuntu:24.04/noble-updates [all])

If this step fails

E: Unable to locate package

Likely causeThe name does not exist in configured Ubuntu repositories or apt metadata is stale.

Safe checks
  • apt-cache search --names-only '{{aptSearchTerm}}'
  • grep -Rh '^deb ' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null

ResolutionCorrect the name or choose venv or pipx. Do not add an untrusted repository merely to bypass the error.

Alternatives

  • If Ubuntu ships an older version than the project requires, prefer a project venv or pipx instead of mixing pip with apt-owned files.

Stop conditions

  • Stop if the simulated transaction removes essential packages or pulls from an unexpected repository.
04

command

Install the reviewed Ubuntu package with apt

caution

Use this step only when the simulated transaction is acceptable and the package is intended to be system-managed. Refresh metadata, install the exact reviewed package, then ask dpkg to identify the installed files rather than assuming an import path.

Why this step matters

apt is the supported owner for Ubuntu system packages and records dependency, file, upgrade, and removal metadata. Using the reviewed exact package avoids a global pip override.

What to understand

apt-get update downloads repository metadata but does not upgrade installed packages by itself.

The quoted package variable prevents shell word splitting; it must contain one validated Ubuntu package name.

dpkg-query proves the package database owns the resulting installation and records the exact version for rollback.

System changes

  • Updates apt metadata and installs the selected .deb package plus the dependencies shown in the prior simulation.

Syntax explained

--yes
Accepts the already-reviewed apt transaction; do not use it before reading the simulation.
-W
Queries dpkg status for the exact package after installation.
-f=...
Prints package, version, and installation state as concrete evidence.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo apt-get update
sudo apt-get install --yes '{{aptPackage}}'
dpkg-query -W -f='${Package} ${Version} ${Status}\n' '{{aptPackage}}'
Example output / evidence
Setting up python3-requests (2.31.0+dfsg-1ubuntu1) ...
python3-requests 2.31.0+dfsg-1ubuntu1 install ok installed

Checkpoint: Confirm apt owns the installation

dpkg-query -W '{{aptPackage}}'

Continue whenThe exact package and installed version are printed without an error.

Stop whenStop if apt proposes removals or the repository origin changed since the simulation.

Security notes

  • Use only configured, trusted Ubuntu repositories and review third-party repository keys separately.

Alternatives

  • Return to the venv route if the Ubuntu package does not meet the project's version constraints.

Stop conditions

  • Do not install when Candidate is (none), the source is unexpected, or the simulated dependency plan changed.
05

command

Create a project virtual environment for Python libraries

caution

For an application or library project, create .venv inside the project directory and invoke that environment's Python explicitly. Installing a requirements file or a pinned package here leaves Ubuntu's base interpreter unchanged and makes the environment disposable and reproducible.

Why this step matters

A virtual environment creates an explicit per-project package boundary. It supports versions independent from Ubuntu packages without deleting or shadowing files in the externally managed base environment.

What to understand

python3-full supplies the standard Ubuntu development runtime, including venv support. The project .venv remains owned by the invoking user.

Calling .venv/bin/python directly is reliable in services, cron jobs, and automation where shell activation is not preserved.

Pin dependencies in a requirements or lock file after validation. A bare latest-version install is suitable only for an initial controlled test.

System changes

  • Installs Ubuntu's python3-full package if absent and creates a user-owned .venv directory containing a separate pip and project dependencies.

Syntax explained

-m venv
Runs Python's standard virtual-environment module using the selected base interpreter.
-m pip
Guarantees pip belongs to the .venv interpreter named immediately before it.
pip show
Reports the installed version and location so the isolation boundary can be verified.
Command
Fill variables0/3 ready

Values stay on this page and are never sent or saved.

sudo apt-get install --yes python3-full
install -d -m 0755 '{{projectDir}}'
python3 -m venv '{{projectDir}}/.venv'
'{{projectDir}}/.venv/bin/python' -m pip install --upgrade pip
'{{projectDir}}/.venv/bin/python' -m pip install '{{packageSpec}}'
'{{projectDir}}/.venv/bin/python' -m pip show '{{packageName}}'
Example output / evidence
Successfully installed pip-25.2
Successfully installed requests-2.32.4
Name: requests
Version: 2.32.4
Location: /srv/example-api/.venv/lib/python3.12/site-packages

Checkpoint: Prove pip installs inside the project

'{{projectDir}}/.venv/bin/python' -c "import sys; print(sys.prefix); print(sys.base_prefix)"

Continue whensys.prefix ends in {{projectDir}}/.venv while sys.base_prefix remains /usr.

Stop whenStop if pip show reports /usr/lib/python3/dist-packages as the new package location.

If this step fails

The virtual environment was not created successfully because ensurepip is not available

Likely causeThe venv support package for Ubuntu's selected Python version is missing.

Safe checks
  • apt-cache policy python3-full python3-venv
  • python3 --version

ResolutionInstall python3-full from the same Ubuntu release, remove the incomplete .venv directory, and create it again.

Security notes

  • Do not commit .venv, embedded credentials, or private-index tokens to source control.

Alternatives

  • For a global command rather than an imported library, use pipx so the executable is exposed without activating a project environment.

Stop conditions

  • Stop if {{projectDir}} is a shared or production path whose owner and deployment process are not established.
06

command

Install a standalone Python CLI with pipx

caution

For tools such as httpie, black, or ansible-lint that expose commands, install Ubuntu's pipx package and let pipx create one isolated environment per application. This avoids dependency conflicts between tools and does not require sudo for the application itself.

Why this step matters

pipx gives command-line applications their own virtual environments and exposes only their entry points. This preserves the system interpreter and avoids forcing unrelated CLI tools to share dependencies.

What to understand

Ubuntu 23.04 and newer provide pipx through apt, which is the safe bootstrap path on a PEP 668 system.

ensurepath updates user shell configuration; a new shell may be required before the command is found.

pipx list records the environment directory, application version, interpreter, and exported commands for later upgrade or removal.

System changes

  • Installs the Ubuntu pipx package, may update the user's shell PATH configuration, and creates a user-owned environment under the pipx home directory.

Syntax explained

pipx ensurepath
Adds the pipx application directory to the user's PATH configuration when required.
pipx install
Creates an isolated environment and exposes the package's console entry points.
pipx list
Shows installed applications, versions, interpreters, and filesystem locations.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo apt-get install --yes pipx
pipx ensurepath
pipx install '{{cliPackage}}'
pipx list
Example output / evidence
installed package httpie 3.2.4, installed using Python 3.12.3
These apps are now globally available
  - http
  - https
venvs are in /home/operator/.local/share/pipx/venvs
apps are exposed on your PATH at /home/operator/.local/bin

Checkpoint: Confirm the CLI belongs to pipx

pipx list; command -v '{{cliCommand}}'

Continue whenpipx lists the package and command -v resolves under the user's local bin directory.

Stop whenStop if the resolved executable points into /usr/local/bin from an earlier sudo pip installation.

Alternatives

  • Use pipx run for a one-off invocation when you do not want a persistent application installation.

Stop conditions

  • Stop if the package is a library without a console entry point; use a project venv instead.
07

command

Use a pinned container for disposable or conflicting workloads

caution

When host isolation is the actual goal, run the task in a pinned Python container rather than weakening the Ubuntu marker. Mount only the required project directory, avoid host networking and privileged mode, and treat the container filesystem as disposable unless the build is captured in an image definition.

Why this step matters

A container gives the workload a separate package filesystem and explicit lifecycle. The restrictive flags prevent a troubleshooting shortcut from silently becoming a privileged host modification.

What to understand

Pin the image by digest in production so a future tag change cannot alter the interpreter or dependency baseline without review.

The project mount is the only writable host-facing path in this example. The container root filesystem is read-only and /tmp is bounded.

Package downloads still use the container's configured network. Apply your normal registry, egress, and supply-chain policy before using this route.

System changes

  • Creates an ephemeral container and temporary virtual environment; the mounted project directory remains available after the container exits.

Syntax explained

--rm
Deletes the stopped container so its temporary package environment is not mistaken for durable state.
--read-only
Prevents writes to the container root filesystem.
--cap-drop=all
Removes Linux capabilities not needed for a package inspection task.
--tmpfs /tmp:...
Provides a bounded temporary writable area for the isolated virtual environment.
Command
Fill variables0/4 ready

Values stay on this page and are never sent or saved.

podman run --rm --read-only --cap-drop=all --security-opt=no-new-privileges -v '{{projectDir}}:/workspace:Z' -w /workspace --tmpfs /tmp:rw,noexec,nosuid,size=256m '{{containerImage}}' sh -lc "python -m venv /tmp/venv && /tmp/venv/bin/python -m pip install '{{packageSpec}}' && /tmp/venv/bin/python -m pip show '{{packageName}}'"
Example output / evidence
Successfully installed requests-2.32.4
Name: requests
Version: 2.32.4
Location: /tmp/venv/lib/python3.12/site-packages

Checkpoint: Confirm the host interpreter is still unchanged

python3 -m pip --version; test -f /usr/lib/python3.12/EXTERNALLY-MANAGED && echo marker-preserved

Continue whenpip still reports an Ubuntu-owned location and marker-preserved is printed.

Stop whenStop if the container requires privileged mode, host root mounts, Docker socket access, or unreviewed secrets.

Security notes

  • Do not mount SSH keys, cloud credentials, package-index tokens, or the container engine socket into an untrusted image.

Alternatives

  • Create a reproducible Containerfile when this environment must be rebuilt or deployed repeatedly.

Stop conditions

  • Do not continue with an unpinned image in a production workflow or when the project mount contains secrets.
08

verification

Verify the selected environment and record rollback evidence

read-only

Run the checks for the route you selected and save the versions and locations with the deployment or project record. Success means the requested import or command works and no new distribution appears in Ubuntu's base pip context.

Why this step matters

Location evidence proves that the remediation respected the ownership boundary. Recording versions and paths also makes later upgrades and rollbacks explainable instead of relying on shell history.

What to understand

The base pip location should remain under Ubuntu's managed directories. The project package should appear only under the chosen .venv.

It is acceptable for an unused route to print no package details. Record only the route actually selected and remove abandoned test environments.

Test the real application or CLI after these ownership checks; an installed package is not proof that application behavior is correct.

System changes

  • None. This step reads interpreter, package, and pipx metadata.

Syntax explained

test -x
Runs project checks only when the virtual-environment interpreter exists.
command -v pipx
Avoids treating an intentionally unused pipx route as a failure.
pip show
Records package version and installation location for the selected environment.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

printf '%s\n' 'Base interpreter:'
python3 -m pip --version
python3 - <<'PY'
import sys
print(f'base prefix: {sys.base_prefix}')
print(f'active prefix: {sys.prefix}')
PY
printf '%s\n' 'Project environment:'
if test -x '{{projectDir}}/.venv/bin/python'; then '{{projectDir}}/.venv/bin/python' -m pip show '{{packageName}}'; fi
printf '%s\n' 'pipx applications:'
if command -v pipx >/dev/null; then pipx list; fi
Example output / evidence
Base interpreter:
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
base prefix: /usr
active prefix: /usr
Project environment:
Name: requests
Version: 2.32.4
Location: /srv/example-api/.venv/lib/python3.12/site-packages
pipx applications:
venvs are in /home/operator/.local/share/pipx/venvs

If this step fails

The package imports in an interactive shell but the service still reports ModuleNotFoundError

Likely causeThe service starts a different Python interpreter or does not use the project's virtual environment.

Safe checks
  • systemctl cat example.service
  • readlink -f /proc/$(pgrep -n -f example)/exe 2>/dev/null

ResolutionPoint the service ExecStart at the reviewed .venv/bin/python or .venv application entry point, then restart only through the normal change process.

Alternatives

  • Capture python -m pip freeze for the selected venv when the project has an approved dependency-locking workflow.

Stop conditions

  • Do not declare success when package location, executable path, or running service interpreter differs from the selected route.

Finish line

Verification checklist

Confirm the Ubuntu base environment remains externally managedpython3 - <<'PY' import pathlib, sysconfig p = pathlib.Path(sysconfig.get_path('stdlib')) / 'EXTERNALLY-MANAGED' print(p) print('present' if p.exists() else 'missing') PYThe Ubuntu standard-library marker path is printed followed by present.
Confirm the selected package is outside the base environment'{{projectDir}}/.venv/bin/python' -m pip show '{{packageName}}' 2>/dev/null || pipx listThe package location is inside the project .venv or the application appears in pipx; it is not newly installed under /usr.
Confirm the original error is no longer relevant to the chosen route'{{projectDir}}/.venv/bin/python' -m pip install --dry-run '{{packageSpec}}'pip resolves the requirement inside the project environment without error: externally-managed-environment.

Recovery guidance

Common problems and safe checks

error: externally-managed-environment appears even with pip install --user

Likely causeUbuntu applies the PEP 668 protection outside virtual environments, including user-site installation, because user packages can still shadow distribution-managed imports.

Safe checks
  • python3 -c "import sys; print(sys.prefix, sys.base_prefix)"
  • python3 -m site --user-site

ResolutionUse a project .venv for libraries or install the command-line application with Ubuntu's pipx package. Do not assume --user creates an independent interpreter.

The newly created .venv has no pip or reports ensurepip is unavailable

Likely causeThe Ubuntu venv components for the selected interpreter are missing or the environment was created before they were installed.

Safe checks
  • python3 --version
  • apt-cache policy python3-full python3-venv

ResolutionInstall python3-full from the current Ubuntu repositories, remove only the incomplete .venv directory, and recreate it.

pipx reports success but the installed command is not found

Likely causeThe pipx binary directory is not in the current shell PATH or the shell has not reloaded its profile.

Safe checks
  • pipx list
  • pipx environment
  • printf '%s\n' "$PATH"

ResolutionRun pipx ensurepath, open a new login shell, and verify command -v for the expected application. Do not copy the entry point into /usr/local/bin manually.

The application still imports an Ubuntu package instead of the .venv version

Likely causeThe service or script invokes /usr/bin/python3, uses an incorrect shebang, or inherits PYTHONPATH entries outside the environment.

Safe checks
  • head -1 ./application-script 2>/dev/null
  • systemctl cat example.service
  • env | grep '^PYTHONPATH='

ResolutionInvoke the reviewed .venv interpreter or entry point explicitly and remove unintended PYTHONPATH overrides through the application's normal configuration process.

A previous sudo pip or --break-system-packages attempt already modified the base interpreter

Likely causeFiles or import precedence may now exist outside apt's recorded state, so simply choosing venv does not repair the earlier modification.

Safe checks
  • python3 -m pip list --format=freeze
  • dpkg -V python3-minimal python3 2>/dev/null
  • python3 -m pip show '{{packageName}}' 2>/dev/null

ResolutionPreserve evidence, identify files and package owners, and restore affected Ubuntu packages through apt in a maintenance window. Do not mass-uninstall from the base interpreter without a reviewed recovery plan.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Use an Ubuntu package when system integration and distribution security updates matter more than obtaining the newest upstream release.
  • Use a project .venv for libraries and application entry points that belong to one repository or deployed service.
  • Use pipx for user-facing Python CLI applications, and use pipx run for short-lived trials when persistence is unnecessary.
  • Use Conda, uv, or another environment manager only when the team has standardized its ownership, lock, update, and rollback process; do not layer tools without a clear source of truth.
  • Use a pinned container when native dependencies conflict or the workload must be discarded cleanly after execution.

Operate it safely

  • Commit a reviewed requirements or lock file for each project environment and rebuild .venv from that source instead of treating the environment directory as an artifact.
  • Update service definitions, developer documentation, and CI jobs to call the selected .venv or pipx command explicitly so future operators do not fall back to system Python.
  • Periodically review Ubuntu and Python versions, package vulnerabilities, and dependency provenance; rebuild isolated environments rather than performing undocumented global upgrades.
  • Add a preflight check to automation that fails when sys.prefix equals sys.base_prefix before any pip install intended for a project.

Reference

Frequently asked questions

What does error: externally-managed-environment mean?

It means the current base Python installation declares that another package manager owns its default package context. On Ubuntu, apt owns the system interpreter and packages. pip refuses the write so an application install does not overwrite or shadow files used by operating-system tools.

Should I delete /usr/lib/python3.x/EXTERNALLY-MANAGED?

No. Deleting the marker only disables the warning mechanism; it does not make pip the owner of apt-managed files. Use apt, a project virtual environment, pipx, or a deliberately isolated container.

Why does pip install --user still show error: externally-managed-environment?

A user-site package can still take precedence over distribution packages on sys.path and change behavior of programs using the base interpreter. Ubuntu therefore directs you to an isolated virtual environment or pipx instead.

When should I use apt instead of pip?

Use apt when Ubuntu provides a suitable package and you want distribution integration, security updates, dependency tracking, and system-wide availability. Use pip only inside an explicitly owned environment for upstream Python dependencies.

When should I use pipx instead of venv?

Use pipx for a command-line application you want on your user PATH. Use venv when a project imports libraries or owns application dependencies as part of its repository and deployment lifecycle.

Is --break-system-packages ever safe?

It is an explicit override, not the standard fix. It requires a separately approved reason, tested recovery plan, exact file ownership understanding, and acceptance that Ubuntu tools or future apt transactions may be affected. This guide intentionally avoids it.

How do I prove the fix is correct?

Record the selected interpreter, pip version, package version, and installation location. The package should resolve inside the project .venv or pipx environment, while the base interpreter remains under /usr and the EXTERNALLY-MANAGED marker remains present.

How often should this guide be reviewed?

Review it within 180 days and whenever Ubuntu, Python, pip, pipx, or PEP 668 behavior changes. Recheck all commands against a supported Ubuntu release before relying on the examples.

Recovery

Rollback

Rollback the chosen owner rather than editing Ubuntu's Python marker. Remove an apt package with apt, delete only the reviewed project .venv, uninstall a pipx application with pipx, or discard the container. Preserve dependency and service evidence before removal.

  1. For an apt route, simulate removal first with sudo apt-get --simulate remove '{{aptPackage}}'; continue only if the transaction is safe, then use sudo apt-get remove '{{aptPackage}}'.
  2. For a venv route, stop the application, preserve the lock or requirements file, verify the path, and remove only '{{projectDir}}/.venv'; recreate it from reviewed dependencies when needed.
  3. For a pipx route, record pipx list, then run pipx uninstall '{{cliPackage}}'; remove shell PATH changes only if no other pipx applications use them.
  4. For a container route, stop and remove the container or revert the pinned image reference; separately revert any reviewed files written to the project mount.

Evidence

Sources and review

Verified 2026-08-23Review due 2027-02-19
Python Packaging User Guide — Externally Managed EnvironmentsofficialPython documentation — Virtual Environments and PackagesofficialUbuntu for Developers — Develop with Python on Ubuntuofficial