SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free🗺️ Learning Roadmaps

LinuxTroubleshooting

Debug common issues with root cause analysis

✍️
Written by senior engineers. Reviewed for technical accuracy.· Updated 2025 · SynfraCore Linux Team
Expert Content

Linux Troubleshooting Guide

Commands that modify system state, restart services, or read privileged files below generally need sudo (or a root shell) unless noted otherwise — that's not called out on every single line, but assume it unless the command is a plain read of your own process/user info.

Issue 1: Cannot SSH into server — Connection refused

Symptom: ssh user@hostssh: connect to host port 22: Connection refused

Root Cause: SSH daemon not running, firewall blocking port 22, wrong port, or host is down.

Debug steps:

bash
ping host                              # is host reachable?
nmap -p 22 host                        # is port 22 open? (nmap isn't installed by default — sudo apt install nmap / sudo dnf install nmap)
nc -zv host 22                         # nc is more often preinstalled than telnet's client — quick alternative to nmap for a single port

# On the server (if console access available) — service name and firewall
# tooling differ by distro family, check which one you're on first:
cat /etc/os-release

# Debian/Ubuntu:
systemctl status ssh                   # unit is "ssh", not "sshd", on Debian/Ubuntu
ss -tlpn | grep ssh
sudo ufw status                        # ufw is Debian/Ubuntu's default firewall front-end

# RHEL/CentOS/Fedora:
systemctl status sshd                  # unit is "sshd" on RHEL-family
ss -tlpn | grep sshd
sudo firewall-cmd --list-all           # firewalld is RHEL-family's default
sudo iptables -L INPUT -n | grep 22    # raw iptables, either family, if neither ufw nor firewalld is in use

Fix patterns:

bash
# Debian/Ubuntu:
sudo systemctl start ssh && sudo systemctl enable ssh
sudo ufw allow 22/tcp

# RHEL/CentOS/Fedora:
sudo systemctl start sshd && sudo systemctl enable sshd
sudo firewall-cmd --permanent --add-service=ssh && sudo firewall-cmd --reload

ssh -p 2222 user@host                  # if the server actually uses a non-standard port

Prevention: Whitelist allowed users with AllowUsers in /etc/ssh/sshd_config, and enable fail2ban to auto-block repeated failed login attempts. (AllowTcpForwarding controls whether SSH port-forwarding/tunneling is permitted through a session — it has no relationship to connection-refused errors; standard hardening guidance is actually to set it to no unless tunneling is specifically needed, not to enable it.)

---

Issue 2: Disk 100% full — system unstable

Symptom: Commands fail with No space left on device. Cannot write logs, create files.

Root Cause: Logs grown unbounded, large temp files, Docker layer accumulation, inode exhaustion.

Debug steps:

