skip to content

Python Installation — Linux

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

12 min read 43 snippets deep dive

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

# 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 (not make install) to avoid replacing the system python3 symlink, 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 install outside a venv with the error error: externally-managed-environment. Always activate a virtual environment first. See venv.

[!WARNING] 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#

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

# 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' 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.

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

# 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] 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.

# 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 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).
# 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:

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

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

[!WARNING] 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.

[!WARNING] 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.

[!WARNING] /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>.

[!TIP] 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#

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.