Cheatsheets — Linux, Docker, Kubernetes & the Daily Toolbelt
Field guide for what an SDE types every day. Each section is a quick-scan table plus the gotchas that bite in production. Code blocks copy on click.
1 · Linux — Files & Permissions
Filesystem moves, listing, and the chmod bits that confuse everyone.
Navigation & listing
Command
Purpose
ls -lah
Long, human-sized, all (incl. dotfiles).
ls -lt | head
Newest files first.
tree -L 2 -a
Two levels deep, all files.
cd -
Jump back to previous dir.
pushd /x / popd
Dir stack — explore then return.
readlink -f file
Resolve symlink to absolute path.
File ops
Command
Purpose
cp -a src dst
Archive: preserve perms, links, timestamps.
rsync -avhP src/ dst/
Resumable, progress, trailing slash matters.
mv -i a b
Interactive — prompt before overwrite.
ln -s target link
Symbolic link.
stat file
Inode, perms, atime/mtime/ctime.
du -sh * / du -hd1
Per-entry size of cwd.
df -h
Disk free per mount.
ncdu /
Interactive disk usage explorer.
Permissions & ownership
# numeric: r=4 w=2 x=1 user|group|other
chmod 755 script.sh # rwxr-xr-x
chmod 600 ~/.ssh/id_rsa # rw------- (private keys MUST be 600)
chmod -R u+rwX,go-w dir # capital X = exec only on dirs / already-exec files
chown -R user:group dir
umask 022 # default mask: new files = 644, dirs = 755
sudo chmod g+s shareddir # setgid on dir = new files inherit group
Find & locate
find . -type f -name "*.log" -mtime -1 # modified in last 24h
find . -type f -size +100M # bigger than 100MB
find . -name node_modules -type d -prune -exec rm -rf {} +
find . -name "*.tmp" -delete
fd 'pattern' # rust find, faster
locate filename # uses prebuilt db (mlocate)
sed -n '5,10p' file # print lines 5-10
sed -i 's/foo/bar/g' file # in-place replace (BSD/macOS: sed -i '' ...)
sed -i.bak 's/old/new/g' file # safer: keep .bak
sed '/^$/d' file # delete blank lines
sed 's/[[:space:]]*$//' file # trim trailing whitespace
awk — column tool
awk '{print $1, $NF}' file # first + last field
awk -F: '$3 >= 1000 {print $1}' /etc/passwd # custom delim
awk '{sum+=$1} END {print sum}' nums # sum column
ps aux | awk '$3 > 50.0' # rows where CPU > 50%
awk 'NR==1 || /ERROR/' log # header + matches
cut · sort · uniq · tr · wc
cut -d, -f1,3 file.csv # CSV columns 1 & 3
sort -k2 -n file # numeric sort by col 2
sort | uniq -c | sort -rn # frequency table (top hitters)
tr 'A-Z' 'a-z' < file # lowercase
wc -l file # line count
paste a b # side-by-side merge
xargs -n1 -P4 -I{} curl {} # parallel: 4 jobs
3 · Processes & System
Who's running what, and who's eating the box.
List & signal
Command
Purpose
ps aux
Every process, BSD format (CPU, MEM, START).
ps -ef --forest
Tree view — see parent/child.
pgrep -af node
PIDs whose cmdline matches.
pkill -9 -f myapp
Kill by full cmdline pattern.
kill -TERM <pid>
15 = polite shutdown. Default.
kill -KILL <pid>
9 = uncatchable. Last resort.
kill -HUP <pid>
1 = reload config (nginx, sshd).
nohup cmd &
Detach from terminal; survives logout.
disown -h %1
Detach existing job from shell.
Live monitors
top # classic
htop # color, scroll, tree (F5)
btop # modern eye-candy
iotop # disk IO per process
iftop # network bandwidth per host
glances # aggregate dashboard
free -h # RAM/swap snapshot
uptime # load avg over 1/5/15 min
vmstat 1 # one sample/sec: cpu, io, swap
iostat -xz 1 # per-device IO stats
What's open / what's listening
ss -tulnp # TCP/UDP, listening, with PID (modern)
netstat -tulnp # legacy alias
lsof -i :8080 # who has port 8080
lsof -p <pid> # files+sockets opened by pid
lsof +D /var/log # who has files under /var/log open
fuser -v /mnt/data # processes using a path
systemd
systemctl status nginx
systemctl start|stop|restart|reload nginx
systemctl enable --now nginx # start now + on boot
systemctl list-units --failed
journalctl -u nginx -f # follow service logs
journalctl --since "10 min ago"
journalctl -p err -b # errors since last boot
4 · Networking — diagnose in 30s
Layer probes
Command
Layer / Purpose
ip a / ip r
L3: addrs and routing table.
ping -c 4 host
L3: reachable? RTT?
traceroute host / mtr host
L3: path. mtr = continuous.
dig +short example.com
DNS: A record only.
dig @8.8.8.8 MX example.com
DNS: query specific server.
host -a example.com
DNS: every record type.
nslookup example.com
DNS: legacy but on every box.
nc -zv host 443
L4: port reachable from here?
curl -I https://x.com
L7: headers only.
tcpdump -i any -n port 443
Packet capture on the wire.
Common debug flow
# 1. DNS resolves?
dig +short api.svc.local
# 2. Route exists?
ip route get 10.0.0.5
# 3. TCP port open from here?
nc -zv api.svc.local 443
# 4. TLS handshake works?
openssl s_client -connect api.svc.local:443 -servername api.svc.local
# 5. App layer responds?
curl -v https://api.svc.local/health
5 · Bash Scripting Essentials
The strict-mode preamble, parameter expansion, and the loops you actually use.
Strict-mode preamble
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# -e exit on error
# -u fail on unset variables
# -o pipefail fail if any pipe stage fails
# IFS safer word-splitting (no spaces)
Variables & parameter expansion
name="alice"
echo "${name^^}" # ALICE (uppercase)
echo "${name,,}" # alice (lowercase)
echo "${name:-anon}" # default if unset/empty
echo "${name:+set}" # 'set' only if name has value
file="/var/log/app.log"
echo "${file##*/}" # app.log (basename)
echo "${file%/*}" # /var/log (dirname)
echo "${file%.log}.bak" # swap extension
arr=(a b c); echo "${#arr[@]}" # 3
Conditionals & loops
if [[ -f "$f" ]]; then echo "exists"; fi
[[ -d "$d" ]] && echo "dir"
[[ "$x" =~ ^[0-9]+$ ]] && echo "numeric"
[[ -z "$s" ]] # empty
[[ -n "$s" ]] # non-empty
for f in *.log; do gzip "$f"; done
for i in {1..5}; do echo $i; done
while read -r line; do echo "$line"; done < file
case "$1" in start) ... ;; stop) ... ;; *) ... ;; esac
Functions, traps, args
cleanup() { rm -f /tmp/lock; }
trap cleanup EXIT INT TERM
main() {
local input="${1:?usage: main <input>}"
echo "processing $input"
}
main "$@"
6 · SSH, scp, rsync
Keys & config
ssh-keygen -t ed25519 -C "you@host" # modern, fast, small
ssh-copy-id user@host # push pubkey
ssh-add ~/.ssh/id_ed25519 # load into agent
# ~/.ssh/config
Host bastion
HostName 1.2.3.4
User ubuntu
IdentityFile ~/.ssh/prod_ed25519
Host prod-*
ProxyJump bastion
User ubuntu
Common usage
ssh -A user@host # forward agent (carefully)
ssh -L 8080:localhost:80 host # local fwd: localhost:8080 → host:80
ssh -R 9000:localhost:9000 host # reverse fwd: expose your service to host
ssh -D 1080 host # SOCKS proxy on :1080
ssh host 'tail -f /var/log/syslog'
scp file user@host:/path/
rsync -avhP --delete src/ user@host:/dst/ # mirror, with progress
docker volume create pgdata
docker run -v pgdata:/var/lib/postgresql/data postgres
docker run -v "$PWD":/app -w /app node:20 npm test # bind mount
docker volume ls
docker volume prune
docker network create app-net
docker run --network app-net --name db postgres
docker run --network app-net --name api myapp # api can resolve "db"
docker network inspect app-net
Cleanup — reclaim disk
docker system df # what's using space
docker system prune -a --volumes # nuke unused images, networks, volumes
docker container prune
docker builder prune # build cache
8 · Dockerfile Patterns
Multi-stage Node example
# syntax=docker/dockerfile:1.7
# ---- build stage ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
# ---- runtime stage ----
FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://127.0.0.1:3000/health || exit 1
CMD ["node", "dist/server.js"]
Layer-cache rules
Order from least → most changing. Base image, system deps, lockfiles, source last.
Copy lockfile alone first, run install, then copy source. Source edits don't invalidate the install layer.
Combine related RUN with && + cleanup in same layer (rm -rf /var/lib/apt/lists/*) — separate layers can't shrink size.
Use .dockerignore — keeps node_modules, .git, secrets out of the build context.
Pin base images by digest in prod: FROM node:20@sha256:....
Run as non-root with USER. Many bases ship a non-root user (node, nginx).
BuildKit cache mounts (--mount=type=cache) speed up package installs without bloating the image.
docker compose up -d # start in background
docker compose up --build api # rebuild then up
docker compose logs -f api db
docker compose ps
docker compose exec api sh
docker compose run --rm api npm test # one-off, then remove
docker compose stop # stop, keep state
docker compose down # stop + remove containers/networks
docker compose down -v # ALSO remove named volumes (data loss!)
docker compose config # render effective merged config
10 · Kubernetes — kubectl
Context & namespaces
kubectl config get-contexts
kubectl config use-context prod
kubectl config set-context --current --namespace=app-prod
kubens app-prod # if you have kubectx/kubens
kubectl cluster-info
Inspect
Command
Purpose
kubectl get pods -A -o wide
All ns, with node + IP.
kubectl get pods -l app=api
Label selector.
kubectl describe pod <p>
Events, status, mounts.
kubectl logs -f <p> -c sidecar
Stream container logs.
kubectl logs <p> --previous
Logs from prior crash.
kubectl exec -it <p> -- sh
Shell in pod.
kubectl top pod
CPU/mem (needs metrics-server).
kubectl get events --sort-by=.lastTimestamp
Latest cluster events.
Apply / rollout / debug
kubectl apply -f k8s/ # apply directory
kubectl diff -f deploy.yaml # what would change
kubectl rollout status deploy/api
kubectl rollout history deploy/api
kubectl rollout undo deploy/api # back one revision
kubectl scale deploy/api --replicas=5
kubectl set image deploy/api api=myapp:1.4
kubectl port-forward svc/api 8080:80
kubectl cp pod:/var/log/app.log .
kubectl debug -it pod/api --image=busybox --target=api # ephemeral container
git stash push -m "wip" -u # include untracked
git stash list
git stash pop # apply + drop
git restore file # discard worktree changes
git restore --staged file # unstage
git clean -fd # remove untracked (DESTRUCTIVE)
Rewrite history (local only!)
git rebase -i HEAD~5 # squash/reorder/edit
git rebase --onto main old-base feat # transplant range
git cherry-pick <sha>
git revert <sha> # safe: new commit that undoes
Recovery
git reflog # every HEAD movement (90 days)
git reset --hard HEAD@{2} # jump back to a reflog entry
git fsck --lost-found # dangling commits
git bisect start && git bisect bad && git bisect good v1.0
# git auto-checks out midpoints; mark good/bad until culprit found
http GET api.x.com/me Authorization:"Bearer $T"
http POST api.x.com/users name=alice age:=30 # := for non-string
http --download big.bin URL
13 · Vim & tmux — Survive Remote Boxes
Vim — bare minimum
Keys
Action
i / a / o
Insert before / after / new line below.
Esc or Ctrl-[
Back to normal mode.
:w / :q / :wq / :q!
Write / quit / write+quit / discard.
dd / yy / p
Delete line / yank line / paste.
u / Ctrl-r
Undo / redo.
/foo · n · N
Search forward · next · prev.
:%s/foo/bar/gc
Replace all, confirm each.
gg / G / :42
Top / bottom / line 42.
v · V · Ctrl-v
Visual char · line · block.
ciw / ci" / ci(
Change inside word / quotes / paren.
tmux — sessions that survive disconnect
tmux new -s work # start named session
tmux ls
tmux attach -t work
# inside: prefix is Ctrl-b
# c new window n / p next / prev window
# " split horiz % split vert
# o next pane arrows move between panes
# d detach x kill pane
# [ scroll mode (q to exit)
14 · Performance & Debug
The USE checklist
Utilization — % busy. CPU: top. Disk: iostat -xz 1. Net: sar -n DEV 1.
# Python
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pipx install black # global CLI tools, isolated
uv pip install -r requirements.txt # fast modern alt
# Node
npm ci # reproducible from lockfile
npm run dev
pnpm install # fast, content-addressed
nvm use 20 # version switch
# Go / Rust
go install github.com/x/y@latest
cargo install ripgrep