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:24 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:24-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:24-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:24@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
git rebase --onto main old-base feat — transplant a commit range
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 24 # version switch
# Go / Rust
go install github.com/x/y@latest
cargo install ripgrep
16 · SQL & Query Patterns
Joins, window functions, and the query shapes interviews drill.
Joins & Grouping
Command
Purpose
INNER JOIN b ON a.id = b.a_id
Rows matching in both tables
LEFT JOIN b ON a.id = b.a_id
All left rows, NULLs where no match
FULL OUTER JOIN b ON …
All rows from both sides
CROSS JOIN b
Cartesian product of both tables
LEFT JOIN b ON … WHERE b.id IS NULL
Anti-join: left rows with no match
SELECT dept, COUNT(*) … GROUP BY dept
Aggregate rows per group
GROUP BY dept HAVING COUNT(*) > 5
Filter groups after aggregation
GROUP BY GROUPING SETS ((a),(b),())
Multiple aggregation levels in one pass
COALESCE(col, 0)
First non-NULL value / default
Window Functions
Command
Purpose
ROW_NUMBER() OVER (ORDER BY ts)
Unique sequential rank, no ties
RANK() OVER (ORDER BY score DESC)
Rank with gaps on ties
DENSE_RANK() OVER (…)
Rank without gaps on ties
LAG(val, 1) OVER (ORDER BY ts)
Previous row's value
LEAD(val, 1) OVER (ORDER BY ts)
Next row's value
SUM(amt) OVER (ORDER BY ts)
Running total (cumulative)
SUM(amt) OVER (PARTITION BY user ORDER BY ts)
Running total reset per partition
AVG(x) OVER (ORDER BY ts ROWS 6 PRECEDING)
7-row moving average
NTILE(4) OVER (ORDER BY score)
Split rows into quartile buckets
FIRST_VALUE(x) OVER (PARTITION BY g ORDER BY ts)
First value per group
Top-N Per Group & Dedup
-- Latest row per user (dedup / top-1 per group)
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id
ORDER BY created_at DESC) AS rn
FROM events
)
SELECT * FROM ranked WHERE rn = 1;
-- Same thing with QUALIFY (Snowflake / BigQuery / DuckDB)
SELECT *
FROM events
QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id
ORDER BY created_at DESC) = 1;
CTEs, Recursion & Gaps-and-Islands
Command
Purpose
WITH cte AS (SELECT …) SELECT * FROM cte
Named subquery for readability
WITH RECURSIVE t AS (…) SELECT …
Hierarchies, graph walks, sequences
EXPLAIN ANALYZE SELECT …
Show plan with real row/timing stats
CREATE INDEX idx ON t (col)
B-tree index to speed lookups/joins
SELECT … FROM t /*+ INDEX(t idx) */
Optimizer hint (Oracle/MySQL) to force index
-- Recursive: employee org chart
WITH RECURSIVE org AS (
SELECT id, name, manager_id, 1 AS depth
FROM emp WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, o.depth + 1
FROM emp e JOIN org o ON e.manager_id = o.id
)
SELECT * FROM org ORDER BY depth;
-- Gaps-and-islands: collapse consecutive login days into ranges
SELECT user_id, MIN(day) AS start_day, MAX(day) AS end_day
FROM (
SELECT user_id, day,
day - (ROW_NUMBER() OVER (PARTITION BY user_id
ORDER BY day))::int AS grp
FROM logins
) s
GROUP BY user_id, grp
ORDER BY user_id, start_day;
# Error ratio > 5% of traffic, per service
(
sum by (service) (rate(http_requests_total{code=~"5.."}[5m]))
/
sum by (service) (rate(http_requests_total[5m]))
) > 0.05
# p99 request latency by route
histogram_quantile(
0.99,
sum by (le, route) (rate(http_request_duration_seconds_bucket[5m]))
)