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.
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
| Task | Command | Notes |
|---|---|---|
| Full system update | sudo apt update && sudo apt full-upgrade -y | full-upgrade handles dependency changes. |
| Install uv (Python toolchain) | curl -LsSf https://astral.sh/uv/install.sh | sh | Replaces pip+venv+pyenv+pipx. v0.11.x. |
| New project env | uv venv && uv add numpy torch | Per-project venv + lockfile. Never sudo pip. |
| Run a script in its env | uv run train.py | Auto-syncs deps first. |
| Pin a Python version | uv python install 3.12 | No pyenv needed. |
| GPU status | nvidia-smi | Header shows driver + max CUDA; table shows VRAM/process. |
| GPU inside Docker | docker run --rm --gpus all ubuntu nvidia-smi | Needs nvidia-container-toolkit (Phase 6). |
| Run a local LLM | ollama run llama3.1 | Serves API on localhost:11434. |
| Install Claude Code | curl -fsSL https://claude.ai/install.sh | bash | Or npm i -g @anthropic-ai/claude-code (Node 18+). |
| Reclaim Docker disk | docker system df · docker system prune -a | Images/volumes fill disks fast on ML boxes. |
| What's listening | sudo ss -tulpn | Open ports + owning process. |
| Which Ubuntu am I on | lsb_release -a · cat /etc/os-release | Confirm LTS codename. |
Phase 0
Choose your Ubuntu (and where to run it)
Which release? Always an LTS
| Release | Status (mid-2026) | Default Python | Use it? |
|---|---|---|---|
| 26.04 LTS "Resolute Raccoon" | Released 2026-04-23 | 3.14 | ✅ Newest LTS — best for a fresh box |
| 24.04 LTS "Noble Numbat" (24.04.4) | Point release Feb 2026 | 3.12 | ✅ Proven, widest tooling support |
| 25.10 "Questing Quokka" (interim) | EOL ~2026-07-09 | 3.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.
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
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-commonbuild-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).
# 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 ghSSH key + git identity for GitHub
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 logingh 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)
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)"' >> ~/.zshrcFast CLI tools (mind the renamed binaries)
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| Tool | apt package | Binary you type | Gotcha |
|---|---|---|---|
| ripgrep | ripgrep | rg | package ≠ command name |
| fd | fd-find | fdfind | symlink to fd (name clash) |
| bat | bat | batcat | alias bat=batcat |
| eza (ls) | universe or gierens.de repo | eza | successor to deprecated exa |
| fzf / zoxide / jq / btop | same | same | — |
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.
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.
curl -LsSf https://astral.sh/uv/install.sh | sh
# restart your shell, then confirm:
uv --versionuv 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 installuv.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)
sudo apt install -y pipx
pipx ensurepath # adds ~/.local/bin to PATH; restart shell
pipx install httpie # each app gets its own venvsudo add-apt-repository -y ppa:deadsnakes/ppa
sudo apt update && sudo apt install -y python3.11 python3.11-venv python3.11-devbase 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).
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.xcurl -fsSL https://fnm.vercel.app/install | bash
fnm install --lts && fnm use lts/latestPhase 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.
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-pluginsudo usermod -aG docker "$USER"
newgrp docker # or log out/in
docker run hello-world
docker compose version # Compose v2 = `docker compose` (space), not docker-composedocker 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).
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 rebootnvidia-smi # header: Driver Version + max CUDA; table: per-GPU util/VRAM/processesnvidia-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.
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.
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 dockerdocker run --rm --gpus all nvidia/cuda:13.3.0-base-ubuntu24.04 nvidia-smiPhase 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.
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) |
~/.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.
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 dirnpm 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)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
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 codeCursor ships an official .deb/.rpm/AppImage (x64 + ARM64) from cursor.com/download; the .deb auto-updates via its own repo.
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
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[boot]
systemd=true
# then run `wsl --shutdown` in PowerShell and reopen- systemd is preset to
truein the official Ubuntu WSL image, sosystemctlworks out of the box. - GPU just works — install the NVIDIA driver on Windows only;
nvidia-smiruns 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 (killsgit status, installs, model loads). - localhost forwarding is on by default: a server on
:8000in WSL is reachable atlocalhost:8000from 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:
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' >> ~/.zshrcHF_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).uv.lock.base with system Python/apt. Shadowed binaries, broken imports. Disable auto_activate_base or skip conda.nvidia-smi) won't run. Match them./mnt/c on WSL. 5–20× slower I/O. Keep code in the Linux home dir.docker system df + du -sh.sudo npm install -g. Permission hell (EACCES). Use nvm/fnm so globals live in your home dir.