A Complete Linux Developer Workstation Setup
Set up a complete Linux developer workstation: distro selection, terminal stack, language version managers, Docker, VS Code, and reproducible dotfiles.
Before you start
- ▸A fresh or existing Linux installation with sudo access
- ▸Internet connectivity to download packages and language runtimes
- ▸A GitHub or GitLab account for remote dotfile storage
Setting up a Linux developer workstation from scratch involves more than installing a few packages. Choices made early—distro, shell, runtime managers—compound into hours saved or lost later. This guide walks through each layer systematically, from distro selection to dotfile management, so you end up with a reproducible, sane environment.
Choosing a Distro
For most developers, the practical choice comes down to three families:
- Ubuntu 24.04 LTS / Debian 12: Widest tool compatibility, largest community, predictable release cadence. Ubuntu ships newer kernels via HWE stacks. Best default for teams that need consistency.
- Fedora 40+: Ships upstream software fast—latest kernels, Wayland-first, Pipewire by default. SELinux enforcing out of the box. Good if you eventually target RHEL/Rocky in production.
- Arch Linux: Rolling release; you control exactly what's installed. Requires more upfront effort but produces a lean, well-understood system. The AUR covers almost every developer tool.
All three work well. Pick Ubuntu/Debian if you want minimal friction; pick Fedora if you want bleeding-edge without full DIY; pick Arch if you want to own every layer.
Base System Updates and Essential Packages
Before anything else, fully update the system and install a common baseline.
Debian / Ubuntu
sudo apt update && sudo apt upgrade -y
sudo apt install -y \
build-essential curl wget git gnupg ca-certificates \
unzip zip tar xz-utils jq ripgrep fd-find bat tree htop
Fedora
sudo dnf upgrade -y
sudo dnf install -y \
@development-tools curl wget git gnupg2 ca-certificates \
unzip zip tar xz jq ripgrep fd-find bat tree htop
Arch
sudo pacman -Syu
sudo pacman -S --needed \
base-devel curl wget git gnupg unzip zip tar xz \
jq ripgrep fd bat tree htop
Note: On Ubuntu, fd-find installs as fdfind; symlink it with ln -s $(which fdfind) ~/.local/bin/fd.
Terminal Stack
Shell: Zsh with a Plugin Manager
Bash is fine; Zsh with good plugins is noticeably faster to work in. Install Zsh and set it as your default shell, then add Sheldon (a fast, TOML-configured plugin manager) or the simpler Oh My Zsh if you prefer convention over configuration.
# Install Zsh (all distros have it)
sudo apt install -y zsh # Debian/Ubuntu
sudo dnf install -y zsh # Fedora
sudo pacman -S zsh # Arch
chsh -s $(which zsh)
# Install Sheldon plugin manager
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/rossmacarthur/sheldon/releases/latest/download/sheldon-installer.sh \
| sh
Essential plugins to declare in ~/.sheldon/plugins.toml: zsh-autosuggestions, zsh-syntax-highlighting, and zsh-completions. Log out and back in for the shell change to take effect.
Terminal Emulator
On Wayland, Ghostty or Alacritty are GPU-accelerated and work natively without XWayland. WezTerm is a strong cross-platform choice with built-in multiplexing. Avoid outdated terminals like xterm for daily driving.
# WezTerm on Debian/Ubuntu
curl -fsSL https://apt.fury.io/wez/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/wezterm.gpg
echo "deb [signed-by=/usr/share/keyrings/wezterm.gpg] https://apt.fury.io/wez/ * *" \
| sudo tee /etc/apt/sources.list.d/wezterm.list
sudo apt update && sudo apt install -y wezterm
# Alacritty on Fedora
sudo dnf install -y alacritty
# Alacritty on Arch
sudo pacman -S alacritty
Multiplexer: tmux or Zellij
tmux is ubiquitous and scriptable. Zellij ships with a discoverable UI and WebAssembly plugin support—good if you find tmux key bindings opaque.
sudo apt install -y tmux # Debian/Ubuntu
sudo dnf install -y tmux # Fedora
sudo pacman -S tmux # Arch
Language Runtime Managers
Never install language runtimes solely through your distro's package manager for development use—you'll need multiple versions. Use dedicated version managers instead.
Node.js — fnm
fnm (Fast Node Manager) is written in Rust, installs in milliseconds, and is compatible with .nvmrc files.
curl -fsSL https://fnm.vercel.app/install | bash
# Add to ~/.zshrc or ~/.bashrc:
eval "$(fnm env --use-on-cd)"
# Install and use a Node version
fnm install --lts
fnm use lts-latest
Python — pyenv
curl https://pyenv.run | bash
# Add to shell profile:
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
pyenv install 3.12.4
pyenv global 3.12.4
Rust — rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
Go
Download the official tarball rather than using distro packages—distros lag behind upstream significantly.
# Replace 1.22.5 with the current release from go.dev/dl
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.zshrc
Docker and Containers
Install Docker Engine directly from Docker's own repository—not the distro-packaged docker.io, which is often outdated.
Debian / Ubuntu
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Fedora
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Post-install: run Docker without sudo
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Log out and back in, then verify:
docker run --rm hello-world
For rootless Docker (strongly recommended on shared machines), see the official rootless setup guide.
VS Code and IDEs
VS Code
# Debian/Ubuntu
wget -qO- https://packages.microsoft.com/keys/microsoft.asc \
| gpg --dearmor | sudo tee /usr/share/keyrings/microsoft.gpg > /dev/null
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.gpg] \
https://packages.microsoft.com/repos/code stable main" \
| sudo tee /etc/apt/sources.list.d/vscode.list
sudo apt update && sudo apt install -y code
# Fedora
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'
sudo dnf install -y code
# Arch (AUR)
yay -S visual-studio-code-bin
Wayland note: Launch VS Code with native Wayland rendering to avoid blurry fonts under XWayland: add --ozone-platform=wayland --enable-features=WaylandWindowDecorations to your .desktop launcher or shell alias.
JetBrains IDEs
Use JetBrains Toolbox to install and update any JetBrains IDE. Download the tarball from jetbrains.com/toolbox-app, extract it, and run the binary—it handles everything else including systemd integration for auto-updates.
Neovim
If you prefer a terminal editor, install Neovim 0.10+ and bootstrap with LazyVim or kickstart.nvim rather than configuring from scratch.
sudo apt install -y neovim # Ubuntu 24.04 ships 0.9+; use PPA for 0.10
sudo dnf install -y neovim # Fedora ships current
sudo pacman -S neovim # Arch always current
Dotfile Management
Dotfiles are the single highest-leverage investment in a developer workstation. A good system lets you reproduce your environment on any machine in minutes.
The bare Git repository approach requires no extra tooling:
# Initialize once
git init --bare $HOME/.dotfiles
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles config --local status.showUntrackedFiles no
# Track files
dotfiles add ~/.zshrc ~/.gitconfig ~/.config/wezterm/wezterm.lua
dotfiles commit -m "Initial dotfiles"
dotfiles remote add origin [email protected]:youruser/dotfiles.git
dotfiles push -u origin main
# Bootstrap on a new machine
git clone --bare [email protected]:youruser/dotfiles.git $HOME/.dotfiles
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles checkout
dotfiles config --local status.showUntrackedFiles no
Alternatively, chezmoi adds templating (useful when config differs between machines) and encrypts secrets. Install it with sh -c "$(curl -fsLS get.chezmoi.io)".
Verification
Run these checks to confirm the core stack is healthy:
echo $SHELL # should be /usr/bin/zsh or /bin/zsh
node --version # e.g., v20.15.0
python3 --version # e.g., Python 3.12.4
rustc --version # e.g., rustc 1.79.0
go version # e.g., go1.22.5 linux/amd64
docker info | grep -i server # should return server details, not permission errors
code --version # VS Code version string
Troubleshooting
- Docker permission denied: You haven't logged out since adding yourself to the
dockergroup. Runnewgrp dockerin the current session as a temporary fix, then log out properly. - pyenv: python version not found: Install build dependencies first. On Debian/Ubuntu:
sudo apt install -y libssl-dev libbz2-dev libreadline-dev libsqlite3-dev libffi-dev zlib1g-dev. - VS Code blurry on Wayland: Add the Wayland flags described above; also check that
WAYLAND_DISPLAYis set in your environment (echo $WAYLAND_DISPLAY). - fnm / pyenv not found after install: The installers append to
~/.bashrc. If you switched to Zsh, manually add the init lines to~/.zshrcand reload withsource ~/.zshrc. - Slow Zsh startup: Profile with
zsh -i -c exittiming orzprof. Common culprits are NVM (replace with fnm), heavy Oh My Zsh plugins, or pyenv init in the wrong place.
Frequently asked questions
- Should I use the distro-packaged Docker or install from Docker's repository?
- Install from Docker's own repository. Distro packages (like Ubuntu's docker.io) lag months behind upstream and may lack features like the compose plugin. The official repo ensures you get current releases.
- Why use fnm or pyenv instead of just apt/dnf for language runtimes?
- Distro packages give you one version system-wide and often trail upstream by 6-18 months. Version managers let you switch between Node 18 and 22, or Python 3.10 and 3.12, per project without conflicts.
- Is a bare Git repository approach for dotfiles safe to use on multiple machines with different configs?
- It works well for identical configs. If your machines differ (different hostnames, OS-specific paths), chezmoi's templating system handles per-machine variation and also encrypts secrets—it's worth the extra learning curve.
- Does VS Code run natively on Wayland or does it use XWayland?
- By default VS Code uses XWayland, which can cause blurry rendering on HiDPI displays. Pass --ozone-platform=wayland to the binary or add it to your .desktop file's Exec line to get native Wayland rendering.
- What is the minimum a developer should track in dotfiles?
- At minimum: ~/.zshrc or ~/.bashrc, ~/.gitconfig, and your editor config (e.g., ~/.config/nvim or VS Code's settings.json). Add terminal emulator configs and tmux.conf next. SSH config and keys should be handled separately with proper secret management.
Related guides
How to Back Up Your Linux System
Learn how to back up your Linux system using Timeshift, rsync, Borg, and Restic — then tie it all together with a practical 3-2-1 backup routine.
How to Choose a Linux Distribution
Match a Linux distro to your real needs — desktop, server, rolling vs LTS, hardware quirks, and package ecosystems — without wading through marketing noise.
How to Dual-Boot Linux and Windows
Shrink the Windows partition, install Linux without breaking the bootloader, configure GRUB, and handle Secure Boot — all in the correct order.
10 Things to Do After Installing Linux
Ten essential post-install steps for any Linux desktop: updates, drivers, firewall, codecs, backups, SSH hardening, and service cleanup — all with modern commands.