The command line is where system administrators live. A well-configured terminal with the right tools can make the difference between spending twenty minutes on a task and spending twenty seconds. The standard GNU utilities that ship with every Linux distribution are powerful, but a generation of modern replacements has raised the bar for speed, usability, and information density. This guide covers the tools and techniques that consistently deliver the biggest productivity gains.

Terminal Multiplexer: tmux

If you SSH into servers without using tmux or screen, you are one network hiccup away from losing your session. tmux gives you persistent sessions, window management, and pane splitting. It is the single most important productivity tool for remote work.

# Start a new named session
tmux new-session -s work

# Detach from session: Ctrl+b, then d
# Reattach to session
tmux attach -t work

# List sessions
tmux ls

# Split pane horizontally: Ctrl+b, then %
# Split pane vertically: Ctrl+b, then "
# Navigate panes: Ctrl+b, then arrow keys
# Resize pane: Ctrl+b, then hold arrow keys (with -r flag)
# Kill pane: Ctrl+b, then x
# Create new window: Ctrl+b, then c
# Switch window: Ctrl+b, then 0-9

A minimal but effective ~/.tmux.conf:

# Use Ctrl+a instead of Ctrl+b (screen habit)
set -g prefix C-a
unbind C-b
bind C-a send-prefix

# Start window numbering at 1
set -g base-index 1
setw -g pane-base-index 1

# Enable mouse mode
set -g mouse on

# Increase history limit
set -g history-limit 50000

# Faster key repetition
set -s escape-time 0

# 256 color support
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",xterm-256color:Tc"

# Split panes with | and -
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

# Reload config
bind r source-file ~/.tmux.conf \; display "Config reloaded"

# Vi mode for copy
setw -g mode-keys vi

# Status bar
set -g status-style bg=colour235,fg=colour136
set -g status-left "#[fg=colour46]#S "
set -g status-right "#[fg=colour136]%Y-%m-%d %H:%M"
set -g status-interval 5

Fuzzy Finding: fzf

fzf is a general-purpose fuzzy finder that transforms how you interact with the shell. Install it, and it immediately improves Ctrl+R (history search), Ctrl+T (file search), and Alt+C (directory navigation).

# Install on most systems
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

# Arch Linux
sudo pacman -S fzf

# Usage examples
# Search command history with fuzzy matching
# Ctrl+R (much better than default reverse-i-search)

# Find and cd into directory
# Alt+C

# Insert file path into command
# Ctrl+T

# Pipe anything into fzf for selection
docker ps --format "{{.Names}}" | fzf | xargs docker logs -f

# Preview files while searching
fzf --preview 'bat --color=always --style=numbers --line-range=:500 {}'

# Kill a process interactively
ps aux | fzf | awk '{print $2}' | xargs kill

# Checkout git branch
git branch | fzf | xargs git checkout

# SSH into a server from your config
grep "^Host " ~/.ssh/config | awk '{print $2}' | fzf | xargs -I{} ssh {}
Tip: Set FZF_DEFAULT_COMMAND to use fd or ripgrep for dramatically faster file searching, especially in large repositories: export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'

Modern CLI Replacements

These tools are drop-in replacements for standard utilities with better defaults, color output, and performance:

Classic Tool Modern Replacement Key Improvement
grep ripgrep (rg) 10-100x faster, respects .gitignore, smart defaults
cat bat Syntax highlighting, line numbers, git integration
ls eza (formerly exa) Colors, icons, git status, tree view
find fd Faster, intuitive syntax, respects .gitignore
du ncdu / dust Interactive, visual, much faster analysis
top btop / htop Better visualization, mouse support, filtering
diff delta Side-by-side, syntax highlighting, git integration
sed sd Simpler syntax, string literals by default

ripgrep (rg)

# Search for a pattern recursively (default behavior)
rg "TODO|FIXME|HACK" --glob '*.go'

# Search with context
rg -C 3 "func main" --type go

# Count matches per file
rg -c "import" --type py

# Search only in specific file types
rg "database" --type yaml --type toml

