Finding and controlling processes precisely
Everyone knows ps aux, but the procps toolset can do far more than one fixed default output. Knowing the format options of ps, the pattern matching of pgrep, and the namespace filters of pkill helps you find processes with precision and avoid the security pitfalls of an overly broad pattern.
Table of Contents
- 1. Beyond ps aux: custom format options
- 2. Thread details and process tree view
- 3. pgrep: finding processes via pattern matching
- 4. Namespace filters in pgrep and pkill
- 5. pkill and the security pitfalls of overly broad patterns
- 6. Securing pkill with precise additional filters
- 7. Comparison to systemctl and journalctl for systemd services
- 8. Best practices for production use
- 9. Combining continuous observation with watch and pgrep
- 10. Summary
- 11. FAQ
1. Beyond ps aux: custom format options
ps aux is the default call almost every admin types reflexively, but its fixed column selection is rarely optimal for a specific question. With ps -eo you can compose the output freely, with exactly the fields relevant to the current diagnosis, from process ID through memory usage to start time.
Especially useful is combining --sort with a custom field list, for instance to see the ten processes with the highest memory usage at a glance, without manually scanning through the entire process list. That saves valuable time during an acute memory issue on a Magento application server compared to scrolling through unsorted aux output.
# Custom field list: PID, user, CPU, RSS in MB, start time, command
ps -eo pid,user,pcpu,rss,lstart,cmd --sort=-rss | head -n 11
# Only processes of a specific user, sorted by CPU load
ps -u www-data -o pid,pcpu,pmem,etime,cmd --sort=-pcpu
2. Thread details and process tree view
By default, ps shows a process as a single line, even if it internally consists of multiple threads. With the -L or -T flag, individual threads are listed as separate lines, which helps when diagnosing multithreaded applications such as certain Elasticsearch or Java processes, to see which specific thread is causing high CPU load.
For the parent-child relationship between processes, ps -ef --forest provides an indented tree view that shows at a glance which PHP-FPM master started which workers, or which cron job spawned which child process, far clearer than a flat list with a PPID column.
# Show threads of a specific process individually
ps -L -p 28417 -o pid,tid,pcpu,comm
# Process tree as an indented tree structure
ps -ef --forest | grep -A5 php-fpm
3. pgrep: finding processes via pattern matching
pgrep searches the process list directly for process names or command line patterns and returns matching PIDs, without the detour through ps | grep and the extra process that comes with it, which can end up matching itself in the result. By default pgrep only checks the process name, with -f it instead matches the pattern against the full command line.
Additional filters such as -u for the user, -P for the parent process, and -x for an exact rather than partial name match make the search precise. Especially -x is often decisive in practice, since without it, processes whose name merely contains the pattern as a substring are matched too.
# Find PIDs of all php-fpm processes for a specific pool (search full command line)
pgrep -f "php-fpm: pool magento-prod"
# Only match exactly "redis-server" as process name, not e.g. "redis-server-old"
pgrep -x redis-server
# Processes of a specific user with detailed output (name + PID)
pgrep -u www-data -l
4. Namespace filters in pgrep and pkill
On hosts with containers or multiple PID namespaces, for instance in Mark-Shust-style Docker setups running several Magento instances, the host's process list quickly gets cluttered, since container-internal processes and host processes are all visible together. Newer versions of pgrep and pkill support the --ns flag to restrict the search to the namespace of a reference process.
That lets you search only within the PID namespace of a specific container, without accidentally hitting identically named processes in another container or on the host itself. If --ns is not available on a given distribution, the fallback is going through nsenter into the target container's namespace instead.
# Determine a container's reference PID
docker inspect --format '{{.State.Pid}}' magento-app-1
# Only search processes in the same PID namespace as this reference process
pgrep --ns 48213 -l php-fpm
5. pkill and the security pitfalls of overly broad patterns
pkill uses the same pattern matching logic as pgrep, but instead of returning a list of PIDs, it sends a signal directly to every match, SIGTERM by default. This exact combination of imprecise pattern and immediate effect makes pkill one of the most dangerous default commands in daily administration.
A classic pitfall: pkill -f magento on a host that, besides the Magento application, also runs an identically named backup script or a log rotation cron with 'magento' in its path, ends up terminating far more processes than intended. On multi-tenant hosts running several Magento instances, an overly broad pattern can even hit processes belonging to an entirely different customer installation.
The safe approach is always two-staged: first test the exact same criteria with pgrep, review the output manually, and only then apply the same pattern with pkill. On top of that, pkill should start with the gentler SIGTERM and only escalate to SIGKILL if there is no reaction, instead of going straight for -9.
# Test first: which processes would this pattern actually match?
pgrep -af "magento-prod-worker"
# Only terminate after manual review, gently with SIGTERM
pkill -f "magento-prod-worker"
# Escalate only if processes are still running after a short wait
sleep 5 && pkill -9 -f "magento-prod-worker"
6. Securing pkill with precise additional filters
To minimize the risk of an overly broad match, combining several filters is worthwhile instead of relying on a single, loosely defined pattern. -u restricts to a specific user, -P to child processes of a known parent, and --older or --newer filters by process age, which helps when cleaning up stuck old workers in a targeted way.
For PHP-FPM pools specifically, it is usually safer to reload the pool master in a controlled way via systemctl reload php8.4-fpm rather than terminating individual workers with pkill, because the master then handles a clean, controlled replacement of workers itself, instead of an externally sent signal disturbing the internal state of the pool.
# Only terminate processes of a specific user AND older than 2 hours
pkill -u www-data --older 7200 -f "worker.php"
# Terminate child processes of a known parent process specifically
pkill -P 28400
7. Comparison to systemctl and journalctl for systemd services
For any process managed as a systemd service, pgrep and pkill are fundamentally the wrong tool for control actions. systemctl restart, systemctl reload, and systemctl stop honor the stop sequence, timeout values, and any dependencies between services defined in the unit file, while a raw signal via pkill bypasses all of that.
If a systemd-managed process is terminated via pkill, systemd, depending on the restart policy, often interprets that as an unexpected crash and restarts the service automatically, which can produce confusing, seemingly unmotivated restarts. For pure observation, pgrep remains useful, but for actual control of systemd services, systemctl should consistently be used instead, complemented by journalctl -u <service> for the associated logs rather than manual process diagnosis.
The sensible scope for pgrep and pkill therefore stays limited to processes not managed by systemd, such as manually started scripts, legacy cron jobs, or debugging sessions where no unit file with a defined lifecycle exists.
# Correct for systemd-managed services: controlled restart
systemctl restart php8.4-fpm
# View the service's associated logs instead of manual process searching
journalctl -u php8.4-fpm -n 100 --no-pager
8. Best practices for production use
Before every pkill call, running a pgrep with the identical pattern as a check belongs into the workflow without exception, even under time pressure during an incident. Patterns should be formulated as specifically as possible, and when in doubt combined with additional filters like -u or -P rather than relying on a single, broadly matching name pattern.
For anything running under systemd, systemctl should consistently be used instead of pkill, since only that respects the stop sequence and any service dependencies defined in the unit file. ps -eo with a custom field list replaces, in most cases, several successive ps aux | grep calls and delivers exactly the information needed for the specific diagnosis from the start.
9. Combining continuous observation with watch and pgrep
For observing a slowly developing problem, such as a PHP-FPM pool gradually consuming more and more memory, combining watch with a targeted ps call is worthwhile, rather than retyping the command manually every few seconds. That lets you track in real time whether the number of workers or their memory usage is actually growing steadily.
pgrep -c, which returns only the count of matches instead of the individual PIDs, also works well for simple threshold checks in monitoring scripts, for instance to alert as soon as the number of running workers in a pool leaves an expected range, without needing to set up a more elaborate monitoring tool for it.
For a one-off but deeper snapshot of all processes of a specific user together with the full command line, ps -u www-data -o pid,cmd --no-headers serves as a good basis for further scripts, for example to automatically derive a list for pgrep or targeted kill calls, instead of manually post-processing raw data.
# Watch a pool's memory usage live every 2 seconds
watch -n 2 'ps -eo pid,rss,cmd | grep "pool magento-prod" | grep -v grep'
# Just the match count for a simple threshold check
pgrep -cf "pool magento-prod"
| Tool | Main Purpose | Risk if Misused | Better Alternative Under systemd |
|---|---|---|---|
| ps -eo/--sort | Format and sort the process list precisely | Low, read-only display | Not applicable |
| pgrep | Find processes by pattern/namespace/user | Low, no effect on processes | Not applicable |
| pkill | Send a signal to every pattern match | High, an overly broad pattern hits the wrong processes | systemctl stop/restart/reload |
| kill -9 directly | Immediate hard termination without cleanup | High, no clean shutdown, potential data loss | systemctl with a defined timeout |
Mironsoft
Server administration, Docker hosts, and performance tuning
Linux servers nobody on the team really understands anymore?
We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.
Server Audit
Review the existing server configuration for security gaps and performance bottlenecks.
Docker Host Setup
Set up and secure production-ready Docker environments for Magento cleanly.
Monitoring & Tuning
Measure resource usage and tune systemd, kernel, and services with purpose.
10. Summary
ps/pgrep/pkill
Audience
Admins finding and controlling processes precisely instead of relying on plain ps aux
Core command
pgrep -f 'pattern' before every pkill to verify
Combine with
additional filters -u, -P, --older for precise targeting
Biggest pitfall
pkill with an overly broad pattern on a multi-tenant host