Every program running on a Linux server is a process. Your web server, PHP-FPM, MySQL, SSH sessions, cron jobs, and the kernel itself all run as processes. Knowing how to list, filter, and manage processes is fundamental to server administration. When a site is slow, a server is overloaded, or a service is not responding, the first step is always checking what is running and how much resources it is consuming.
This guide covers the commands you need for listing processes, finding specific ones, checking resource usage, identifying what is using a port, and stopping processes that need to be stopped.
Listing all processes with ps#
ps aux
ps aux
This is the most commonly used form. It shows every process on the system with useful details:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 169612 13456 ? Ss Feb15 0:12 /usr/lib/systemd/systemd
root 512 0.0 0.0 16716 5892 ? Ss Feb15 0:03 /usr/sbin/sshd -D
www-data 1234 0.2 1.5 452312 124568 ? S 09:15 0:45 php-fpm: pool www
mysql 1456 1.2 8.3 1842560 678432 ? Sl Feb15 142:30 /usr/sbin/mysqld
john 2345 0.0 0.0 21560 5320 pts/0 Ss 10:22 0:00 -bash
The columns:
- USER – which user owns the process
- PID – the process ID (used to send signals or kill the process)
- %CPU – current CPU usage as a percentage
- %MEM – current memory usage as a percentage of total RAM
- VSZ – virtual memory size in KB (includes shared libraries, not very useful on its own)
- RSS – resident set size in KB (actual physical memory the process is using)
- STAT – process state (S=sleeping, R=running, Z=zombie, D=uninterruptible sleep, T=stopped)
- START – when the process started
- TIME – total CPU time consumed
- COMMAND – the command that started the process
Filter with grep
ps aux
outputs every process, which can be hundreds of lines. Pipe to grep to find what you need:
ps aux | grep nginx
root 1100 0.0 0.1 41456 8320 ? Ss Feb15 0:00 nginx: master process
www-data 1101 0.0 0.3 42112 24568 ? S Feb15 2:15 nginx: worker process
www-data 1102 0.0 0.3 42096 24312 ? S Feb15 2:12 nginx: worker process
john 3456 0.0 0.0 12144 1080 pts/0 S+ 10:45 0:00 grep nginx
The last line is the grep command itself showing up in the process list. To exclude it:
ps aux | grep [n]ginx
The
[n]
trick makes the grep pattern different from the literal string “nginx,” so the grep process does not match itself.
List processes for a specific user
ps -u www-data
Shows only processes owned by
www-data
. Useful for checking how many PHP-FPM workers are running or what a specific user is doing on the server.
ps aux --sort=-%mem | head -20
Sorts by memory usage (descending) and shows the top 20. This is the fastest way to find what is consuming the most RAM.
ps aux --sort=-%cpu | head -20
Same but sorted by CPU usage. Useful when load is high and you need to identify the culprit.
Real-time monitoring with top#
top
top
shows a live, updating view of processes sorted by CPU usage by default. It refreshes every 3 seconds.
top - 10:45:23 up 42 days, 3:15, 2 users, load average: 0.42, 0.35, 0.28
Tasks: 142 total, 1 running, 140 sleeping, 0 stopped, 1 zombie
%Cpu(s): 5.2 us, 1.3 sy, 0.0 ni, 93.2 id, 0.3 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 7953.5 total, 512.3 free, 5124.8 used, 2316.4 buff/cache
MiB Swap: 2048.0 total, 1892.0 free, 156.0 used. 2548.7 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1456 mysql 20 0 1842560 678432 32456 S 3.3 8.3 142:30.45 mysqld
1234 www-data 20 0 452312 124568 12340 S 1.0 1.5 0:45.12 php-fpm
The header shows system-wide stats: uptime, load average, CPU breakdown, and memory usage.
Key commands while top is running:
-
M– sort by memory usage -
P– sort by CPU usage (default) -
k– kill a process (prompts for PID) -
u– filter by user -
c– toggle full command path -
1– show per-CPU core breakdown -
q– quit
The load average numbers (0.42, 0.35, 0.28) show the average number of processes waiting for CPU time over the last 1, 5, and 15 minutes. On a 4-core server, a load average of 4.0 means all cores are fully utilized. Above that means processes are waiting for CPU time. A load average consistently above the number of CPU cores indicates the server is overloaded.
The wa (I/O wait) percentage in the CPU line shows how much time the CPU spends waiting for disk I/O. A high wa value (above 10-20%) usually means disk is the bottleneck, not CPU. This is common on servers with slow disks or when MySQL is doing heavy table scans.
htop: a better top#
htop
htop
is an improved version of
top
with color-coded output, visual CPU and memory bars, mouse support, and easier process management. It is not installed by default on most distributions but is available in the standard repositories:
# RHEL/Rocky Linux/CentOS
sudo dnf install htop
# Ubuntu/Debian
sudo apt install htop
htop
shows the same information as
top
but in a more readable format. You can scroll through the process list, search with
/
, filter with
\
, and kill processes with
F9
. The tree view (
F5
) shows parent-child relationships between processes, which is useful for understanding which PHP-FPM workers belong to which pool or which processes were spawned by cron.
Finding specific processes#
pgrep
pgrep nginx
Returns just the PIDs of processes matching “nginx”:
1100
1101
1102
With more detail:
pgrep -a nginx
1100 nginx: master process /usr/sbin/nginx -g daemon on;
1101 nginx: worker process
1102 nginx: worker process
The
-a
flag shows the full command line alongside the PID.
pgrep by user
pgrep -u www-data
Lists PIDs of all processes owned by
www-data
.
Count processes
pgrep -c php-fpm
Returns just the count. Useful in scripts to check how many PHP-FPM workers are active:
echo "PHP-FPM workers: $(pgrep -c php-fpm)"
Checking which process uses a port#
When a service will not start because the port is already in use, or when you need to identify what is listening on a specific port:
ss (modern replacement for netstat)
ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1100,fd=6))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1100,fd=7))
LISTEN 0 128 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=1456,fd=22))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=512,fd=3))
The flags:
-t
for TCP,
-l
for listening,
-n
for numeric (no DNS resolution),
-p
for process info.
This shows you exactly what is listening on each port. MySQL on 3306, Nginx on 80 and 443, SSH on 22. If you are troubleshooting a 521 error or SSH connection refused, this command tells you immediately whether the service is listening.
Filter by port
ss -tlnp | grep ':443'
Shows only what is listening on port 443.
lsof
sudo lsof -i :80
Shows all processes using port 80, including the process name, PID, user, and connection state.
lsof
requires root to see processes owned by other users.
sudo lsof -i :3306
Shows what is connected to MySQL. Useful for diagnosing too many connections issues.
Listing services with systemctl#
Modern Linux distributions use systemd to manage services.
systemctl
lists and controls them.
List all running services
systemctl list-units --type=service --state=running
UNIT LOAD ACTIVE SUB DESCRIPTION
crond.service loaded active running Command Scheduler
mysqld.service loaded active running MySQL Server
nginx.service loaded active running The nginx HTTP and reverse proxy server
php-fpm.service loaded active running The PHP FastCGI Process Manager
sshd.service loaded active running OpenSSH server daemon
This shows only services that are currently running. It is a cleaner view than
ps aux
when you want to see which services are active.
List all services (including stopped)
systemctl list-units --type=service --all
Check a specific service
systemctl status nginx
This shows whether the service is running, its PID, memory usage, recent log entries, and how long it has been running. This is usually the first command to run when investigating a service issue.
List services that start on boot
systemctl list-unit-files --type=service --state=enabled
Shows which services are configured to start automatically when the server boots. If a service should be running but is not, check if it is enabled here. For restarting services and the server itself, see How to restart Linux: command line reference.
Killing processes#
kill by PID
kill 1234
Sends SIGTERM (signal 15) to process 1234, asking it to shut down gracefully. The process can catch this signal and clean up before exiting. Most well-behaved processes exit within a few seconds.
If the process does not respond to SIGTERM:
kill -9 1234
Sends SIGKILL (signal 9), which terminates the process immediately. The process cannot catch or ignore this signal. Use this only when SIGTERM does not work, because SIGKILL does not give the process a chance to clean up (close files, release locks, flush buffers).
Kill by name
pkill php-fpm
Sends SIGTERM to all processes matching “php-fpm.” Be careful with this because it matches partial names.
pkill php
would kill any process with “php” in its name.
Kill all processes by a user
pkill -u john
Sends SIGTERM to every process owned by
john
. This is the equivalent of logging the user out forcefully.
Kill a process using a specific port
sudo fuser -k 80/tcp
Kills whatever is listening on port 80. Useful when a port is stuck and you need to free it for another service.
Common troubleshooting scenarios#
Server is slow, what is using CPU?
ps aux --sort=-%cpu | head -10
Or open
top
and press
P
to sort by CPU. Look for processes with high %CPU that should not be there. Common culprits: a PHP process stuck in an infinite loop, a MySQL query doing a full table scan, or a backup process running during peak hours.
Server is running out of memory
ps aux --sort=-%mem | head -10
Or open
top
and press
M
to sort by memory. Check the RSS column for actual memory usage. MySQL and PHP-FPM are typically the largest memory consumers on a web server. If PHP-FPM workers are consuming too much memory individually, check for memory leaks in WordPress plugins.
A service is not running
systemctl status nginx
If the service is “inactive (dead)” or “failed,” check the logs:
journalctl -u nginx --no-pager -n 50
This shows the last 50 log entries for the service, which usually includes the error that caused it to stop.
Zombie processes
Zombie processes (state
Z
in
ps
output) are processes that have finished executing but their parent has not collected their exit status. A few zombies are normal and harmless. Hundreds of zombies indicate a bug in the parent process.
ps aux | grep ' Z '
Zombies cannot be killed with
kill
because they are already dead. The fix is to restart the parent process, which clears the zombie entries.
Running process checks over SSH#
You can run any of these commands on a remote server without opening an interactive session:
ssh user@server "ps aux --sort=-%cpu | head -10"
ssh user@server "systemctl status nginx"
ssh user@server "ss -tlnp"
This is useful for quick checks across multiple servers. See How to run commands over SSH for more patterns.
Quick reference#
# All processes
ps aux
# Sort by CPU usage
ps aux --sort=-%cpu | head -20
# Sort by memory usage
ps aux --sort=-%mem | head -20
# Filter by name
ps aux | grep [n]ginx
# Filter by user
ps -u www-data
# Real-time monitoring
top
htop
# Find PIDs by name
pgrep nginx
pgrep -a nginx # with full command
pgrep -c php-fpm # count only
# Check what is listening on ports
ss -tlnp
ss -tlnp | grep ':443'
sudo lsof -i :80
# List running services
systemctl list-units --type=service --state=running
# Check a specific service
systemctl status nginx
# Kill by PID
kill PID # graceful
kill -9 PID # force
# Kill by name
pkill process-name
# Kill by port
sudo fuser -k 80/tcp