A Sysadmin's Daily Driver Setup
Build a complete sysadmin terminal workflow with tmux, Neovim, fzf, atuin, version-controlled dotfiles, clean multi-server SSH, and audit-ready habits.
Before you start
- ▸A non-root user with sudo privileges
- ▸Git 2.28 or later installed
- ▸Basic familiarity with a terminal and shell RC files
- ▸SSH key pair already generated (or willingness to create one)
A polished terminal environment is the difference between a slow, forgetful workflow and one that keeps up with your thoughts. This guide walks through building a cohesive daily-driver setup: persistent sessions with tmux, a capable editor, fuzzy-finding everything, a searchable shell history, dotfile version control, clean multi-server SSH, and habits that make audit trails a natural by-product rather than an afterthought.
1. tmux — Persistent, Organised Sessions
tmux gives you persistent sessions that survive SSH disconnects and a window/pane model that maps naturally to multi-task work.
Install
# Debian / Ubuntu
sudo apt install tmux
# Fedora / RHEL family
sudo dnf install tmux
# Arch
sudo pacman -S tmux
Minimal ~/.tmux.conf
cat > ~/.tmux.conf << 'EOF'
set -g prefix C-a
unbind C-b
bind C-a send-prefix
set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",*256col*:Tc"
# Sane pane splits
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Vim-style pane navigation
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# Reload config
bind r source-file ~/.tmux.conf \; display "Reloaded"
# Status bar
set -g status-right '%H:%M %d-%b'
EOF
Start a named session at the top of every work day with a meaningful name — this pays off when you have six sessions open:
tmux new-session -s ops
# Attach later:
tmux attach -t ops
# List sessions:
tmux ls
Audit note: tmux can pipe all pane output to a log file. Enable this per session when you need a full record:
# Inside tmux, prefix then:
:pipe-pane -o 'cat >> ~/logs/session-%Y%m%d.log'
2. Neovim — A Modern Editor Without the Config Rabbit Hole
Neovim is now the pragmatic choice: maintained, LSP-native, Lua-configured. Install it from upstream rather than distro repos to avoid stale versions.
# Ubuntu (PPA for a current release)
sudo add-apt-repository ppa:neovim-ppa/stable
sudo apt update && sudo apt install neovim
# Fedora (usually current in default repos)
sudo dnf install neovim
# Arch
sudo pacman -S neovim
For sysadmin work, a lean config beats a heavy distribution like NvChad when you need to reproduce it on remote servers quickly. A sensible starting point:
mkdir -p ~/.config/nvim
cat > ~/.config/nvim/init.lua << 'EOF'
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.expandtab = true
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.termguicolors = true
vim.opt.clipboard = "unnamedplus"
vim.g.mapleader = " "
-- Quick save / quit
vim.keymap.set("n", "w", ":w")
vim.keymap.set("n", "q", ":q")
EOF
Add plugins only when the need is clear: lazy.nvim is the current standard plugin manager. Keep it as a separate stanza in your dotfiles so servers that don't need it stay lean.
3. fzf — Fuzzy Find Everything
fzf replaces Ctrl-R history search, powers file selection, and integrates with git, kill, ssh, and almost anything that produces a list.
# Debian / Ubuntu
sudo apt install fzf
# Fedora / RHEL
sudo dnf install fzf
# Arch
sudo pacman -S fzf
Source the shell integration. For bash, add to ~/.bashrc; for zsh, ~/.zshrc:
# bash — path may vary by distro, check with: dpkg -L fzf | grep completion
source /usr/share/doc/fzf/examples/key-bindings.bash
source /usr/share/doc/fzf/examples/completion.bash
# Or with the upstream git install:
# source ~/.fzf.bash
Key bindings after sourcing: Ctrl-R fuzzy-searches history, Ctrl-T fuzzy-inserts a file path, Alt-C fuzzy-changes directory. Add useful defaults via environment variables:
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border --inline-info'
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
fd needs to be installed separately (apt install fd-find / dnf install fd-find / pacman -S fd) and is dramatically faster than find for interactive use.
4. Atuin — Shell History Worth Having
Atuin stores your shell history in a local SQLite database (with optional encrypted sync), records exit codes, working directories, and durations. This alone transforms Ctrl-R into a serious tool.
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh
Then add to your shell RC file:
# bash
echo 'eval "$(atuin init bash)"' >> ~/.bashrc
# zsh
echo 'eval "$(atuin init zsh)"' >> ~/.zshrc
Reload the shell, then import existing history:
atuin import auto
Atuin replaces Ctrl-R with a full-screen TUI. Filter by host, directory, or exit code. For audit purposes, the fact that it records exit codes means you can quickly surface all failed commands from a maintenance window:
atuin search --exit 1 --after '2024-01-01'
5. Dotfiles — Version Controlled, Deployable in 60 Seconds
The bare Git repository pattern avoids symlink managers and works everywhere Git does.
git init --bare $HOME/.dotfiles
alias dot='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dot config --local status.showUntrackedFiles no
# Add the alias to your shell RC so it persists
echo "alias dot='git --git-dir=\$HOME/.dotfiles/ --work-tree=\$HOME'" >> ~/.bashrc
# Track files
dot add ~/.bashrc ~/.tmux.conf ~/.config/nvim/init.lua
dot commit -m "initial dotfiles"
dot remote add origin [email protected]:youruser/dotfiles.git
dot push -u origin main
To deploy on a new machine:
git clone --bare [email protected]:youruser/dotfiles.git $HOME/.dotfiles
alias dot='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dot checkout
dot config --local status.showUntrackedFiles no
Security reminder: never commit files containing secrets. Use ~/.gitignore to exclude .ssh/id_*, credential files, and anything under /etc you might accidentally stage.
6. Multi-Server SSH — Clean, Fast, Auditable
A well-structured ~/.ssh/config is worth more than any wrapper script.
cat >> ~/.ssh/config << 'EOF'
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
Host bastion
HostName 203.0.113.10
User deploy
ForwardAgent yes
Host prod-web-*
User deploy
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519_prod
Host prod-web-01
HostName 10.0.1.11
Host prod-web-02
HostName 10.0.1.12
EOF
chmod 600 ~/.ssh/config
Use ed25519 keys everywhere; retire RSA 2048 keys on any new setup. Generate once:
ssh-keygen -t ed25519 -C "$(whoami)@$(hostname)-$(date +%Y%m%d)" -f ~/.ssh/id_ed25519
For bulk operations across many hosts, pair SSH config with a tool like pssh (parallel-ssh) rather than writing fragile for-loops:
sudo apt install pssh # debian/ubuntu; package is 'parallel-ssh' on some distros
pssh -H "prod-web-01 prod-web-02" -l deploy -i 'uptime'
7. Audit-Friendly Workflow Habits
Tools only help when paired with repeatable habits. These are low-friction and cover the most common audit requirements.
- Script everything with
script(1)or tmux logging during planned maintenance.script -a ~/logs/maint-$(date +%Y%m%d-%H%M).logrecords a full typescript of your terminal session, including timing data with-t. - Use
sudo -isparingly; prefersudo commandso each privileged action is individually logged tojournaldand syslog. Check withjournalctl _COMM=sudo -n 50. - Tag tmux windows by task:
prefix + ,renames the window. A window nameddb-migrationis self-documenting in any screen recording. - Write a two-line comment before any destructive command — even in an interactive session. Atuin captures it in history; your future self and your auditors will both appreciate it.
Verification
After setting everything up, run through this quick checklist:
# Confirm versions
tmux -V
nvim --version | head -1
fzf --version
atuin --version
# Confirm dotfiles remote is set
dot remote -v
# Confirm SSH config is valid (dry-run connect)
ssh -G prod-web-01 | grep hostname
Troubleshooting
- tmux colours look wrong: Make sure
$TERMoutside tmux isxterm-256colororalacritty/foot(Wayland). Addexport TERM=xterm-256colorto your RC file only if your terminal doesn't set it correctly — don't hardcode it inside tmux itself. - fzf Ctrl-R not overriding atuin: Atuin takes over
Ctrl-Rwhen its init runs. Source atuin after fzf, or just use atuin for history and fzf for file/path operations. - dot checkout fails on new machine: Git will refuse to overwrite existing files. Back up or remove the conflicting files (usually a default
.bashrc) then re-rundot checkout. - SSH ProxyJump not working on older servers:
ProxyJumprequires OpenSSH 7.3+. On older targets, fall back toProxyCommand ssh bastion -W %h:%pin the SSH config block.
Frequently asked questions
- Should I use vim or Neovim on remote servers where I don't have my dotfiles?
- Keep a minimal vimrc in your dotfiles and set EDITOR=vim on those hosts. Neovim is for your main workstation; knowing both lets you work anywhere without frustration.
- Is it safe to use atuin's optional cloud sync for work history?
- Atuin encrypts history client-side before syncing, so the server never sees plaintext. That said, many employers prohibit syncing command history to third-party services — check your security policy and use local-only mode if in doubt.
- Why a bare Git repo for dotfiles instead of GNU Stow or a dedicated tool?
- The bare-repo approach has zero runtime dependencies beyond Git itself, which is present on every server you'll ever touch. Stow is fine but adds a package dependency; dedicated tools like chezmoi add even more complexity.
- How do I handle per-machine differences in my dotfiles, like a different $PATH on macOS versus Linux?
- Use conditional blocks in your shell RC: check $OSTYPE or use 'if command -v brew &>/dev/null; then' style guards. Keep machine-specific overrides in a sourced ~/.bashrc.local file that is listed in .gitignore.
- Does tmux work well under Wayland?
- tmux itself is terminal-agnostic and works fine. The clipboard integration ('set -g clipboard on' with OSC 52 support) works in modern Wayland terminals like foot and alacritty but may need the xclip/wl-clipboard fallback in others.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.