Operate the server
Read the right logs
Follow a request through DNS, Nginx, the application service, and system logs without dumping unrelated data.
When something breaks, the temptation is to tail every log on the server and hope the answer jumps out. It rarely does. Instead, follow one request through the layers it crosses: DNS, TLS, Nginx, the app. At each layer ask one question: did the request get here?
Start from the client
Make the request with full details:
curl --verbose https://notes.example.com/health
The output shows the IP curl resolved, the TLS handshake with the certificate subject, the request headers it sent, and the status line it got back. Right there you know whether DNS, TLS and HTTP each worked. Note the time and the status before you touch the server.
Watch the server while you reproduce
Nginx writes to /var/log/nginx/access.log and /var/log/nginx/error.log. The app’s output goes to the systemd journal. Open two SSH sessions and follow both, then repeat the curl:
sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log
sudo journalctl -u notes-app --since "10 minutes ago" -f
Now read what appeared. A line in access.log proves the request reached Nginx. A line in error.log like connect() failed (111: Connection refused) while connecting to upstream means Nginx got it but the app didn’t answer. A line in the app’s journal proves the request made it all the way. The last layer that saw the request is where the problem starts.
When nothing shows up
If no log moved, the request never reached the server. Work from the outside in:
dig +short notes.example.com
sudo ufw status
systemctl is-active nginx notes-app
sudo ss -lntp | grep -E ':80|:443|:3000'
One command per layer: does the name resolve to this IP, is the firewall letting it through, are both services running, is something listening on each port. You’ll usually find the gap in under a minute.
Two mistakes I see a lot
The first is reading old messages. You find an error, fix it, and it was from last Tuesday. Filter the journal by the current boot and the time you reproduced the problem:
sudo journalctl -u notes-app -b --since "2026-07-30 12:00"
Replace the timestamp with your own.
The second is restarting before looking. A restart often makes the symptom go away and the evidence with it. Collect first, restart second.
Logs contain secrets
Access logs hold IPs and URLs with query strings. Application logs can hold whatever a developer printed. Never log passwords, authorization headers, session cookies, full request bodies or environment variables. When you share a log excerpt with someone, redact it first.
Try this: request a path that doesn’t exist, like /does-not-exist, and follow the resulting 404 through curl, the Nginx access log and the app journal. Write down the last layer that saw it. That’s the same exercise you’ll do under pressure someday, so do it once now when nothing is on fire.
Lesson completed