Graph showing common WordPress performance mistakes affecting VPS hosting on Rocky Linux.

Common WordPress Performance Mistakes on VPS Hosting

, , ,

Series: WordPress Performance on DirectAdmin (Rocky Linux 9)

Phase 1: Foundations — Part 3 of 30

Baseline Metrics for WordPress on DirectAdmin (Rocky Linux 9)

This post is part 3 of the "WordPress Performance on DirectAdmin (Rocky Linux 9)" series (Phase 1: Foundations). Here we focus on establishing repeatable, low-noise baseline metrics before we start tuning PHP-FPM, NGINX/Apache, or MySQL in later phases.

Goals and scope

The objective is to build a simple, reproducible baseline for a DirectAdmin-managed WordPress instance on Rocky Linux 9:

  • Confirm the stack (PHP-FPM pools, web server mode, per-domain config paths).
  • Measure current performance under light and moderate load.
  • Capture system, PHP-FPM, and web server behavior with logs and metrics.
  • Document a repeatable test procedure you can re-run after every change.

We’ll assume:

  • DirectAdmin on Rocky Linux 9 (EL9), using PHP-FPM (recommended) and Apache or NGINX (or NGINX reverse-proxy to Apache).
  • Shell access with sudo.
  • At least one WordPress site under a DirectAdmin user (e.g. user1, domain example.com).

1. Confirm DirectAdmin stack and paths

1.1 Check web stack mode

In DirectAdmin, confirm which web stack is active:

  • DirectAdmin > Extra Features > CustomBuild 2.0 > Edit Options
  • Look for:
    • webserver (e.g. apache, nginx_apache, or nginx)
    • php1_mode (should be php-fpm for this series)

On Rocky Linux 9, confirm services:

sudo systemctl status httpd nginx php-fpm --no-pager

Typical DirectAdmin layouts:

  • Apache only: httpd + php-fpm
  • NGINX reverse-proxy to Apache: nginx + httpd + php-fpm
  • NGINX + PHP-FPM only: nginx + php-fpm

1.2 PHP-FPM pool locations (DirectAdmin)

