Ubuntu 24.04 / 26.04 LTS · native, VM & WSL2 · June 2026

Ubuntu Linux for AI & Agentic Developers

A paste-ready setup checklist with saved progress for a clean AI/ML dev box: uv Python (PEP 668), Node LTS, Docker, NVIDIA CUDA + container toolkit, Ollama for local LLMs, Claude Code & other agent CLIs, and WSL2 — plus the anti-patterns that break ML environments.

Dev box setup
0%
0 / 0
How to use this page. Work top-down; tick each step as you finish it (progress saves to your browser's localStorage). Hit to copy any command block. Commands assume a non-root user with sudo on Ubuntu 24.04 or 26.04 LTS (Debian-family apt); WSL2 differences are flagged in Phase 9. Version-sensitive figures are tagged (as of mid-2026) — re-verify before pinning in production.
Security. Adding your user to the docker group is root-equivalent; the NVIDIA/GPU steps and any curl … | sh installer run untrusted code as you — read scripts before piping to a shell. For internet-facing boxes, also work through Linux Server Hardening.

Quick Reference — highest-frequency commands

TaskCommandNotes
Full system updatesudo apt update && sudo apt full-upgrade -yfull-upgrade handles dependency changes.
Install uv (Python toolchain)curl -LsSf https://astral.sh/uv/install.sh | shReplaces pip+venv+pyenv+pipx. v0.11.x.
New project envuv venv && uv add numpy torchPer-project venv + lockfile. Never sudo pip.
Run a script in its envuv run train.pyAuto-syncs deps first.
Pin a Python versionuv python install 3.12No pyenv needed.
GPU statusnvidia-smiHeader shows driver + max CUDA; table shows VRAM/process.
GPU inside Dockerdocker run --rm --gpus all ubuntu nvidia-smiNeeds nvidia-container-toolkit (Phase 6).
Run a local LLMollama run llama3.1Serves API on localhost:11434.
Install Claude Codecurl -fsSL https://claude.ai/install.sh | bashOr npm i -g @anthropic-ai/claude-code (Node 18+).
Reclaim Docker diskdocker system df · docker system prune -aImages/volumes fill disks fast on ML boxes.
What's listeningsudo ss -tulpnOpen ports + owning process.
Which Ubuntu am I onlsb_release -a · cat /etc/os-releaseConfirm LTS codename.

Phase 0
Choose your Ubuntu (and where to run it)

Which release? Always an LTS
ReleaseStatus (mid-2026)Default PythonUse it?
26.04 LTS "Resolute Raccoon"Released 2026-04-233.14✅ Newest LTS — best for a fresh box
24.04 LTS "Noble Numbat" (24.04.4)Point release Feb 20263.12✅ Proven, widest tooling support
25.10 "Questing Quokka" (interim)EOL ~2026-07-093.13❌ About to expire

Support windows: an LTS gets 5 years of free security maintenance; 10 years total via free Ubuntu Pro (ESM, covers universe too) for personal use on up to 5 machines; 15 years with the Legacy add-on.

Don't develop on an interim release (the non-LTS like 25.10): only 9 months of updates, and CUDA/driver/PyTorch wheels target the LTS first. Pick an LTS and stay on it.
Native, VM, WSL2, or cloud GPU?
  • Bare-metal Ubuntu — best for a local NVIDIA GPU; full driver/CUDA performance, no virtualization tax. The default for serious local training/inference.
  • WSL2 (Ubuntu on Windows 11) — excellent now: GPU passthrough works (host driver only), systemd is on by default in the Ubuntu image. Great if your daily driver is Windows. See Phase 9. Keep code in the Linux FS, not /mnt/c.
  • VM (UTM/VMware/VirtualBox) — fine for CPU dev; GPU passthrough is fiddly. Good for isolation/testing.
  • Cloud GPU (Lambda, RunPod, AWS/GCP/Azure) — rent an H100/A100/L40S by the hour when you don't own a GPU; same Ubuntu setup applies. Use SSH + VS Code Remote-SSH (Phase 8).

Phase 1
System baseline, git & GitHub

Update, then install build essentials
baseline packages
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y build-essential curl wget git ca-certificates gnupg \
                    unzip pkg-config software-properties-common

build-essential (gcc/make) is needed to compile many Python/native wheels and CUDA samples.

Latest git + GitHub CLI

Distro git is often old. Add the maintainers' PPA for current git, and install gh from GitHub's repo (note the keyring now lives in /etc/apt/keyrings).

git (current) + gh CLI
# current git
sudo add-apt-repository -y ppa:git-core/ppa && sudo apt update && sudo apt install -y git

# GitHub CLI (official repo)
sudo mkdir -p -m 755 /etc/apt/keyrings
wget -nv -O- https://cli.github.com/packages/githubcli-archive-keyring.gpg \
  | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
  | sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
sudo apt update && sudo apt install -y gh
SSH key + git identity for GitHub
ed25519 key + auth
ssh-keygen -t ed25519 -C "[email protected]"     # accept defaults; set a passphrase
git config --global user.name  "Your Name"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global pull.rebase true
# authenticate gh (opens browser / device code), then it manages the key:
gh auth login
gh auth login can upload your SSH key and set credential helpers automatically — simpler than pasting keys into the web UI.

Phase 2
Shell & modern CLI tools

zsh + starship prompt (optional but nice)
shell
sudo apt install -y zsh
chsh -s "$(which zsh)"                         # log out/in to take effect
# starship: fast cross-shell prompt
curl -sS https://starship.rs/install.sh | sh
echo 'eval "$(starship init zsh)"' >> ~/.zshrc
Fast CLI tools (mind the renamed binaries)
install
sudo apt install -y ripgrep fd-find bat fzf zoxide jq btop
# Debian/Ubuntu rename two of them to avoid clashes — alias them back:
mkdir -p ~/.local/bin
ln -sf "$(which fdfind)" ~/.local/bin/fd
echo 'alias bat=batcat' >> ~/.zshrc
echo 'eval "$(zoxide init zsh)"' >> ~/.zshrc      # adds the `z` smart-cd
Toolapt packageBinary you typeGotcha
ripgrepripgreprgpackage ≠ command name
fdfd-findfdfindsymlink to fd (name clash)
batbatbatcatalias bat=batcat
eza (ls)universe or gierens.de repoezasuccessor to deprecated exa
fzf / zoxide / jq / btopsamesame

Phase 3
Python the right way — uv & PEP 668

⚠️ Why sudo pip install now fails (PEP 668)

On Ubuntu 24.04+ the system Python is externally managed: pip install requests against it errors with error: externally-managed-environment. This is on purpose — pip and apt fighting over the same files used to silently break the OS.

Never "fix" it with sudo pip install --break-system-packages. That bypasses the guard rail and can corrupt distro tooling (apt itself is written in Python). The correct answer is always a per-project virtual environment.

Three valid approaches: uv (recommended below), the stdlib python3 -m venv, or pipx for standalone CLI apps.

Install uv — one tool for Python, venvs & deps

uv (by Astral, v0.11.x as of mid-2026) is a Rust-fast drop-in that replaces pip, virtualenv, pyenv, pip-tools, and pipx. It's the modern default for AI work — resolves and installs huge dep trees (torch, transformers) in seconds.

install
curl -LsSf https://astral.sh/uv/install.sh | sh
# restart your shell, then confirm:
uv --version
daily workflow
uv python install 3.12        # download a CPython (replaces pyenv)
uv init myproj && cd myproj   # new project with pyproject.toml
uv add torch transformers     # add deps + write uv.lock
uv run python train.py        # run inside the project env (auto-synced)
uv venv                       # just make a .venv in an existing project
uv pip install -r requirements.txt   # pip-compatible interface
uv tool install ruff          # install a CLI app isolated (pipx-style)
uvx ruff check .              # run a tool ephemerally, no install
Commit uv.lock for reproducible installs. uv sync recreates the exact env on another machine — gold for "works on my box" GPU repros.
pipx & deadsnakes (the alternatives)
pipx — isolated Python CLI apps
sudo apt install -y pipx
pipx ensurepath          # adds ~/.local/bin to PATH; restart shell
pipx install httpie      # each app gets its own venv
deadsnakes — extra Python versions (when uv won't do)
sudo add-apt-repository -y ppa:deadsnakes/ppa
sudo apt update && sudo apt install -y python3.11 python3.11-venv python3.11-dev
Skip conda unless a specific package demands it (rare now). Mixing conda's base with system Python/apt shadows binaries and breaks imports. If you must, never auto-activate base (conda config --set auto_activate_base false).

Phase 4
Node.js (for agent CLIs & tooling)

Install Node via nvm or fnm — not apt/snap

Most agent CLIs (Claude Code, Gemini CLI, Codex) and JS tooling need Node. Node 24 "Krypton" is the active LTS as of mid-2026. Use a version manager so global npm -g doesn't need sudo (the cause of endless EACCES errors).

nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
# restart shell, then:
nvm install --lts && nvm use --lts && nvm alias default 'lts/*'
node --version    # v24.x
fnm (faster Rust alternative)
curl -fsSL https://fnm.vercel.app/install | bash
fnm install --lts && fnm use lts/latest
Avoid the snap/apt Node for development: apt pins an old major; snap confinement blocks access to some paths and breaks native toolchains.

Phase 5
Docker Engine (the right repo, not snap)

Install Docker Engine + Compose v2 from Docker's repo

Install Docker Engine (not the snap, not docker.io) using the current DEB822 .sources format. Docker Desktop is optional and separate.

official apt install
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
                    docker-buildx-plugin docker-compose-plugin
run without sudo (root-equivalent — see note)
sudo usermod -aG docker "$USER"
newgrp docker            # or log out/in
docker run hello-world
docker compose version   # Compose v2 = `docker compose` (space), not docker-compose
The docker group = root. Anyone in it can mount the host FS as root via a container. On shared/exposed machines prefer rootless mode: dockerd-rootless-setuptool.sh install (needs uidmap).

Phase 6
NVIDIA GPU: driver, CUDA & the container toolkit

Install the NVIDIA driver

The R595 production branch is current (as of mid-2026); newer feature branches exist (R610). Let ubuntu-drivers pick the right one, then reboot. Skip this entirely in WSL2 — the driver lives on the Windows host (Phase 9).

auto-install best driver
ubuntu-drivers list                 # see candidates (add --gpgpu on servers)
sudo ubuntu-drivers install         # autoinstall the recommended driver
# or pin explicitly, e.g.:  sudo apt install -y nvidia-driver-595
sudo reboot
verify after reboot
nvidia-smi    # header: Driver Version + max CUDA; table: per-GPU util/VRAM/processes
Reading nvidia-smi: the "CUDA Version" in the header is the maximum the driver supports — not what's installed. nvcc --version shows the actual toolkit. The toolkit must be ≤ the driver's max.
CUDA toolkit — usually you don't need the full install

CUDA Toolkit 13.3 is current (as of mid-2026). But for most PyTorch/TensorFlow work you don't install the system CUDA toolkit at all — the framework wheels bundle the CUDA runtime; you only need the driver.

install GPU PyTorch into a uv project (no system CUDA)
uv venv && uv add torch --torch-backend=auto
uv run python -c "import torch; print(torch.cuda.is_available(), torch.version.cuda)"

Install the full toolkit (nvcc, headers) only when compiling custom CUDA kernels or building from source.

NVIDIA Container Toolkit — GPUs inside Docker

To use --gpus all in containers, install the container toolkit and point Docker's runtime at it.

install + configure
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
  | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
  | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
prove it works
docker run --rm --gpus all nvidia/cuda:13.3.0-base-ubuntu24.04 nvidia-smi

Phase 7
Local LLMs with Ollama

Install Ollama & run a model locally

Ollama runs open-weight LLMs locally and serves an OpenAI-compatible REST API on localhost:11434 — handy for offline coding agents, RAG, and privacy-sensitive work.

install + use
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.1            # download weights
ollama run llama3.1            # interactive chat
ollama list                    # what's installed
# API: curl http://localhost:11434/api/generate -d '{"model":"llama3.1","prompt":"hi"}'
Model size (~Q4)Min RAM/VRAM (approx.)
7–8B~8 GB
13–14B~16 GB
33–34B~32 GB
70B~48–64 GB (community estimate)
Disk warning: models land in ~/.ollama/models and are multi-GB each. Audit with du -sh ~/.ollama; remove with ollama rm <model>. With no GPU, models run on CPU — slow but functional.

Phase 8
AI coding agents & editors

Install the agent CLIs

All run great on Ubuntu/WSL2. Need a paid account for the hosted models. Compare them in AI Coding Agents Compared.

Claude Code (Anthropic)
curl -fsSL https://claude.ai/install.sh | bash      # native installer (recommended)
# or via npm (needs Node 18+):  npm install -g @anthropic-ai/claude-code
claude        # launch in your project dir
OpenAI Codex CLI · Google Gemini CLI · aider
npm install -g @openai/codex          # OpenAI Codex CLI
npx @google/gemini-cli                 # Gemini CLI (needs Node 20+); or npm i -g @google/gemini-cli
uv tool install aider-chat             # aider (open-source, bring-your-own API key)
Prefer the native installers over sudo npm -g — they avoid permission issues and self-update. Keep API keys in your shell profile or a secrets manager, never in a committed file.
VS Code (+ Remote-SSH/WSL) or Cursor
VS Code (Microsoft apt repo)
wget -qO- https://packages.microsoft.com/keys/microsoft.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/microsoft.gpg
sudo tee /etc/apt/sources.list.d/vscode.sources >/dev/null <<'EOF'
Types: deb
URIs: https://packages.microsoft.com/repos/code
Suites: stable
Components: main
Architectures: amd64,arm64,armhf
Signed-By: /usr/share/keyrings/microsoft.gpg
EOF
sudo apt update && sudo apt install -y code

Cursor ships an official .deb/.rpm/AppImage (x64 + ARM64) from cursor.com/download; the .deb auto-updates via its own repo.

Develop on a remote/cloud GPU box with the Remote-SSH extension (or WSL extension for WSL2) — you edit locally while code, terminals, and the agent run on the remote machine.

Phase 9
WSL2 — Ubuntu on Windows 11

Install, systemd, and the rules that matter
PowerShell (Windows)
wsl --install -d Ubuntu-24.04     # installs WSL2 + Ubuntu
wsl --update                       # keep the WSL kernel current
wsl --shutdown                     # restart the WSL2 VM after config changes
/etc/wsl.conf (inside Ubuntu) — enable systemd if needed
[boot]
systemd=true
# then run `wsl --shutdown` in PowerShell and reopen
  • systemd is preset to true in the official Ubuntu WSL image, so systemctl works out of the box.
  • GPU just works — install the NVIDIA driver on Windows only; nvidia-smi runs in WSL via the host driver. Never install a Linux display driver inside WSL.
  • Filesystem performance: keep project files in the Linux ext4 FS (~/), not under /mnt/c/... — cross-OS access is dramatically slower (kills git status, installs, model loads).
  • localhost forwarding is on by default: a server on :8000 in WSL is reachable at localhost:8000 from Windows (unless you switch to mirrored networking).

Disk & upkeep (AI boxes fill up fast)

Model weights, datasets, Docker layers, and HF caches devour disk silently. Audit and reclaim regularly:

find & reclaim space
df -h /                                  # overall disk
du -sh ~/.cache/huggingface ~/.ollama ~/.cache/uv 2>/dev/null   # usual culprits
docker system df                          # docker images/volumes/build cache
docker system prune -a --volumes          # reclaim (destructive — removes unused)
sudo apt autoremove --purge -y && sudo apt clean
# move the HF cache to a bigger disk if needed:
echo 'export HF_HOME=/data/hf' >> ~/.zshrc
Set HF_HOME / OLLAMA_MODELS to a large data volume up front if your root partition is small (common on cloud GPU instances).

Common mistakes & anti-patterns

sudo pip install / --break-system-packages. Bypasses PEP 668 and can break apt itself. Always use a venv (uv).
One giant shared Python env. Dependency conflicts and silent breakage. One venv per project; commit uv.lock.
Installing dev tools via snap. Confinement blocks paths and toolchains (Docker, Node, browsers). Prefer apt repos / official installers.
Mixing conda base with system Python/apt. Shadowed binaries, broken imports. Disable auto_activate_base or skip conda.
Installing a Linux NVIDIA driver inside WSL. Breaks GPU passthrough. Driver belongs on the Windows host only.
Toolkit > driver CUDA version. A CUDA toolkit newer than the driver's max (from nvidia-smi) won't run. Match them.
Project files in /mnt/c on WSL. 5–20× slower I/O. Keep code in the Linux home dir.
Ignoring disk until training crashes. HF/Ollama/Docker caches fill root. Audit with docker system df + du -sh.
Developing on an interim (non-LTS) release. 9-month support; ML wheels target the LTS. Stay on an LTS.
sudo npm install -g. Permission hell (EACCES). Use nvm/fnm so globals live in your home dir.