# Show files that DON'T match
rg --files-without-match "test" --type py

# Replace text (preview)
rg "old_function" --replace "new_function" --type go

# Search compressed files
rg -z "error" /var/log/syslog.gz

bat

# View a file with syntax highlighting
bat config.yml

# Show only specific lines
bat --line-range 10:20 main.go

# Use as a pager for other commands
docker logs mycontainer 2>&1 | bat --language log

# Show diff with syntax highlighting
bat --diff file1.py file2.py

# Combine with fzf for preview
fzf --preview 'bat --color=always {}'

eza (ls replacement)

# Long listing with git status
eza -la --git

# Tree view
eza --tree --level=2

# Sort by modification time
eza -la --sort=modified

# Show only directories
eza -D

# Custom columns with icons
eza -la --icons --header --group

JSON and YAML Processing: jq and yq

Working with APIs and configuration files means constant JSON and YAML processing. jq and yq make this painless:

# jq basics
# Pretty-print JSON
curl -s https://api.github.com/repos/docker/compose | jq .

# Extract specific fields
docker inspect mycontainer | jq '.[0].NetworkSettings.IPAddress'

# Filter arrays
docker network inspect bridge | jq '.[0].Containers | to_entries[] | .value.Name'

# Transform JSON
cat data.json | jq '[.items[] | {name: .metadata.name, status: .status.phase}]'

# Conditional filtering
cat pods.json | jq '.items[] | select(.status.phase == "Running") | .metadata.name'

# CSV output
docker ps --format json | jq -r '[.Names, .Status, .Ports] | @csv'

# yq for YAML (Mike Farah's version)
# Read a value
yq '.services.app.image' docker-compose.yml

# Update a value
yq -i '.services.app.image = "myapp:2.0"' docker-compose.yml

# Convert YAML to JSON
yq -o json docker-compose.yml

# Merge YAML files
yq eval-all '. as $item ireduce ({}; . * $item)' base.yml override.yml

curl Tricks for API Work

# POST JSON data
curl -X POST http://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "admin", "email": "[email protected]"}'

# Follow redirects and show response headers
curl -fsSL -D - https://example.com -o /dev/null

# Download with progress bar and resume support
curl -C - -O https://example.com/large-file.tar.gz

# Timing information
curl -o /dev/null -s -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n" https://example.com

# Test multiple URLs in parallel
curl --parallel --parallel-immediate \
  https://api.example.com/health \
  https://api.example.com/status \
  https://api.example.com/version

# Use a response file for complex requests
curl -K request.cfg

# Upload a file
curl -F "[email protected]" https://upload.example.com/api/files

Shell History Optimization

Most shells default to a tiny history that is overwritten between sessions. Fix this:

# ~/.bashrc or ~/.zshrc
# Increase history size
export HISTSIZE=100000
export HISTFILESIZE=200000
export SAVEHIST=100000

# Append to history, don't overwrite
shopt -s histappend  # bash
setopt APPEND_HISTORY  # zsh

# Share history between sessions immediately
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"  # bash
setopt SHARE_HISTORY  # zsh

# Don't store duplicates or commands starting with space
export HISTCONTROL=ignoreboth  # bash
setopt HIST_IGNORE_DUPS  # zsh
setopt HIST_IGNORE_SPACE  # zsh

# Add timestamp to history
export HISTTIMEFORMAT="%F %T  "  # bash
setopt EXTENDED_HISTORY  # zsh

Alias Management

Well-organized aliases eliminate repetitive typing. Group them by function:

# ~/.aliases (sourced from .bashrc/.zshrc)

# Navigation
alias ..="cd .."
alias ...="cd ../.."
alias ....="cd ../../.."

# Modern replacements
alias ls="eza --icons"
alias ll="eza -la --icons --git"
alias cat="bat --paging=never"
alias grep="rg"
alias find="fd"
alias top="btop"
alias du="dust"

# Docker shortcuts
alias d="docker"
alias dc="docker compose"
alias dps="docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'"
alias dlogs="docker logs -f"
alias dexec="docker exec -it"
alias dclean="docker system prune -af --volumes"
alias dstats="docker stats --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}'"

