Series: WordPress Performance on DirectAdmin (Rocky Linux 9)
Phase 2: PHP & Runtime — Part 6 of 30
Overview: Why PHP Handler Choice Matters
This post is part of the “WordPress Performance on DirectAdmin (Rocky Linux 9)” series (slug=wp-perf-da), part 6 of 30, in Phase 2 (PHP & Runtime). Here we focus on the PHP handler: PHP-FPM vs mod_php.
On DirectAdmin with Rocky Linux 9, your main options are:
- PHP-FPM (FastCGI Process Manager) behind Apache, or NGINX+Apache
- mod_php (libapache2-style PHP module) directly in Apache workers
For WordPress, PHP-FPM is almost always faster and more resource-efficient, especially under concurrency. mod_php is simpler but ties PHP processes to Apache and makes scaling/tuning harder.
DirectAdmin on Rocky 9: What You Actually Have
On Rocky Linux 9 with a current DirectAdmin stack, the usual layouts are:
- Apache + PHP-FPM (per-domain pools under
/usr/local/php*/sockets) - NGINX (reverse proxy) + Apache + PHP-FPM
- Apache + mod_php (older or simplified setups)
DirectAdmin manages PHP versions and handlers via CustomBuild. Configs live in:
/etc/httpd/conf/httpd.confand/etc/httpd/conf/extra/httpd-php*.conf(Apache)/etc/nginx/nginx.confand/etc/nginx/conf.d/directadmin.conf(NGINX proxy)/usr/local/directadmin/data/users/<user>/php-fpm*.conf(per-user/pool PHP-FPM)
All examples below assume:
- Rocky Linux 9
- systemd managed services:
httpd,nginx,php-fpmXX - DirectAdmin CustomBuild 2.0
mod_php: How It Works and Where It Fails
Execution model
With mod_php, Apache loads PHP as a module. Each Apache worker can run PHP directly. Typical DirectAdmin/Apache config uses prefork MPM for mod_php to avoid thread-safety issues.
Key consequences:
- Every Apache child process is “fat” (includes PHP interpreter)
- Max concurrent PHP requests ≈
MaxRequestWorkers(orMaxClientsin older configs) - Memory per Apache worker is high; idle workers still hold PHP memory
Performance characteristics
- Pros
- Simpler stack, fewer moving parts
- Low latency for very low traffic sites
- Cons
- Poor concurrency scaling; memory usage explodes under load
- Harder to isolate per-domain resource usage
- Less compatible with NGINX reverse proxy patterns DirectAdmin prefers now
For WordPress with any real traffic, mod_php tends to hit RAM limits first, causing swap and latency spikes.
PHP-FPM: Why It Usually Wins
Execution model
PHP-FPM runs a dedicated pool of PHP worker processes and speaks FastCGI. Apache (via proxy_fcgi) or NGINX passes PHP requests to a pool.
On DirectAdmin, each user (and often each domain) has its own pool file, for example:
/usr/local/directadmin/data/users/example/php-fpm72.conf
/usr/local/directadmin/data/users/example/php-fpm81.conf
Typical pool socket path:
listen = /usr/local/php81/sockets/example.com.sock
Performance characteristics
- Pros
- Per-pool process limits (
pm.max_children) give predictable memory usage - Better throughput under concurrency than mod_php
- Per-domain tuning possible in DirectAdmin
- Safer to use with event-based Apache MPM or NGINX
- Per-pool process limits (
- Cons
- Slightly more complex debugging (one more service)
- Misconfigured pools (too few or too many children) can hurt performance
In practice, for WordPress on Rocky 9 with DirectAdmin, PHP-FPM is the correct default. mod_php is only reasonable for tiny, low-memory, single-site boxes where simplicity beats scalability.
Checking What You’re Running Now
As root or sudoer on Rocky 9:
sudo apachectl -M | egrep 'php|proxy_fcgi|mpm'
Interpretation:
- mod_php active:
- Something like
php_module (shared) - Likely
mpm_prefork_moduleenabled
- Something like
- PHP-FPM active:
proxy_moduleandproxy_fcgi_moduleloaded- No
php_module
Check PHP-FPM services:
sudo systemctl list-units 'php-fpm*'
On a typical DirectAdmin/Rocky 9 install, you’ll see something like php-fpm81.service.
Switching DirectAdmin from mod_php to PHP-FPM
Warning: This change can cause downtime if misconfigured. Perform during a maintenance window and have console access.
1. Confirm current CustomBuild settings
cd /usr/local/directadmin/custombuild
./build options | egrep 'php[0-9]|php_fpm|mod_php'
Look for lines like:
php1_release=8.1php1_mode=mod_phporphp1_mode=php-fpm
2. Plan handler change
To switch primary PHP to PHP-FPM:
cd /usr/local/directadmin/custombuild
sudo ./build set php1_mode php-fpm
If you have multiple PHP versions, set each as needed (e.g. php2_mode).
3. Rebuild Apache/PHP configs
Downtime risk: Apache reloads; PHP handler changes. Expect 10–30 seconds of disruption.
cd /usr/local/directadmin/custombuild
sudo ./build apache
sudo ./build php n
sudo ./build rewrite_confs
Then restart services:
sudo systemctl restart httpd
sudo systemctl restart php-fpm81
# If using NGINX proxy:
sudo systemctl restart nginx
Verify modules again:
sudo apachectl -M | egrep 'php|proxy_fcgi|mpm'
You should see proxy_fcgi_module and no php_module.
Per-Domain PHP-FPM Tuning in DirectAdmin
Where DirectAdmin stores pool configs
Per-user pool files are under:
/usr/local/directadmin/data/users/<user>/php-fpmXX.conf
DirectAdmin may regenerate these from templates located in:
/usr/local/directadmin/data/templates/php-fpmXX.conf
/usr/local/directadmin/data/templates/custom/php-fpmXX.conf
To keep changes persistent, use custom/ templates rather than editing generated files directly.
Good starting values for a small WordPress site
Assume:
- 2 vCPUs
- 4 GB RAM
- Single medium WordPress site
Per-pool snippet (conceptual; adjust via templates):
[example.com]
user = example
group = example
listen = /usr/local/php81/sockets/example.com.sock
listen.owner = example
listen.group = example
pm = dynamic
pm.max_children = 12
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
pm.max_requests = 500
request_terminate_timeout = 120s
Rationale:
- pm.max_children=12: if each PHP process uses ~80–120 MB, that’s ~960–1440 MB, plus MySQL, web server, OS. Fit within 4 GB.
- pm.max_requests=500: recycles workers periodically to mitigate memory leaks.
Applying template-based tuning
Warning: Template mistakes can break all PHP-FPM pools. Validate syntax before reload.
- Copy default template to custom:
sudo mkdir -p /usr/local/directadmin/data/templates/custom cd /usr/local/directadmin/data/templates sudo cp php-fpm81.conf php-fpm81.conf.backup sudo cp php-fpm81.conf custom/php-fpm81.conf - Edit
custom/php-fpm81.confwith your preferred editor:sudo vi /usr/local/directadmin/data/templates/custom/php-fpm81.confAdjust the
pm.*directives in the per-user section, using DirectAdmin tokens where appropriate. Keep syntax valid INI. - Rebuild configs and reload:
cd /usr/local/directadmin/custombuild sudo ./build php_fpm sudo ./build rewrite_confs sudo systemctl reload php-fpm81
measuring: PHP-FPM vs mod_php on Rocky 9
1. Warm up caches
- Ensure WordPress object/page cache is enabled (covered in other phases).
- Run a few warm-up hits:
curl -s -o /dev/null -w '%{time_total} ' https://example.com/
2. Basic latency checks
Measure single-request latency before and after handler changes:
for i in {1..5}; do
curl -s -o /dev/null -w '%{time_total}
' https://example.com/
done
Compare average values for mod_php vs PHP-FPM. You should see similar or slightly improved latency with PHP-FPM.
3. Concurrency tests with wrk
Install wrk (if not already available):
sudo dnf install -y epel-release
sudo dnf install -y wrk
Run from another host (preferred) or localhost:
wrt -t4 -c40 -d60s https://example.com/
Key metrics:
- Requests/sec: higher is better
- Latency distribution: look at 95th/99th percentile
PHP-FPM should maintain better latency at higher concurrency than mod_php, given correct pm.max_children.
4. Using k6 for WordPress flows
For more realistic tests (logins, cart, etc.), use k6. Install:
sudo dnf install -y k6
Example simple script (load.js):
import http from 'k6/http';
import { sleep } from 'k6';
export let options = {
vus: 20,
duration: '2m',
};
export default function () {
http.get('https://example.com/');
sleep(1);
}
Run:
k6 run load.js
Compare results between mod_php and PHP-FPM setups.
Observability: Watching PHP-FPM Under Load
System-level monitoring
During tests, observe CPU, memory, and process counts:
top -c
Look for php-fpm: pool example.com processes and track their count vs pm.max_children.
Logs
Tail web server and PHP-FPM logs while testing:
sudo tail -f /var/log/httpd/access_log /var/log/httpd/error_log
sudo tail -f /var/log/php-fpm/www-error.log 2>&1 | egrep 'slow|error'
On DirectAdmin, per-domain logs may be under:
/var/log/httpd/domains/example.com.error.log
/var/log/httpd/domains/example.com.log
WordPress-level checks (WP-CLI)
Use WP-CLI to quickly verify site health after handler changes:
cd /home/example/domains/example.com/public_html
sudo -u example -s -- wp core verify-checksums
sudo -u example -s -- wp plugin status
sudo -u example -s -- wp option get home
This confirms WordPress is still functional and paths/permissions are intact.
Practical Recommendations
When to use PHP-FPM
- Any multi-site or multi-tenant DirectAdmin server
- Any site with concurrent traffic > 5–10 requests/sec
- When using NGINX reverse proxy
- When you need per-domain resource isolation
When mod_php might be acceptable
- Single tiny site, very low traffic
- Legacy application requiring specific Apache/PHP module behavior
- You fully accept scalability limitations and want minimal moving parts
For most DirectAdmin/Rocky 9 WordPress deployments, PHP-FPM is faster at scale, safer for multi-tenancy, and aligns better with current DirectAdmin templates and best practices.
Quick Checklist: Migrating Safely to PHP-FPM
- Baseline:
- Record current
apachectl -Moutput - Measure current latency with
curland throughput withwrkork6
- Record current
- Plan:
- Estimate available RAM and choose initial
pm.max_children - Schedule a maintenance window
- Estimate available RAM and choose initial
- Change:
- Use DirectAdmin CustomBuild to set
php*_mode=php-fpm - Rebuild Apache/PHP and rewrite configs
- Restart/reload
httpd,php-fpm*, andnginxif used
- Use DirectAdmin CustomBuild to set
- Verify:
- Confirm
proxy_fcgi_moduleloaded,php_moduleabsent - Check logs for errors and 5xx responses
- Run WP-CLI health checks
- Confirm
- Tune:
- Adjust pool settings via DirectAdmin templates
- Retest with load tools, watch CPU/RAM
Note: This article offers general technical guidance. Validate all configurations in a safe environment before applying them to production.
Previous: How to Configure PHP 8.3 for Maximum WordPress Performance on DirectAdmin
Next: OPcache Explained: How to Properly Enable and Tune It for WordPress


Leave a Reply