bash
df -h                                  # which filesystem is full?
df -i                                  # check inode usage separately — a filesystem can be "full" on inodes with plenty of byte-space free
du -sh /* 2>/dev/null | sort -rh | head -20  # find large directories
du -sh /var/log/* 2>/dev/null | sort -rh | head -10     # common culprit
find / -size +1G -type f 2>/dev/null   # files over 1GB
lsof 2>/dev/null | grep deleted | grep -v "0,0"   # deleted files a process still has open (space isn't freed until the process closes them)

Fix:

bash
sudo journalctl --vacuum-size=200M     # trim systemd journal
sudo truncate -s 0 /var/log/syslog     # emergency: zero out log (Debian/Ubuntu path; RHEL-family: /var/log/messages)
sudo find /tmp -mtime +7 -delete       # clean old temp files
docker system prune -a -f              # if Docker is installed

Prevention: Configure log rotation in /etc/logrotate.d/. Monitor disk usage with alerting (not just manually running df -h).

---

Issue 3: Process zombie state — defunct processes

Symptom: ps aux | grep Z shows zombie processes. top shows a nonzero zombie count.

Root Cause: Child process finished but its parent never called wait()/waitpid() to collect its exit status.

Debug steps:

bash
# Zombies show as Z+ (not a bare Z) when started from an interactive shell
# foreground process group -- an exact-match "$8==\"Z\"" filter misses these.
# Use a substring/regex match instead:
ps aux | awk '$8 ~ /Z/ {print $0}'     # list all zombies
ps -ef | grep defunct

# Find the parent of a specific zombie
ps -ef | grep <zombie-pid>             # look at the PPID column
pstree -p | grep <zombie-pid>

Fix:

bash
kill -s SIGCHLD <parent-pid>           # only works if the parent actually has a SIGCHLD
                                        # handler that calls wait() -- often does nothing,
                                        # since a parent that already had one usually
                                        # wouldn't have accumulated zombies in the first place
kill -9 <parent-pid>                   # the reliable fix: kill the parent, the zombie is
                                        # reaped automatically once its parent exits
# Zombies are harmless individually (they hold almost no resources -- just a
# process table entry), but a growing count indicates a genuinely buggy
# parent process that needs fixing, not just repeated manual cleanup

---

Issue 4: High CPU — system load unresponsive

Symptom: top shows load average > number of CPU cores. System very slow.

Load average on Linux counts both CPU-runnable processes and processes in uninterruptible sleep (D state — usually blocked on disk I/O), unlike the classic BSD/Unix definition. A high load average alone doesn't prove CPU is the bottleneck — confirm with vmstat/pidstat before assuming it's pure CPU saturation; it could be an I/O-wait storm instead.

Debug steps:

bash
top -c                                 # real-time view, press 1 for per-core breakdown
uptime                                 # load average (1/5/15 min)
ps aux --sort=-%cpu | head -10         # CPU-heavy processes
pidstat -u 2 5                         # CPU per process over time (needs the sysstat package: sudo apt install sysstat / sudo dnf install sysstat)
strace -p <pid>                        # what syscalls is the culprit making
cat /proc/<pid>/status                 # process state details

Fix:

bash
nice -n 19 <command>                   # start a new command at lower priority
renice +10 -p <pid>                    # reduce priority of an already-running process
kill -9 <pid>                          # terminate if genuinely runaway
# For Kubernetes: check for CPU throttling specifically
kubectl top pods -n production
kubectl describe pod <name> | grep -A3 Limits

Prevention: Set CPU limits on containers. Use ulimit -u for per-user process-count limits. Implement autoscaling for genuinely variable load.

---

Issue 5: OOM Killer terminating processes

Symptom: Process disappears unexpectedly. dmesg | grep -i oom shows kills.

Root Cause: System ran out of RAM. The kernel's OOM killer selected and killed a process to free memory.

Debug steps:

bash
dmesg | grep -i "oom\|killed"          # see what was killed and why
journalctl -k | grep -i oom            # kernel log via journald -- works regardless of distro
cat /var/log/syslog 2>/dev/null | grep oom   # Debian/Ubuntu-specific path; use journalctl -k above if this file doesn't exist (e.g. RHEL-family)
free -h                                # current memory state
vmstat -s                              # memory statistics
cat /proc/<pid>/status | grep VmRSS    # a specific process's memory usage

Fix:

bash
# Increase swap (emergency, buys time -- not a real fix for sustained memory pressure)
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

# Protect a critical process from being OOM-killed.
# /proc/<pid>/oom_adj is the LEGACY interface, deprecated since kernel 2.6.36
# (~2010) -- often doesn't exist at all on a current kernel. Use oom_score_adj,
# which uses a different scale (-1000 to 1000, not -17 to 15):
echo -1000 | sudo tee /proc/<critical-pid>/oom_score_adj   # -1000 = never kill

# Kubernetes: set memory limits explicitly (unbounded memory requests are
# what leads to OOMKilled pods in the first place)
resources:
  limits:
    memory: "512Mi"
  requests:
    memory: "256Mi"

Prevention: Monitor memory with alerts at 80% and 90% thresholds, before the OOM killer has to intervene. Set proper container memory limits. Swap is a reasonable safety net on dev servers, not a substitute for right-sizing production memory limits.

Share:
Join our Community
Daily tips, job alerts, interview help — join engineers learning together
Up Next
🏆
LinuxCertification
Exam guides, practice questions, and prep strategies
Also Worth Exploring
← Back to all Linux modules
Interview Q&ACertification