How to Fix CPU0 Overload on Multi‑Core Web Servers Using taskset and PHP‑FPM
This article shows how to detect a CPU0 load imbalance on a multi‑core web server, confirms PHP‑FPM as the cause with mpstat and pidstat, and provides a Bash script that uses taskset to evenly distribute PHP‑FPM processes across all CPUs, plus a cron tip to keep the setting persistent.
During a discussion the author noticed that on a four‑core web server CPU0 was consistently much busier than the other cores, and the suspicion fell on PHP‑FPM processes.
Running mpstat -P ALL 1 10 produced the following snapshot, clearly showing CPU0’s idle time far lower than the others:
CPU %usr %nice %sys %iowait %irq %soft ... %idle
all 17.57 0.03 1.78 0.00 0.35 0.23 ... 80.04
0 43.17 0.00 4.12 0.00 1.41 1.00 ... 50.30
1 9.80 0.00 0.81 0.00 0.00 0.00 ... 89.39
2 9.31 0.00 1.20 0.00 0.00 0.00 ... 89.49
3 7.94 0.10 0.80 0.00 0.00 0.00 ... 91.16To verify that PHP‑FPM was responsible, the author used pidstat combined with grep and awk:
pidstat | grep php-fpm | awk '{print $(NF-1)}' | sort | uniq -c
157 0
34 1
34 2
32 3The output shows that the majority of PHP‑FPM workers were scheduled on CPU0, explaining the imbalance. Since PHP‑FPM lacks an explicit affinity option like Nginx, the solution is to bind each worker to a specific CPU using taskset.
The following Bash script enumerates all CPUs, finds the PIDs of PHP‑FPM worker processes, and assigns each worker to a CPU in a round‑robin fashion:
#!/bin/bash
CPUs=$(grep -c processor /proc/cpuinfo)
PIDs=$(ps aux | grep "php-fpm[:] pool" | awk '{print $2}')
let i=0
for PID in $PIDs; do
CPU=$(echo "$i % $CPUs" | bc)
let i++
taskset -pc $CPU $PID
doneAfter executing the script, a new mpstat run shows a much more balanced distribution of load across all four CPUs:
CPU %usr %nice %sys %iowait %irq %soft ... %idle
all 15.73 0.03 1.61 0.00 0.20 0.23 ... 82.20
0 16.28 0.10 1.62 0.10 0.81 0.91 ... 80.18
1 16.16 0.10 1.51 0.00 0.00 0.10 ... 82.13
2 14.46 0.10 1.71 0.00 0.00 0.00 ... 83.73
3 15.95 0.00 1.71 0.00 0.00 0.00 ... 82.35One caveat: when a PHP‑FPM worker reaches the max_requests limit it is automatically restarted, which clears any affinity set by taskset. To keep the binding permanent, the script should be scheduled via cron (e.g., run every minute).
In practice the distribution can be tuned; for example, if CPU0 remains heavily loaded, you may choose to assign workers only to CPUs 1‑3.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