# Git shortcuts
alias g="git"
alias gs="git status -sb"
alias gl="git log --oneline --graph --decorate -20"
alias gd="git diff"
alias gco="git checkout"
alias gcm="git commit -m"
alias gp="git push"
alias gpl="git pull --rebase"

# System administration
alias ports="ss -tulnp"
alias myip="curl -4 ifconfig.me"
alias meminfo="free -h && echo '---' && cat /proc/meminfo | head -5"
alias cpuinfo="lscpu | grep -E '^(Architecture|CPU\(s\)|Model name|Thread|Core)'"
alias journal="journalctl -f -n 50"
alias syslog="tail -f /var/log/syslog"

# Safety nets
alias rm="rm -i"
alias cp="cp -i"
alias mv="mv -i"

# Quick edit
alias hosts="sudo vim /etc/hosts"
alias sshconfig="vim ~/.ssh/config"

Zsh vs Fish vs Bash

Feature Bash Zsh Fish
Available everywhere Yes (POSIX default) Needs installation Needs installation
Tab completion Basic (improvable) Excellent (context-aware) Excellent (auto-suggest)
Syntax highlighting No (plugin needed) Plugin (zsh-syntax-highlighting) Built-in
Auto-suggestions No Plugin (zsh-autosuggestions) Built-in
POSIX compatible Yes Mostly No
Script compatibility Universal High Fish-specific syntax
Plugin ecosystem Limited Oh My Zsh, Zinit Fisher, Oh My Fish
Configuration complexity Simple Moderate (can be complex) Simple (web-based config)

Recommendation: Use Zsh with a minimal plugin set (zsh-autosuggestions, zsh-syntax-highlighting, and fzf integration) for the best balance of features and compatibility. Fish is excellent for interactive use but its non-POSIX syntax creates friction when writing portable scripts.

Dotfiles Management

Your dotfiles are your portable development environment. Manage them properly:

# Option 1: Bare git repository (simplest, no symlinks)
git init --bare $HOME/.dotfiles
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles config --local status.showUntrackedFiles no

# Add files
dotfiles add ~/.bashrc ~/.tmux.conf ~/.vimrc
dotfiles commit -m "Initial dotfiles"
dotfiles remote add origin [email protected]:user/dotfiles.git
dotfiles push -u origin main

# Clone on a new machine
git clone --bare [email protected]:user/dotfiles.git $HOME/.dotfiles
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles checkout

# Option 2: GNU Stow (symlink manager)
# ~/.dotfiles/
#   tmux/.tmux.conf
#   vim/.vimrc
#   zsh/.zshrc
cd ~/.dotfiles
stow tmux vim zsh  # Creates symlinks in $HOME

htop and btop: Process Management

# htop keyboard shortcuts
# F5: Tree view
# F6: Sort by column
# F4: Filter by name
# F9: Kill process
# u: Filter by user
# H: Hide user threads
# t: Tree view toggle
# /: Search

# btop (more modern alternative)
# Features: GPU monitoring, disk I/O, network per process
# Configurable themes and layouts

ncdu: Disk Usage Analysis

# Interactive disk usage analyzer
ncdu /var/log

# Exclude certain directories
ncdu / --exclude /proc --exclude /sys --exclude /dev

# Export scan results for later viewing
ncdu -o scan.json /home
ncdu -f scan.json  # View later, even on different machine

# Faster alternative: dust
dust -d 2 /var  # Show top 2 levels of /var

Every minute spent configuring your terminal environment pays back tenfold over a career of system administration. The tools listed here are not about chasing the latest trend; they represent genuine productivity multipliers that have proven their value in daily use. Start with tmux and fzf if you adopt nothing else, then gradually integrate the modern replacements as they become natural. Your terminal should feel like a precision instrument, not a blunt tool.

For managing Docker infrastructure specifically, combining these CLI tools with a platform like usulnet gives you the best of both worlds: the power and flexibility of the command line for ad-hoc tasks, and a web interface for monitoring and management that does not require SSH access.