cheat sheet

Python Installation

Install Python 3 on Debian/Ubuntu, Fedora/RHEL, and Arch Linux. Covers package managers, pyenv, and building from source for unsupported distros.

Python Installation — Linux

Debian / Ubuntu

bash
sudo apt update
sudo apt install python3 python3-pip python3-venv python3-dev -y

Output:

text
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:

bash
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

bash
# 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:

text
Installed:
  python3.12-3.12.3-1.fc40.x86_64
  python3.12-libs-3.12.3-1.fc40.x86_64
Complete!

Arch Linux / Manjaro

bash
sudo pacman -Syu python python-pip

Output:

text
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

Arch's python package 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.

bash
# 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:

text
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:

bash
# 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:

text
Collecting setuptools
Collecting pip
Installing collected packages: setuptools, pip
Successfully installed pip-24.0 setuptools-69.5.1

Use make altinstall (not make install) to avoid replacing the system python3 symlink, which could break OS tools.

Verify

bash
python3 --version
python3 -m pip --version
python3 -c "import venv; print('venv OK')"

Output:

text
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

Externally managed environments — Debian 12+ and Ubuntu 23.04+ enforce PEP 668, blocking pip install outside a venv with the error error: externally-managed-environment. Always activate a virtual environment first. See venv.

Missing python3-venv — On Ubuntu, python3 -m venv fails until you install python3-venv (or python3.12-venv for a specific version): sudo apt install python3-venv.

Next steps

bash
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.

bash
# 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:

text
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

openSUSE keeps the system python3 separate from the developer-installable python312. Both can coexist; update-alternatives is not the openSUSE convention — use eselect-style links or just call python3.12 explicitly.

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.

bash
# 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:

text
(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

If you see error: invalid command 'bdist_wheel' or ImportError: Error loading shared library, you are hitting the musl-vs-glibc wheel gap. Either install the matching -dev packages and let pip compile from source, or switch your base image to python: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.

bash
# 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:

text
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.

nix
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  packages = [
    (pkgs.python312.withPackages (ps: with ps; [ requests rich pytest ]))
  ];
}
bash
nix-shell             # drops into a shell with Python 3.12 + packages
python --version
which python

Output:

text
Python 3.12.3
/nix/store/abc...-python3-3.12.3/bin/python

On NixOS, never pip install system-wide — the read-only /nix/store will refuse. Use a venv inside a nix-shell, or declare dependencies in shell.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.

bash
# 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:

text
Installed Python 3.12.3 in 1.10s
 + cpython-3.12.3-linux-x86_64-gnu (python3.12)

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.

bash
# 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:

text
python 3.12.3 installation successful
mise python@3.12.3 ✓ installed
3.12.3

mise reads the same .tool-versions file as asdf but starts up in single-digit milliseconds rather than ~80 ms. If you are starting fresh in 2026, prefer mise.

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.

bash
# 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:

text
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 PythonDistro
apt, add-apt-repository, unattended-upgradesDebian/Ubuntu
dnf, yum, firewalld, cloud-initFedora/RHEL
pacman (some scripts), mkinitcpioArch
zypper (helpers), YaSTopenSUSE
cloud-init, salt-minion (everywhere)Many distros

Rules:

  1. Never sudo pip install against the system Python. PEP 668 blocks this on modern Debian/Ubuntu (and Fedora 38+) for a good reason.
  2. Never replace /usr/bin/python3 with a different version via update-alternatives. System scripts have shebangs like #!/usr/bin/python3 and assume the distro's exact build.
  3. Always work inside a virtual environment, a uv-managed Python, a pyenv-managed Python, or a conda env.
  4. To install a few CLI tools globally for your user (not system), prefer pipx (apt install pipx or pip install --user pipx).
bash
# 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:

text
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:

TagBaseSizeWhen to use
python:3.12Debian~1 GBMaximum compatibility, all wheels work
python:3.12-slimDebian (minimal)~150 MBProduction — small, glibc, almost all wheels work
python:3.12-alpineAlpine (musl)~50 MBSmallest, but C extensions may need compilation
python:3.12-bookwormPinned to specific Debian~1 GBLong-term reproducibility
python:3.12-bullseyeOlder Debian~1 GBMaximum wheel compatibility
dockerfile
# 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"]
bash
# Drop into a one-shot Python 3.12 shell, no install needed
docker run --rm -it python:3.12-slim bash

Output:

text
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.

bash
# 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:

text
/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

SymptomCauseFix
error: externally-managed-environmentPEP 668 enforced on modern Debian/Ubuntu/FedoraUse a venv, pipx, or uv instead of system pip
python3 -m venv fails with ensurepip is not availablepython3-venv package not installedsudo apt install python3-venv
ModuleNotFoundError: No module named '_ssl' after source buildMissing libssl-dev at compile timeReinstall libssl-dev, rebuild: ./configure --enable-optimizations && make -j$(nproc) && sudo make altinstall
ModuleNotFoundError: No module named '_sqlite3'Missing libsqlite3-dev at compile timeSame as above with libsqlite3-dev
pip install is slow on Raspberry Pi or ARMNo wheels for arm64/armv7 — pip compiles from sourceSwitch to piwheels: pip install --extra-index-url https://www.piwheels.org/simple/
dnf install python3.12 returns "No match" on RHELEPEL repo not enabledsudo dnf install epel-release
add-apt-repository: command not foundsoftware-properties-common missingsudo apt install software-properties-common
SSL: CERTIFICATE_VERIFY_FAILED from pipSystem CA bundle missing or stalesudo apt install --reinstall ca-certificates && sudo update-ca-certificates
pyenv install fails on _uuid moduleMissing uuid-devsudo apt install uuid-dev

Common pitfalls (extended)

update-alternatives for python — Don't repoint /usr/bin/python3 away from the distro default. Even Ubuntu's own update-manager will break if python3 doesn't behave the way Canonical's scripts expect.

PPAs are not always trustworthyppa:deadsnakes/ppa is widely used and maintained, but other PPAs may be unmaintained. Always check the PPA's update frequency before adding it to a production system.

/tmp on noexec mount — Some hardened distros mount /tmp with noexec, which breaks pip when it tries to execute build scripts from /tmp. Set TMPDIR to a writable, executable location: TMPDIR=$HOME/.cache/pip-tmp pip install <pkg>.

For air-gapped environments (no internet from the build host), use pip download on a connected host to grab wheels, then pip install --no-index --find-links=./wheels <pkg> on the target.

Real-world recipes

Fresh Ubuntu 24.04 in three commands

bash
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

bash
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

bash
# 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)

yaml
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

bash
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

bash
# 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

bash
python3 -m venv .venv
source .venv/bin/activate

See Virtual Environments, pip vs uv, and apt-get for deeper coverage.