X-Ray is the PHP request tracer bundled with CloudLinux OS Pro. It samples slow requests on a per-account basis, captures full stack traces with timings down to the function call, and shows you where the time went — without the runtime cost of leaving Xdebug enabled across a shared server.
This guide is for hosts already running CloudLinux on cPanel or Plesk who want to turn the X-Ray plugin on, configure useful triggers, and read a trace without falling into the "trace everything" trap that fills up dashboards with noise.
How X-Ray works
X-Ray hooks into PHP as a Zend extension. On most requests it does nothing measurable. When a request crosses a wall-clock duration threshold you set (default 1000 ms), the tracer keeps the per-function timing data it has been buffering and writes it back to a central CloudLinux backend that the cPanel and Plesk plugins read.
You configure tracing on two axes:
- Per-account, on demand — kick off a 24-hour or 7-day trace for one user's domain while you investigate a complaint
- Continuous monitoring — leave a low-frequency sampler on for the whole server, catching outliers as they happen
Overhead on requests that don't trip the threshold is negligible. On a captured request, expect a few hundred milliseconds of additional latency for the trace write. That's the right trade — you only pay the cost on requests that were already slow.
Enable X-Ray on your server
X-Ray ships with CloudLinux OS Pro and Solo Pro. If you're on the free edition or CloudLinux OS Shared (non-Pro), upgrade through your license first — X-Ray is not sold as a standalone SKU any more.
Install the agent from the CloudLinux repos:
yum install x-ray-agent
On cPanel, the X-Ray entry appears in WHM under Plugins once cagefsctl --rebuild-alt-php-ini
finishes. On Plesk, the X-Ray icon shows under Server Management → Tools & Settings
after the agent registers with the backend.
Start the agent and check it's authenticated against your CloudLinux license:
systemctl enable --now x-ray-agent
x-ray-cli status
You want to see agent: running and a server token. If you get unregistered, run
x-ray-cli register and re-check. Failed registration is usually a firewall rule
blocking outbound 443 to api.cloudlinux.com.
Configure tracing triggers
The default trigger fires on requests slower than 1000 ms. That's a sensible starting point for shared WordPress hosting — most pages render in 200–400 ms, so a 1-second request is usually worth a look.
Per-domain tasks
In WHM, open Plugins → X-Ray Monitoring and click Add Task. Pick:
- A cPanel user
- One or more domains under that user
- A duration: 24 hours, 7 days, or until manually stopped
- A request-time threshold in milliseconds
For an active investigation, 24 hours at 500 ms captures enough samples to find the pattern without burying you in noise. For background monitoring of a known-troublesome account, run 7 days at 1500 ms.
Picking a threshold
The threshold is wall-clock time, so it includes time spent waiting on the database, on remote APIs, and on disk I/O. That's the right number for "the site feels slow" complaints. It is not the right number if you're hunting CPU-bound code on a server that's also memory-pressured — a swap event will trip every threshold and the traces will all blame whatever PHP happened to be on the stack at the time.
Set the threshold by what's actionable, not by what's interesting. If a customer's site relies on a third-party API that consistently takes 600 ms and they refuse to change it, set the threshold to 1500 ms and stop logging the calls you can't fix.
Read a trace
Each captured request becomes a trace in the X-Ray dashboard. Open one and you see three views:
- Timeline — every function call as a horizontal bar, indented by call depth, coloured by category (DB, file I/O, network, PHP execution)
- Hot spots — the same data sorted by total time spent inside the function
- Database queries — every SQL statement issued during the request, grouped by identical query, with execution time and origin file/line
Start with hot spots. The function at the top is where the time is. In WordPress, this
is almost always WP_Query::get_posts, wp_remote_get, or maybe_unserialize — meaning
the bottleneck is an unindexed meta query, a synchronous external API call, or bloated
serialised data being unpacked on every request.
If the top hot spot is mysqli_query or PDO::execute, switch to the database view.
X-Ray groups identical queries, so a plugin re-running
SELECT * FROM wp_options WHERE autoload = 'yes' 47 times in one request stands out
immediately. That's a missing object cache, not a slow database.
Common bottlenecks X-Ray surfaces
In practice, what X-Ray finds on shared hosting clusters into four buckets:
- Bloated autoloaded options in WordPress, where one plugin stuffs several megabytes
of serialised data into
wp_optionsand every request unpacks it - External API calls in the request path — analytics beacons, licence-check pings, geo lookups — that hang for 2–5 seconds when the upstream is degraded
- N+1 query patterns in WooCommerce extensions and custom themes that loop over posts and re-query meta on each iteration
- PHP session storage on disk when many sessions live in the same directory and inode-level lock contention serialises requests
The first two are fixable inside the customer's site without your involvement — send
them the trace. The third often is too; most plugin authors will ship a patch if you
hand them a labelled hot-spot screenshot. The fourth is yours: move sessions to tmpfs
or Redis, or partition session storage by user hash.
X-Ray data also feeds AccelerateWP's cache decisions — if you already run AccelerateWP, the traces it captures are visible in the same dashboard and worth reviewing before you tune cache policy by hand.
When X-Ray isn't the right tool
X-Ray traces PHP. It won't help you with:
- LiteSpeed or Nginx reverse-proxy delays — use LiteSpeed's access log timing fields for those
- MySQL server-side optimisation — use the slow query log and
EXPLAIN, not X-Ray's query view, which only shows what PHP asked for, not how MySQL planned it - System-level resource contention like LVE limits being hit — that's
the LVE Manager and
lveinfo
If you've already concluded the bottleneck is below PHP, X-Ray will confirm "PHP was waiting" but won't tell you what for.
Next steps
- Tune LVE limits for noisy neighbours once X-Ray shows resource starvation, not code, is the issue
- Configure AccelerateWP, which uses X-Ray traces to decide what to cache for WordPress accounts
- Switch MySQL Governor modes if X-Ray's database view keeps pointing at a handful of accounts hammering shared MySQL