DirectAdmin manages per-user PHP-FPM pools. Common EL9 paths:

  • Global config: /etc/php-fpm.conf
  • Pool configs: /etc/php-fpm.d/*.conf
  • DirectAdmin-managed pools (by user): /usr/local/directadmin/data/users/<user>/php/php-fpm<version>.conf (source templates)

List pools:

grep -R "\[" /etc/php-fpm.d/*.conf

sudo php-fpm -tt

We won’t change pool settings yet; we just need to know where they are for later phases.

1.3 Per-domain web server configs

DirectAdmin generates per-domain configs from templates:

  • Apache vhosts: /etc/httpd/conf/extra/httpd-vhosts.conf (includes per-domain files in /usr/local/directadmin/data/users/<user>/httpd)
  • NGINX: /etc/nginx/nginx.conf with includes from /etc/nginx/conf.d/ and /usr/local/directadmin/data/users/<user>/nginx.conf
  • Templates (do not edit lightly): /usr/local/directadmin/data/templates/

We’ll later modify templates for persistent tuning; for baselining, we only read configs and logs.

2. Install basic tooling on Rocky Linux 9

Install minimal load testing and diagnostic tools. These commands are safe and idempotent.

sudo dnf install -y epel-release

# HTTP load tools (choose at least one)
sudo dnf install -y wrk
# or:
sudo dnf install -y golang-k6

# Network debugging
sudo dnf install -y curl

# System observation
sudo dnf install -y htop iotop strace

# Log viewing helpers
sudo dnf install -y jq

Ensure firewalld allows HTTP/HTTPS:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

3. Prepare WordPress for testing

3.1 Confirm WP-CLI

WP-CLI is the safest way to interact with WordPress from the shell.

cd /var/www/html 2>/dev/null || true
# DirectAdmin often uses:
# /home/<user>/domains/<domain>/public_html

# Example:
cd /home/user1/domains/example.com/public_html

wp core version

If wp is not found, install it (safe):

cd /usr/local/bin
sudo curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo php wp-cli.phar --info
sudo chmod +x wp-cli.phar
sudo mv wp-cli.phar wp

Then re-run:

cd /home/user1/domains/example.com/public_html
wp core version

3.2 Create a test page

Use a dedicated page for performance checks (no heavy plugins or builders).

cd /home/user1/domains/example.com/public_html

# Create a simple "baseline" page via WP-CLI
wp post create --post_type=page --post_title="Baseline Test" \
  --post_status=publish --post_content="Baseline performance test page."

Get the permalink:

wp post list --post_type=page --fields=ID,post_title,post_name,post_status

Assume slug is baseline-test; URL will be something like:

https://example.com/baseline-test/

4. Establish single-request baselines

4.1 Warm up PHP-FPM and caches

First hit the page a few times to warm PHP-FPM, opcode cache, and any page cache plugin.

curl -k -I https://example.com/baseline-test/
curl -k -I https://example.com/baseline-test/
curl -k -I https://example.com/baseline-test/

Use -k only if you have self-signed certificates; otherwise omit it.

4.2 Measure TTFB and response size

Use curl with timing:

curl -k -o /dev/null -s -w \
  "dns: %{time_namelookup}s connect: %{time_connect}s ttfb: %{time_starttransfer}s total: %{time_total}s size: %{size_download} bytes
" \
  https://example.com/baseline-test/

Record:

  • time_starttransfer (TTFB)
  • time_total (full load)
  • size_download

Do 5 runs and record the median. A reasonable starting goal on a small VPS (1–2 vCPU) for a simple page:

  • TTFB: < 250 ms (local network)
  • Total: < 400 ms

5. Light load testing (wrk)

Warning: Load tests can temporarily increase CPU and memory usage. Avoid running this on production during peak traffic.

5.1 Basic wrk test

From the server itself (low network latency):

wrk -t2 -c20 -d30s https://example.com/baseline-test/
  • -t2: 2 threads
  • -c20: 20 concurrent connections
  • -d30s: 30 seconds

Record:

  • Requests/sec
  • Latency distribution (50%, 75%, 90%, 99%)

Good starting expectations for a modest DirectAdmin EL9 VPS (2 vCPU, 4 GB RAM, basic PHP-FPM config):

  • Simple cached page: 500–1500 req/s
  • Uncached dynamic page: 50–300 req/s

5.2 Optional: k6 script

If you prefer more structured scenarios, create a simple k6 script:

cat > baseline.js << 'EOF'
import http from 'k6/http';
import { check } from 'k6';

export let options = {
  vus: 20,
  duration: '30s',
};

export default function () {
  let res = http.get('https://example.com/baseline-test/');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'ttfb < 300ms': (r) => r.timings.waiting < 300,
  });
}
EOF

k6 run baseline.js

Again, run off-peak and record throughput and 95th percentile latency.

6. Observe logs and PHP-FPM behavior

6.1 Tail web server logs

While running wrk or k6, tail logs in another terminal.

Apache logs (DirectAdmin per-domain)

sudo tail -f /var/log/httpd/access_log /var/log/httpd/error_log

Or per-domain logs, often:

sudo tail -f /var/log/httpd/domains/example.com.log \
  /var/log/httpd/domains/example.com.error.log

NGINX logs

sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log

# Per-domain (DirectAdmin):
sudo tail -f /var/log/nginx/domains/example.com.log \
  /var/log/nginx/domains/example.com.error.log

Watch for:

  • 5xx errors (502/503/504)
  • Long request times in access logs (if configured)
  • Upstream timeouts (NGINX + PHP-FPM/Apache)

6.2 PHP-FPM logs and status

Check PHP-FPM logs (path may vary by PHP version, e.g. php-fpm80):

sudo journalctl -u php-fpm --since "10 minutes ago" --no-pager

Look for:

  • server reached pm.max_children setting
  • Slow script warnings
  • Pool-level errors for the DirectAdmin user

List pools and basic settings:

grep -E "^\[|pm\." /etc/php-fpm.d/*.conf

We’ll tune pm.max_children, pm.max_requests, etc., in a later phase; for now, just note current values.

7. System-level metrics during load

Run a light load test while observing system metrics.

7.1 CPU, memory, load

htop

Key points during the 30s test:

  • CPU usage > 90%: may be CPU-bound (PHP, MySQL, or NGINX/Apache).
  • High load average (much > vCPU count): potential process contention or I/O wait.
  • Swap usage increasing: memory pressure; PHP-FPM pool size may be too large.

7.2 Disk I/O

sudo iotop -o

Watch for:

  • High write IOPS from MySQL or PHP logs.
  • High read IOPS from WordPress files (plugins/themes) or MySQL data.

8. Quick WordPress-level checks

8.1 Check active plugins and theme

From the site directory:

cd /home/user1/domains/example.com/public_html

wp plugin list --status=active
wp theme list

Note any heavy plugins (page builders, security suites, backup plugins running on-page loads). Disable non-essential plugins during baseline tests to isolate platform performance:

# Example: disable a non-critical plugin
wp plugin deactivate some-heavy-plugin

Record which plugins were active during baseline so you can keep tests comparable later.

8.2 Confirm caching behavior

For each test run, check response headers:

curl -k -I https://example.com/baseline-test/

Look for:

  • Page cache plugin headers (e.g. x-litespeed-cache, x-wp-cache, x-cache).
  • Proxy/cache headers (e.g. via, x-cache if behind an external CDN).

Record whether baselines are "cached" or "uncached". You’ll want both in later phases.

9. Build a repeatable baseline checklist

Use this short checklist every time you change DirectAdmin templates, PHP-FPM pools, or NGINX/Apache configs in later phases.

9.1 Pre-test checklist

  • Confirm services:
    • sudo systemctl status nginx httpd php-fpm
  • Confirm WordPress is reachable:
    • curl -k -I https://example.com/baseline-test/
  • Confirm domain logs exist:
    • ls /var/log/httpd/domains/ (Apache)
    • ls /var/log/nginx/domains/ (NGINX)
  • Note plugin and theme state:
    • wp plugin list --status=active
    • wp theme list

9.2 Baseline test sequence

  1. Warm up:
    • curl -k -I https://example.com/baseline-test/ (3–5 times)
  2. Single-request timing:
    • Run the curl -w command 5 times and record median TTFB and total.
  3. Light load:
    • wrk -t2 -c20 -d30s https://example.com/baseline-test/
    • Record requests/sec and 95th percentile latency.
  4. Observe logs during the wrk run:
    • Web server access/error logs.
    • PHP-FPM journal logs.
  5. Observe system during the wrk run:
    • htop for CPU/memory.
    • iotop -o for disk I/O.
  6. Document results:
    • Include date/time, config versions, plugin list, and any anomalies.

10. Notes on DirectAdmin template safety

DirectAdmin regenerates configs from templates (e.g. in /usr/local/directadmin/data/templates/). Any manual edits in /etc/httpd or /etc/nginx can be overwritten by CustomBuild runs.

For this baseline phase:

  • Do not edit template files yet.
  • Only inspect existing configs and logs.
  • In later phases we’ll use custom templates (e.g. custom/nginx_server.conf) for persistent tuning.

11. What to keep for future phases

Before moving on to tuning PHP-FPM pools and NGINX/Apache in the next posts, keep the following data:

  • Single-request TTFB and total time for /baseline-test/.
  • wrk (or k6) throughput and latency metrics.
  • PHP-FPM pool settings for the DirectAdmin user (from /etc/php-fpm.d).
  • Web server mode (Apache, NGINX, or NGINX+Apache) from DirectAdmin CustomBuild.
  • Active plugin list and theme.
  • Any 5xx errors or timeouts observed in logs under load.

These baselines are your reference when we later adjust:

  • PHP-FPM pm.* values per DirectAdmin user.
  • NGINX/Apache timeouts, buffers, and keepalive settings.
  • OPcache and MySQL configuration on Rocky Linux 9.

This article offers general technical guidance. Validate all configurations in a safe environment before applying them to production.

Previous: How WordPress Actually Handles a Page Request (And Where Performance Is Lost)

Next: DirectAdmin vs cPanel for WordPress Performance

Smart reads for curious minds

We don’t spam! Read more in our privacy policy

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *