Series: WordPress Performance on DirectAdmin (Rocky Linux 9)
Phase 1: Foundations — Part 2 of 30
WordPress Performance on DirectAdmin (Rocky Linux 9), Part 2: Baseline Measurement & Profiling
This post is part 2 of 30 in the “WordPress Performance on DirectAdmin (Rocky Linux 9)” series (slug: wp-perf-da), within Phase 1: Foundations. In part 1 we focused on basic sanity checks; here we build a reproducible baseline and profiling workflow before deeper tuning in later phases.
Goals and scope
Before changing PHP-FPM pools, NGINX/Apache templates, or MySQL, you need:
- A repeatable way to measure performance.
- Clear visibility into PHP, web server, and WordPress behavior.
- Per-domain baselines so you can compare before/after changes.
This article focuses on a single DirectAdmin-managed WordPress domain on Rocky Linux 9, but the same approach applies per domain.
1. Confirm environment and prerequisites
1.1 Identify DirectAdmin stack for the target domain
DirectAdmin can run:
- Apache with mod_php or PHP-FPM
- NGINX as reverse proxy in front of Apache
- NGINX-only (less common in default installs)
On Rocky Linux 9, you should prefer PHP-FPM. Confirm what your target domain uses:
sudo grep -R "php-fpm" /usr/local/directadmin/data/users/*/httpd.conf 2>/dev/null
sudo grep -R "nginx_proxy" /usr/local/directadmin/data/users/*/nginx.conf 2>/dev/null
Then inspect the user and domain-specific configs (replace user and example.com):
sudo cat /usr/local/directadmin/data/users/user/httpd.conf
sudo cat /usr/local/directadmin/data/users/user/nginx.conf 2>/dev/null || echo "no nginx for this user"
Note whether the VirtualHost for example.com uses php-fpm handlers and whether NGINX is proxying to Apache.
1.2 Confirm PHP-FPM pool for the domain
DirectAdmin typically creates per-user or per-domain PHP-FPM pools under:
/usr/local/phpXY/etc/php-fpm.d/(custom PHP builds), or/etc/php-fpm.d/(OS PHP, less common if using CustomBuild)
List pools:
sudo ls -1 /usr/local/php*/etc/php-fpm.d/ 2>/dev/null
Open the relevant pool (example for PHP 8.2, user user):
sudo sed -n '1,120p' /usr/local/php82/etc/php-fpm.d/user.conf
Record:
pm,pm.max_children,pm.max_requestslistensocket path or portphp_admin_value[error_log]if set
We’ll use these later when correlating load with PHP-FPM behavior.
2. Establish a clean WordPress performance baseline
2.1 Ensure WP-CLI is available
WP-CLI is essential for consistent testing. If not installed centrally, install under /usr/local/bin:
cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
sudo mv wp-cli.phar /usr/local/bin/wp
sudo chmod +x /usr/local/bin/wp
Test from the WordPress document root (e.g. /home/user/domains/example.com/public_html):
cd /home/user/domains/example.com/public_html
sudo -u user wp core version
2.2 Create a stable test page
For consistent benchmarks, avoid admin pages, logged-in sessions, and heavy plugins. Create or pick a simple public page (e.g. a static content page or a basic blog post).
- List pages:
cd /home/user/domains/example.com/public_html
sudo -u user wp post list --post_type=page --fields=ID,post_title,post_name,post_status --format=table
- If needed, create a “Perf Test” page:
sudo -u user wp post create \
--post_type=page \
--post_title="Perf Test" \
--post_status=publish \
--post_content="Baseline performance test page."
- Get the permalink:
sudo -u user wp post get <ID_FROM_ABOVE> --field=guid
Use that URL (or the pretty permalink) for all HTTP benchmarks in this phase.
3. Basic HTTP and TTFB measurements
3.1 Quick latency and TTFB with curl
Use curl from the same server to remove external network variance. Replace https://example.com/perf-test/ with your URL.
curl -o /dev/null -s -w \
'HTTP %{http_code}
Time_Namelookup %{time_namelookup}
Time_Connect %{time_connect}
Time_Appconnect %{time_appconnect}
Time_Pretransfer %{time_pretransfer}
Time_Starttransfer %{time_starttransfer}
Time_Total %{time_total}
' \
https://example.com/perf-test/
Key metrics:
- Time_Starttransfer: effectively TTFB; includes PHP and DB time.
- Time_Total: full response time.
Run it 5–10 times and note min/median/max. Store initial results (e.g. in a shared doc or Git-tracked notes) as your baseline.
3.2 Basic concurrency test with wrk
Install wrk (not in base Rocky 9 repos). From EPEL:
sudo dnf install -y epel-release
sudo dnf install -y wrk
Warning: Load generation can impact production users. Run during low-traffic windows and start with conservative settings.
Example low-intensity test:
wrk -t4 -c20 -d30s https://example.com/perf-test/
- -t4: 4 threads
- -c20: 20 concurrent connections
- -d30s: 30 seconds duration
Capture:
- Requests/sec
- Latency distribution (50%, 75%, 90%, 99%)
- Non-2xx/3xx responses (should be 0 for the test page)
Record these as your initial concurrency baseline.
4. Logging and visibility for profiling
4.1 Apache / NGINX access and error logs
DirectAdmin typically stores per-domain logs under the domain path. Common locations:
/var/log/httpd/domains/example.com.log/var/log/httpd/domains/example.com.error.log/var/log/nginx/domains/example.com.log/var/log/nginx/domains/example.com.error.log
Confirm for your domain:
sudo ls -1 /var/log/httpd/domains
sudo ls -1 /var/log/nginx/domains 2>/dev/null || echo "no nginx domain logs"
During tests, tail logs in another terminal:
sudo tail -F /var/log/httpd/domains/example.com.log \
/var/log/httpd/domains/example.com.error.log
If using NGINX reverse proxy, tail NGINX logs as well:
sudo tail -F /var/log/nginx/domains/example.com.log \
/var/log/nginx/domains/example.com.error.log
Look for:
- 5xx errors (500, 502, 503, 504)
- Slow requests if you have custom log formats with request time (we’ll add this in a later phase)
4.2 PHP-FPM slow log (per pool)
PHP-FPM slow logs are critical for profiling WordPress bottlenecks.
Warning: Editing PHP-FPM pool configs triggers pool reload/restart. Brief disruption is possible. Do this during a low-traffic window.
Open the pool config (example for user user, PHP 8.2):
sudo vi /usr/local/php82/etc/php-fpm.d/user.conf
Add or adjust:
request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm/user-slow.log
Create the log file and set permissions:
sudo touch /var/log/php-fpm/user-slow.log
sudo chown user:user /var/log/php-fpm/user-slow.log
sudo chmod 640 /var/log/php-fpm/user-slow.log
Reload the relevant PHP-FPM service (check service name using systemctl list-units | grep fpm):
sudo systemctl reload php-fpm82.service
Tail the slow log during wrk tests:
sudo tail -F /var/log/php-fpm/user-slow.log
Each entry will show the script and stack trace for requests exceeding 3 seconds, which often points to slow plugins, themes, or DB calls.
5. Baseline WordPress-level profiling
5.1 Check plugin and theme footprint
Use WP-CLI to see what’s active:
cd /home/user/domains/example.com/public_html
sudo -u user wp plugin list --status=active --fields=name,status,version
sudo -u user wp theme list --fields=name,status,version
For a baseline, consider temporarily disabling clearly non-essential heavy plugins (page builders, analytics, etc.) on a staging copy of the site to see the delta. In production, keep changes minimal and coordinated.
5.2 Measure page generation time from WordPress
Without installing heavy profiling plugins, you can add a short-lived mu-plugin to log generation time for the test page.
Warning: Code changes can break the site if misapplied. Test on staging first if possible.
Create wp-content/mu-plugins/perf-timer.php:
cd /home/user/domains/example.com/public_html
sudo -u user mkdir -p wp-content/mu-plugins
sudo -u user tee wp-content/mu-plugins/perf-timer.php >/dev/null <<'EOF'
<?php
add_action( 'template_redirect', function () {
if ( ! defined( 'WP_START' ) ) {
return;
}
$uri = $_SERVER['REQUEST_URI'] ?? '';
// Adjust path to your test page if needed.
if ( strpos( $uri, '/perf-test' ) !== 0 ) {
return;
}
add_action( 'shutdown', function () {
$elapsed = microtime( true ) - WP_START;
error_log( sprintf( 'PERF_TEST page=%s time=%.4f', $_SERVER['REQUEST_URI'] ?? '', $elapsed ) );
}, PHP_INT_MAX );
} );
EOF
This logs generation time to the PHP error log (often /var/log/httpd/domains/example.com.error.log or a pool-specific log). Hit the test URL and then:
sudo grep 'PERF_TEST' /var/log/httpd/domains/example.com.error.log | tail
Record typical values. Remove or comment out this mu-plugin after baseline collection to avoid log noise.
6. Good starting values for PHP-FPM and system limits
This phase is about measurement, but you should verify that your current PHP-FPM settings are not obviously broken before collecting baselines.
6.1 Check PHP-FPM pool limits
Open the pool config again:
sudo sed -n '1,160p' /usr/local/php82/etc/php-fpm.d/user.conf
For a typical small WordPress site (2–4 vCPU, 4–8 GB RAM) a reasonable starting point:
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
Warning: Misconfigured pm.max_children can cause 502/503 errors or memory exhaustion. Do not increase pm.max_children until you’ve measured memory usage under load (we’ll cover this in a later phase).
After changes:
sudo systemctl reload php-fpm82.service
6.2 Confirm systemd and firewalld state
Ensure services are healthy before and after tests:
sudo systemctl status httpd
sudo systemctl status nginx 2>/dev/null || echo "nginx not in use"
sudo systemctl status php-fpm82
Check firewall (if testing from external hosts):
sudo firewall-cmd --list-services
Make sure http and https are allowed:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
7. DirectAdmin-specific templates and per-domain tuning hooks
DirectAdmin uses templates for Apache and NGINX vhosts. In later phases we’ll customize these for logging and caching; for now, just identify where they live so you know what’s safe to change.
7.1 Locate Apache and NGINX templates
Common template directories:
/usr/local/directadmin/data/templates/custom/(preferred override location)/usr/local/directadmin/data/templates/(defaults; don’t edit directly if you can avoid it)
List templates:
sudo ls -1 /usr/local/directadmin/data/templates
sudo ls -1 /usr/local/directadmin/data/templates/custom 2>/dev/null || echo "no custom templates yet"
For Apache + NGINX proxy setups, you’ll usually see:
virtual_host2.conf/virtual_host2_secure.confnginx_server.conf/nginx_server_secure.conf
To customize safely, copy a template into the custom directory and then adjust it there. We’ll use this mechanism later for extended logging and caching headers.
7.2 Per-domain overrides
DirectAdmin supports per-domain custom configurations (e.g. Apache include files). For now, just note that for example.com you’ll see references in:
sudo grep -n 'example.com' /usr/local/directadmin/data/users/user/httpd.conf
In later phases, we’ll attach per-domain snippets (e.g. for SetEnvIf, Header, advanced logging) via these mechanisms instead of editing global configs.
8. Baseline checklist
Before moving on in the series, ensure you have:
- Identified the stack for the domain (Apache-only vs NGINX+Apache, PHP-FPM pool in use).
- Created or selected a stable public test page.
- Captured
curltiming metrics (TTFB and total time) for that page. - Run at least one low-intensity
wrktest and recorded RPS and latency distribution. - Enabled and tailed:
- Apache/NGINX access and error logs
- PHP-FPM slow log for the relevant pool
- Collected basic WordPress profiling data (active plugins/themes, optional perf-timer mu-plugin logs).
- Verified PHP-FPM pool settings are within reasonable starting values.
These baselines allow you to prove that future tuning (e.g. PHP-FPM pool sizing, opcode cache changes, full-page caching) actually improves performance and doesn’t just shift bottlenecks.
This article offers general technical guidance. Validate all configurations in a safe environment before applying them to production.
Previous: WordPress Performance Tuning on DirectAdmin: The Complete Guide
Next: Common WordPress Performance Mistakes on VPS Hosting


Leave a Reply