A slow forum is rarely fixed by changing one PHP setting or buying a larger server blindly. The useful question is not Is the server slow? but Which queue grows when users feel the slowdown?
This runbook separates four common causes of XenForo latency: CPU contention, PHP-FPM saturation, MySQL work, and storage delay. Every command below is read-only unless a step explicitly says otherwise. Capture a normal window and an incident window with the same commands; isolated screenshots are weak evidence.
1. Define the symptom before touching the stack
Record one UTC interval when the forum is healthy and one when it is slow. For each interval, keep:
- the affected URL class: thread view, search, login, posting, attachment upload, or admin task
- application response time, preferably p50, p95, and p99 rather than only an average
- HTTP status codes and upstream response time from the web-server access log
- concurrent requests and logged-in user count
- scheduled jobs, backups, imports, search indexing, and add-on maintenance running at that time.
Use one incident directory so timestamps line up:
incident="/var/tmp/xf-$(date -u +%Y%m%dT%H%M%SZ)"
install -d -m 700 "$incident"
date -u --iso-8601=seconds | tee "$incident/start.txt"
uname -a > "$incident/kernel.txt"
uptime > "$incident/uptime.txt"
free -m > "$incident/memory.txt"
df -hT > "$incident/filesystems.txt"
Do not publish raw logs before removing IP addresses, usernames, query strings, cookies, tokens, and private paths.
2. Decide whether the wait begins at CPU or storage
Collect CPU, run-queue, pressure-stall, process, and block-device data during the same 60-second window:
LC_ALL=C mpstat -P ALL 1 60 > "$incident/mpstat.txt" &
LC_ALL=C vmstat -w -t 1 60 > "$incident/vmstat.txt" &
LC_ALL=C pidstat -u -d -w -p ALL 1 60 > "$incident/pidstat.txt" &
LC_ALL=C iostat -xz -t 1 60 > "$incident/iostat.txt" &
wait
Interpret combinations, not single columns:
- A sustained run queue above the available vCPU count, high CPU pressure, and low idle time points to guest-side CPU saturation.
- Rising steal time that coincides with application latency can indicate hypervisor scheduling delay. A zero value does not prove that the host is perfect, but a correlated rise is useful evidence.
- Rising read or write latency, queue depth, and I/O pressure points toward the storage path. Do not treat device utilization alone as a universal saturation signal on virtual disks or modern SSDs.
- High iowait is a clue, not a diagnosis. Correlate it with device latency, pressure, and the processes issuing I/O.
If /proc/pressure/cpu and /proc/pressure/io exist, sample them once per second. PSI tells you how much time runnable work is stalled, which is often closer to the user-visible problem than a five-minute load average.
3. Check PHP-FPM as a queue, not as a memory formula
A busy forum can be slow while the host still has free CPU because requests are waiting for PHP workers. Enable the PHP-FPM status endpoint only on a private management path protected by an allowlist or local socket. Never expose it publicly.
During the incident, compare:
- active processes versus the configured maximum
- max-active-processes and max-children-reached counters
- listen-queue depth and its historical maximum
- slow-request records from the FPM slow log
- resident memory per worker under realistic traffic.
If max children reached increases while the CPU is not saturated, requests are queuing at FPM. Raising the worker limit may help only when memory and database capacity exist. Estimate from observed RSS, leave room for MySQL, the kernel page cache, the web server, and traffic spikes, then change one variable in a maintenance window.
If workers are busy for a long time, adding more can amplify the real bottleneck. Use the slow log to find whether workers wait on database queries, remote APIs, filesystem operations, or add-on code.
4. Separate MySQL concurrency from slow individual queries
Start with current state:
SHOW GLOBAL STATUS WHERE Variable_name IN (
'Threads_connected', 'Threads_running',
'Created_tmp_disk_tables', 'Created_tmp_tables',
'Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests',
'Innodb_row_lock_time', 'Innodb_row_lock_waits'
);
SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS;
Capture these at the same time as the Linux metrics. A large connection count is not automatically bad; Threads_running, lock waits, and query latency are more useful. Likewise, a buffer-pool miss counter is cumulative, so compare deltas over a fixed interval.
If Performance Schema is enabled, identify statement digests with high total latency, high rows examined, or repeated executions. Inspect representative queries with EXPLAIN on a staging copy or during a controlled diagnostic window. Do not add indexes from intuition alone: an index can improve reads while increasing write cost and storage.
A temporary slow-query-log window can be valuable, but enabling it changes server state and may write sensitive query data. Plan it, protect the log, set a short observation period, and disable it after collection.
5. Test XenForo-specific suspects one at a time
Infrastructure graphs cannot identify every application cause. Common forum-side suspects include:
- an add-on executing work on every page view
- search or indexing tasks overlapping peak traffic
- attachment or image processing bursts
- scheduled jobs, email queues, imports, and sitemap generation
- external API calls inside request handling
- session, permission, or template work made expensive by an add-on
- a large thread or search path that produces unusually heavy queries.
Reproduce the slow URL with the same account permissions in staging. Disable one suspect add-on at a time there, keep caches and dataset size comparable, and record the before-and-after request trace. Clearing every cache, restarting every service, and changing multiple limits simultaneously destroys the evidence you need.
6. Use a decision table
| Evidence during the same interval | First hypothesis | Next check |
|---|---|---|
| FPM queue grows; CPU and MySQL are calm | Worker capacity or blocked PHP calls | FPM slow log and per-worker RSS |
Threads_running and lock waits rise |
Database contention | statement digests, InnoDB status, transaction scope |
| CPU PSI and run queue rise; steal stays low | Guest CPU saturation | hot processes, add-ons, request mix |
| Steal rises with latency; guest CPU is not full | Hypervisor scheduling pressure | repeatable timestamps for the provider |
| I/O PSI and device latency rise together | Storage-path contention | per-process I/O, backup/index tasks, provider evidence |
| Only one route is slow | Application/query path | request trace and query digest |
The table is a prioritization tool, not proof by itself. Repeat the observation and look for the same correlation before migrating or resizing.
7. Scale only after the bottleneck has a name
More vCPU helps a CPU-bound workload only when useful work can run in parallel. Faster single-core performance can matter for serial PHP execution, but it will not fix a locked database transaction. More RAM can reduce disk reads when the working set does not fit, but it will not repair an unindexed query. NVMe can reduce storage latency, but it will not clear an FPM listen queue caused by a remote API.
When comparing shared hosting, VPS, or VDS, carry your evidence into the decision: sustained CPU demand, measured memory working set, database size, p95 storage latency, peak concurrent PHP workers, backup window, and growth margin. The product label is less important than verified resource boundaries and an upgrade path.
A practical handoff bundle
A useful incident bundle contains UTC start and end times, the affected URLs, application percentiles, web-server upstream timings, FPM status and slow traces, MySQL snapshots, Linux CPU/I/O evidence, exact package versions, and a short list of changes made. Add checksums after collection so later edits are visible.
That bundle turns “the forum feels slow” into a claim another engineer or provider can test.
Disclosure: This article was prepared by the EniyiSunucum infrastructure team, which provides server and hosting services. Readers evaluating managed environments can compare XenForo hosting options, but the diagnostic method above is provider-neutral and contains no unpublished benchmark claims. AI assistance was used for structure and language editing; commands, interpretations, and links were reviewed before publication.