Linux — Level by Level›12 · Checkpoint: five broken servers

Lesson 12 of 19 · Level 2 — Intermediate: troubleshooting

Checkpoint: five broken servers

Five realistic broken servers, each presented as symptoms and evidence. Diagnose each one from what you see, as you would on call, then compare with the worked solution.

Practitioner
Key wordsincidentevidenceEADDRINUSElsof +L1iowaitStrictModessystemd-resolved

How to use this lesson

Each case gives you the symptom a user reports and the evidence you'd collect. Before opening the solution, write down:

  1. Which layer is failing (from what you've learned in Levels 1–2).
  2. The one command that proves it.
  3. The fix, and how to prevent it next time.

This is like a detective game. Each case has clues. You don't get points for guessing. You get points for pointing at the clue that proves your answer.

Case 1: "The API returns 502 after the deploy"

Evidence:

$ systemctl status orders-api
× orders-api.service - Orders API
     Active: failed (Result: exit-code) since Sat 2026-09-27 11:02:44 UTC
$ journalctl -u orders-api -n 5 --no-pager
… orders-api[4410]: Error: listen EADDRINUSE: address already in use 0.0.0.0:8080
… systemd[1]: orders-api.service: Main process exited, code=exited, status=1/FAILURE
Solution

Layer: port. Something already holds port 8080.

$ sudo ss -ltnp | grep ':8080 '
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=3120,fd=21))
$ ps -o pid,lstart,cmd -p 3120
  PID                  STARTED CMD
 3120 Fri Sep 26 09:14:10 2026 node /srv/orders-api/old/server.js

An old copy, started by hand yesterday outside systemd, is still running. Stop it (sudo kill 3120), then sudo systemctl start orders-api.

Prevention: run services only through systemd, and make the deploy script check the port before starting.

Case 2: "Disk full, but nothing big on it"

Evidence:

$ df -h /
Filesystem              Size  Used Avail Use% Mounted on
/dev/mapper/vg0-root     40G   40G     0 100% /
$ sudo du -xsh /
14G /
Solution

Layer: filesystem. df ≫ du means deleted but still open files.

$ sudo lsof +L1
COMMAND  PID   USER FD TYPE DEVICE    SIZE/OFF NLINK NODE NAME
java    2211 appsvc 5w  REG  253,0 27917287424     0 1311 /var/log/app/app.log (deleted)

Someone ran rm on a 26 GB live log. Restart the service (or truncate via /proc/2211/fd/5 as an emergency measure) to release the space.

Prevention: logrotate with copytruncate (or a reopen signal) for this app, and alerts at 80% disk.

Case 3: "Everything is slow since noon"

Evidence:

$ uptime
 12:18:40 up 40 days,  load average: 31.40, 28.02, 12.77
$ nproc
8
$ vmstat 1 3
 r  b   swpd   free   buff  cache   si   so    bi     bo   in   cs us sy id wa st
 2 29      0 412300  81220 9120440    0    0  1200 188000 2100 3300  4  3 12 81  0
Solution

Layer: disk. Look at b = 29 (processes blocked on I/O) and wa = 81%: the CPUs are mostly waiting. The high load comes from I/O wait, not computation.

$ iostat -xz 1 2 | grep -E 'Device|sd|nvme'
Device  r/s    w/s   wkB/s  w_await  aqu-sz  %util
sda     8.0  1450.0 188000.0  210.30   305.10 100.00
$ sudo pidstat -d 1 1
UID  PID   kB_rd/s   kB_wr/s  Command
0   9981      0.00 185000.00  tar

A backup (tar) moved from 02:00 to 12:00 is saturating the disk. Stop or ionice -c3 -p 9981 it now; move the schedule back.

Prevention: run backups off-peak with ionice/nice, ideally from a snapshot, and alert on disk await.

Case 4: "The deploy user can't SSH in any more"

Evidence (from the client):

$ ssh deploy@web-02
deploy@web-02: Permission denied (publickey).

From the server's log:

$ sudo journalctl -u ssh -n 3 --no-pager
… sshd[7718]: Authentication refused: bad ownership or modes for directory /home/deploy
Solution

Layer: permissions. Someone ran chmod -R 777 /home/deploy "to fix a permission problem". sshd's StrictModes now ignores the keys because anyone could have edited them.

$ sudo chmod 755 /home/deploy
$ sudo chmod 700 /home/deploy/.ssh
$ sudo chmod 600 /home/deploy/.ssh/authorized_keys
$ sudo chown -R deploy:deploy /home/deploy/.ssh

Also review what else the recursive 777 exposed, and whether authorized_keys was modified while it was world-writable.

Prevention: never use 777 as a fix; manage user homes and keys with configuration management.

Case 5: "Nothing can resolve any hostname"

Evidence:

$ curl https://api.github.com
curl: (6) Could not resolve host: api.github.com
$ cat /etc/resolv.conf | grep nameserver
nameserver 127.0.0.53
$ ping -c1 1.1.1.1
64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=4.1 ms
Solution

Layer: DNS. The network works (ping by IP succeeds). 127.0.0.53 is the systemd-resolved local stub.

$ systemctl is-active systemd-resolved
inactive
$ sudo systemctl enable --now systemd-resolved
$ resolvectl query api.github.com
api.github.com: 140.82.121.6

Someone disabled systemd-resolved while "cleaning up services".

Prevention: keep a list of essential services in configuration management, and a synthetic DNS check in monitoring.

Going deeper: turn cases into habits

Each case maps to one habit: 1 check ports and orphans, 2 df vs du, 3 load with idle CPU = I/O, 4 never 777, 5 test by IP before blaming DNS. Keep a personal runbook of cases like these. It's the most valuable document you'll own on call.

You've finished Linux Level 2

You can now troubleshoot services, disks, networks, performance and SSH from evidence. Level 3 goes under the hood: the kernel, cgroups and namespaces, NUMA and CPU pinning, eBPF tracing, SELinux and host hardening.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.