Python Installation — Linux#
Debian / Ubuntu#
sudo apt update
sudo apt install python3 python3-pip python3-venv python3-dev -y
Output:
Reading package lists... Done
The following NEW packages will be installed:
python3 python3-dev python3-pip python3-venv
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
For a newer version than the distro default, add the deadsnakes PPA:
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt update
sudo apt install python3.12 python3.12-venv python3.12-dev -y
Fedora / RHEL / Rocky / AlmaLinux#
# Fedora
sudo dnf install python3 python3-pip -y
# RHEL / Rocky / AlmaLinux — enable EPEL first
sudo dnf install epel-release -y
sudo dnf install python3.12 -y
Output:
Installed:
python3.12-3.12.3-1.fc40.x86_64
python3.12-libs-3.12.3-1.fc40.x86_64
Complete!
Arch Linux / Manjaro#
sudo pacman -Syu python python-pip
Output:
resolving dependencies...
Packages (2) python-3.12.3-1 python-pip-24.0-1
Total Installed Size: 74.32 MiB
:: Proceed with installation? [Y/n] Y
[!NOTE] Arch’s
pythonpackage always tracks the latest stable Python, so it updates frequently. This is excellent for getting new versions but can occasionally break packages that haven’t updated yet.
pyenv — version-independent approach#
Works on any Linux distro. Installs Python in your home directory, no root required.
# Install build dependencies (Debian/Ubuntu)
sudo apt install -y make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
libffi-dev liblzma-dev
# Install pyenv
curl https://pyenv.run | bash
# Append to ~/.bashrc / ~/.zshrc
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
source ~/.bashrc
# Install and set default
pyenv install 3.12.3
pyenv global 3.12.3
Output:
Downloading Python-3.12.3.tar.xz...
-> https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tar.xz
Installing Python-3.12.3...
Installed Python-3.12.3 to /home/alice/.pyenv/versions/3.12.3
Building from source (unsupported distros)#
Use this when no package manager provides your target version:
# Install build dependencies first (Debian/Ubuntu)
sudo apt install -y build-essential libssl-dev zlib1g-dev libffi-dev libsqlite3-dev
# Download and build
wget https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tgz
tar xf Python-3.12.3.tgz
cd Python-3.12.3
./configure --enable-optimizations --with-ensurepip=install
make -j$(nproc)
sudo make altinstall # altinstall avoids overwriting the system python3
Output:
Collecting setuptools
Collecting pip
Installing collected packages: setuptools, pip
Successfully installed pip-24.0 setuptools-69.5.1
[!TIP] Use
make altinstall(notmake install) to avoid replacing the systempython3symlink, which could break OS tools.
Verify#
python3 --version
python3 -m pip --version
python3 -c "import venv; print('venv OK')"
Output:
Python 3.12.3
pip 24.0 from /home/alice/.pyenv/versions/3.12.3/lib/python3.12/site-packages/pip (python 3.12)
venv OK
Common pitfalls#
[!WARNING] Externally managed environments — Debian 12+ and Ubuntu 23.04+ enforce PEP 668, blocking
pip installoutside a venv with the errorerror: externally-managed-environment. Always activate a virtual environment first. See venv.
[!WARNING] Missing
python3-venv— On Ubuntu,python3 -m venvfails until you installpython3-venv(orpython3.12-venvfor a specific version):sudo apt install python3-venv.
Next steps#
python3 -m venv .venv
source .venv/bin/activate
See Virtual Environments.
openSUSE / SLES#
openSUSE Tumbleweed (rolling) and Leap (stable) use zypper and ship Python under versioned package names. Tumbleweed tends to track upstream within weeks; Leap aligns with SUSE Linux Enterprise and may be a release or two behind.
# Tumbleweed / Leap
sudo zypper refresh
sudo zypper install python312 python312-pip python312-devel
# SLES — first enable the Python module
sudo SUSEConnect --product sle-module-python3/15.5/x86_64
sudo zypper install python311 python311-pip
Output:
The following 3 NEW packages are going to be installed:
python312 python312-pip python312-devel
Continue? [y/n/v/...? shows all options] (y): y
Retrieving: python312-3.12.3-1.1.x86_64.rpm
[!NOTE] openSUSE keeps the system
python3separate from the developer-installablepython312. Both can coexist;update-alternativesis not the openSUSE convention — useeselect-style links or just callpython3.12explicitly.
Alpine / musl-based distros#
Alpine Linux (commonly used as a Docker base image) ships Python compiled against musl libc instead of glibc. Most wheels on PyPI assume glibc, so pure-Python packages install fine, but C-extension packages may need to be compiled from source — sometimes with extra system packages.
# Alpine apk
apk add --no-cache python3 py3-pip
# When you need to build C extensions
apk add --no-cache build-base python3-dev libffi-dev openssl-dev
Output:
(1/3) Installing python3 (3.12.3-r0)
(2/3) Installing py3-pip (24.0-r0)
(3/3) Installing py3-setuptools (69.5.1-r0)
OK: 84 MiB in 50 packages
[!WARNING] If you see
error: invalid command 'bdist_wheel'orImportError: Error loading shared library, you are hitting the musl-vs-glibc wheel gap. Either install the matching-devpackages and let pip compile from source, or switch your base image topython:3.12-slim(Debian-based, glibc, prebuilt wheels work).
Gentoo#
Gentoo’s portage system compiles everything from source by default. The python USE flag selects which Python versions are available system-wide; eselect python chooses the default python3.
# Pull the current ebuild tree
sudo emerge --sync
# Install a specific Python slot
sudo emerge -av '=dev-lang/python-3.12*'
# Choose the default
sudo eselect python list
sudo eselect python set python3.12
Output:
Available Python interpreters, in order of preference:
[1] python3.11
[2] python3.12 *
NixOS / nix-shell#
NixOS treats Python as a per-project derivation rather than a globally-installed package. A shell.nix declares which Python and which packages, and nix-shell materializes them in a hermetic environment.
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = [
(pkgs.python312.withPackages (ps: with ps; [ requests rich pytest ]))
];
}
nix-shell # drops into a shell with Python 3.12 + packages
python --version
which python
Output:
Python 3.12.3
/nix/store/abc...-python3-3.12.3/bin/python
[!TIP] On NixOS, never
pip installsystem-wide — the read-only/nix/storewill refuse. Use a venv inside anix-shell, or declare dependencies inshell.nix.
Method — uv python install#
uv downloads precompiled CPython tarballs (from the python-build-standalone project) and unpacks them under ~/.local/share/uv/python/. No build dependencies, no compilation, no root — and it works the same on Debian, Fedora, Arch, Alpine, and NixOS.
# Install uv itself
curl -LsSf https://astral.sh/uv/install.sh | sh
exec $SHELL # reload PATH
# Install Python
uv python install 3.12
uv python install 3.11 3.13
# List
uv python list
# Use without touching PATH
uv venv --python 3.12
uv run python --version
Output:
Installed Python 3.12.3 in 1.10s
+ cpython-3.12.3-linux-x86_64-gnu (python3.12)
[!NOTE] The python-build-standalone tarballs uv uses are statically linked against musl-glibc compatibility shims — they run on most Linux distros including Alpine. The trade-off is that some niche extension modules (like those linking to system-wide BLAS) may behave differently than a from-source build.
Method — asdf and mise#
Both are polyglot version managers that handle Python alongside Node, Ruby, Go, and dozens of others via plugins. Useful on shared dev machines, in CI runners that need a specific minor version, and on personal laptops where you already use them for other languages.
# asdf
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.0
echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
source ~/.bashrc
asdf plugin add python
asdf install python 3.12.3
asdf global python 3.12.3
# mise — faster Rust-based successor
curl https://mise.run | sh
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
source ~/.bashrc
mise use --global python@3.12
mise current python
Output:
python 3.12.3 installation successful
mise python@3.12.3 ✓ installed
3.12.3
[!TIP]
misereads the same.tool-versionsfile asasdfbut starts up in single-digit milliseconds rather than ~80 ms. If you are starting fresh in 2026, prefermise.
Method — conda / mamba / miniforge#
For scientific computing, GPU machine learning, or polyglot data stacks (Python + R + C compilers + CUDA), the conda ecosystem manages Python and the native libraries it links against.
# Install Miniforge (community fork, defaults to conda-forge, no Anaconda license risk)
curl -LO "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh -b -p ~/miniforge3
~/miniforge3/bin/conda init bash
exec $SHELL
# Create an environment
mamba create -n ml python=3.12 numpy pandas pytorch cuda-version=12.4 -c pytorch -c nvidia
mamba activate ml
python -c "import torch; print(torch.cuda.is_available())"
Output:
Looking for: ['python=3.12', 'numpy', 'pandas', 'pytorch', 'cuda-version=12.4']
+ python 3.12.3-h99e199e_0
+ pytorch 2.3.0-cu124_py312
done
True
System Python vs user-managed Python#
Every Linux distro ships a Python that other system tools depend on. Treat it as a runtime for those tools — not a development environment.
| Tool that depends on system Python | Distro |
|---|---|
apt, add-apt-repository, unattended-upgrades | Debian/Ubuntu |
dnf, yum, firewalld, cloud-init | Fedora/RHEL |
pacman (some scripts), mkinitcpio | Arch |
zypper (helpers), YaST | openSUSE |
cloud-init, salt-minion (everywhere) | Many distros |
Rules:
- Never
sudo pip installagainst the system Python. PEP 668 blocks this on modern Debian/Ubuntu (and Fedora 38+) for a good reason. - Never replace
/usr/bin/python3with a different version viaupdate-alternatives. System scripts have shebangs like#!/usr/bin/python3and assume the distro’s exact build. - Always work inside a virtual environment, a uv-managed Python, a pyenv-managed Python, or a conda env.
- To install a few CLI tools globally for your user (not system), prefer
pipx(apt install pipxorpip install --user pipx).
# Wrong — will be blocked by PEP 668 on modern distros
sudo pip install httpie
# Right — pipx installs each tool in its own venv under ~/.local/pipx
sudo apt install pipx
pipx ensurepath
pipx install httpie
Output:
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.
installed package httpie 3.2.2, installed using Python 3.12.3
These apps are now globally installed
- http
- https
- httpie
done! ✨ 🌟 ✨
Container / Docker approach#
For reproducible builds, CI, and ephemeral dev environments, the official python image on Docker Hub is well-curated. Pick the variant that matches your use case:
| Tag | Base | Size | When to use |
|---|---|---|---|
python:3.12 | Debian | ~1 GB | Maximum compatibility, all wheels work |
python:3.12-slim | Debian (minimal) | ~150 MB | Production — small, glibc, almost all wheels work |
python:3.12-alpine | Alpine (musl) | ~50 MB | Smallest, but C extensions may need compilation |
python:3.12-bookworm | Pinned to specific Debian | ~1 GB | Long-term reproducibility |
python:3.12-bullseye | Older Debian | ~1 GB | Maximum wheel compatibility |
# Production-grade Dockerfile
FROM python:3.12-slim AS builder
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project
FROM python:3.12-slim
COPY --from=builder /app/.venv /app/.venv
COPY . /app
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "myapp"]
# Drop into a one-shot Python 3.12 shell, no install needed
docker run --rm -it python:3.12-slim bash
Output:
Unable to find image 'python:3.12-slim' locally
Pulling fs layer
Downloaded newer image for python:3.12-slim
root@9f3b2:/# python --version
Python 3.12.3
Verification recipes#
After any install, run these to confirm the result.
# Where is python3 resolving from?
which -a python3
type -a python3
# Version
python3 --version
# Is pip wired to the same interpreter?
python3 -m pip --version
# Does the venv module work?
python3 -m venv /tmp/_probe && rm -rf /tmp/_probe && echo 'venv OK'
# Does SSL work?
python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
# Does sqlite3 work? (commonly missing on minimal source builds)
python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
# Does the build of Python include all stdlib modules?
python3 -c "import bz2, lzma, ctypes, hashlib, ssl, sqlite3, zlib, readline, tkinter; print('all good')"
# Can pip reach PyPI?
python3 -m pip install --dry-run --user requests
Output:
/home/alice/.pyenv/shims/python3
/usr/bin/python3
Python 3.12.3
pip 24.0 from /home/alice/.pyenv/versions/3.12.3/lib/python3.12/site-packages/pip (python 3.12)
venv OK
OpenSSL 3.0.13 30 Jan 2024
3.46.0
all good
Would install requests-2.32.3 certifi-2024.7.4 charset-normalizer-3.3.2 idna-3.7 urllib3-2.2.2
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
error: externally-managed-environment | PEP 668 enforced on modern Debian/Ubuntu/Fedora | Use a venv, pipx, or uv instead of system pip |
python3 -m venv fails with ensurepip is not available | python3-venv package not installed | sudo apt install python3-venv |
ModuleNotFoundError: No module named '_ssl' after source build | Missing libssl-dev at compile time | Reinstall libssl-dev, rebuild: ./configure --enable-optimizations && make -j$(nproc) && sudo make altinstall |
ModuleNotFoundError: No module named '_sqlite3' | Missing libsqlite3-dev at compile time | Same as above with libsqlite3-dev |
pip install is slow on Raspberry Pi or ARM | No wheels for arm64/armv7 — pip compiles from source | Switch to piwheels: pip install --extra-index-url https://www.piwheels.org/simple/ |
dnf install python3.12 returns “No match” on RHEL | EPEL repo not enabled | sudo dnf install epel-release |
add-apt-repository: command not found | software-properties-common missing | sudo apt install software-properties-common |
SSL: CERTIFICATE_VERIFY_FAILED from pip | System CA bundle missing or stale | sudo apt install --reinstall ca-certificates && sudo update-ca-certificates |
pyenv install fails on _uuid module | Missing uuid-dev | sudo apt install uuid-dev |
Common pitfalls (extended)#
[!WARNING]
update-alternativesforpython— Don’t repoint/usr/bin/python3away from the distro default. Even Ubuntu’s ownupdate-managerwill break ifpython3doesn’t behave the way Canonical’s scripts expect.
[!WARNING] PPAs are not always trustworthy —
ppa:deadsnakes/ppais widely used and maintained, but other PPAs may be unmaintained. Always check the PPA’s update frequency before adding it to a production system.
[!WARNING]
/tmponnoexecmount — Some hardened distros mount/tmpwithnoexec, which breakspipwhen it tries to execute build scripts from/tmp. SetTMPDIRto a writable, executable location:TMPDIR=$HOME/.cache/pip-tmp pip install <pkg>.
[!TIP] For air-gapped environments (no internet from the build host), use
pip downloadon a connected host to grab wheels, thenpip install --no-index --find-links=./wheels <pkg>on the target.
Real-world recipes#
Fresh Ubuntu 24.04 in three commands#
sudo apt update && sudo apt install -y python3 python3-venv python3-pip pipx
pipx ensurepath
pipx install uv
Newest Python on Ubuntu 22.04 LTS#
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt update
sudo apt install -y python3.13 python3.13-venv python3.13-dev
python3.13 --version
Locked-down server with no root access#
# Install everything to ~/.local — no sudo anywhere
curl -LsSf https://astral.sh/uv/install.sh | sh
~/.local/bin/uv python install 3.12
~/.local/bin/uv venv --python 3.12
source .venv/bin/activate
Reproducible CI install (GitHub Actions on Linux runners)#
jobs:
test:
strategy:
matrix:
python: ['3.11', '3.12', '3.13']
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv python install ${{ matrix.python }}
- run: uv sync --frozen
- run: uv run pytest
Building Python from source with full optimisation#
sudo apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev libffi-dev libncurses5-dev libgdbm-dev \
liblzma-dev tk-dev uuid-dev
curl -LO https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tgz
tar xf Python-3.12.3.tgz
cd Python-3.12.3
./configure \
--enable-optimizations \
--with-lto \
--enable-shared \
--with-ensurepip=install \
--prefix=/opt/python/3.12.3
make -j"$(nproc)"
sudo make altinstall
/opt/python/3.12.3/bin/python3.12 --version
Cleaning up an old install#
# apt-installed
sudo apt remove --purge python3.11 python3.11-* && sudo apt autoremove
# pyenv-installed
pyenv uninstall 3.12.3
# Source-installed via altinstall
sudo rm /usr/local/bin/python3.12 /usr/local/bin/pip3.12
sudo rm -rf /usr/local/lib/python3.12
# uv-installed
uv python uninstall 3.12
# Verify
which -a python3 python3.12 python3.13
Next steps#
python3 -m venv .venv
source .venv/bin/activate
See Virtual Environments, pip vs uv, and apt-get for deeper coverage.