Linux & Bash — Performance Troubleshooting & Scripting
The USE Method: a systematic framework, not ad hoc guessing
For every system resource (CPU, memory, disk, network), check three things: Utilization (how busy is it), Saturation (is work queued waiting for it), and Errors (is it failing). This structured approach is what separates efficient troubleshooting from randomly running commands hoping something looks wrong — a genuinely slow server is almost always traceable to one specific resource being saturated or erroring, not a vague, diffuse "everything is slow."
vmstat is the right first command specifically because it surfaces both CPU saturation (the r column) and memory pressure (swap activity) in one glance, before diving into resource-specific tools.
Disk I/O: `iostat` and what `await` actually tells you
High await specifically means requests are waiting, not that the disk itself is broken — this is a saturation signal (the USE method's second check), pointing toward too much concurrent I/O demand for the storage's actual throughput, not necessarily a hardware failure.
Network: `ss` over the older `netstat`
Many TIME-WAIT connections is normal on a high-traffic server (connections finishing their close sequence). Many CLOSE-WAIT connections is a real, specific signal: the application isn't closing connections it should be — a common, genuine resource-leak pattern worth investigating in the application code, not the OS.
`strace` and `lsof`: process-level debugging
strace on a hung or misbehaving process reveals exactly what it's doing at the syscall level — a process repeatedly calling connect() and failing reveals a network dependency issue directly, faster than guessing from application logs alone. lsof is the standard tool for "why won't this file/port release" — showing precisely which process holds a given file or socket open.
Log Analysis: grep, awk, sed, journalctl
This is the single most frequent Linux task in DevOps work — something is misbehaving, and the answer is somewhere in a log file or the systemd journal.
grep/awk/sed work on plain text log files; journalctl is how you read logs for anything running as a systemd service, since those logs often aren't written to a flat file at all by default (they live in the binary journal store) — journalctl -u is the right first command for a systemd-managed service, not grep-ing around /var/log hoping to find it.
Networking & Security Basics
tcpdump and packet-level tools are for confirming whether traffic is actually leaving/arriving at the OS level at all — useful specifically when application-level logs show a request as "sent" but the other side never saw it, which narrows the problem to the network layer rather than the application.
Production Bash: `set -euo pipefail`, every time
-e: exit immediately on any command failure, rather than continuing past it silently.
-u: error on any reference to an undefined variable, catching typos before they cause quiet, wrong behavior.
-o pipefail: a pipeline's exit code reflects the rightmost non-zero exit status among all its commands (or 0 if every command succeeded) — not just the last command's status by default, and not the first failing command either (a common misstatement, worth being precise about). Without pipefail, cmd_that_fails | cmd_that_succeeds reports success, since only the last command's exit code counts by default — pipefail fixes that by surfacing a failure from any stage of the pipeline. If multiple commands in the same pipeline fail, the reported code is the rightmost one that failed, not the first.
This should be the first line of essentially every production Bash script — the cost is near-zero, and the alternative (a script silently continuing past a real failure) is a genuine, recurring source of "the script ran but didn't actually do what it should have" incidents.
Quoting variables — a small habit, a real, common bug source
Unquoted variable expansion undergoes word-splitting — a plain string value containing a space gets treated as multiple separate arguments, which is a genuine, recurring source of scripts that work fine in testing (with simple test values) and break on real-world input (a filename with a space in it). The fix isn't just "add quotes" — it's using an actual bash array (files=(...), expanded as "${files[@]}") when the underlying data is genuinely a list of items, since quoting a plain unquoted string ("$files") just treats the whole thing as one single item instead of fixing the iteration. [[ ]] (not [ ]) for conditionals and $(command) (not backticks) for command substitution are the same category of small, consistently-recommended defaults — modern, more predictable syntax with fewer edge-case surprises than their older equivalents.
Compression and Archives
Kubernetes context: cgroups and the OOM killer
Kubernetes enforces container resource limits using Linux cgroups directly — when a container's memory usage exceeds its configured limit, the kernel's OOM killer terminates the offending process, which surfaces in kubectl describe pod as OOMKilled. This is a kernel-level mechanism, not a Kubernetes-specific one — understanding that connection is what makes OOMKilled behavior predictable rather than mysterious: it's the same OOM-killer mechanism that would terminate any process exceeding a cgroup memory limit, container or not.

