The Apache MPM (Multi-Processing Module) decides how Apache accepts connections, how many it can hold open at once, and how much RAM each one costs. On a cPanel server it is one of the two or three settings that actually moves the needle under real load — yet most boxes are still on the wrong MPM, or on the right MPM with MaxRequestWorkers left at the EasyApache default.
This is the working guide for cPanel admins: which MPM to pick, how to switch it, the numbers to set, and the mistakes that turn a healthy server into one that flatlines at 200 concurrent requests.
The three MPMs, side by side
| MPM | Concurrency model | RAM per active connection | PHP handler compatibility | Best for |
|---|---|---|---|---|
| event | Multi-process, multi-threaded, dedicated listener for keepalives | Lowest | PHP-FPM, mod_lsapi, mod_fcgid | Almost every modern cPanel server |
| worker | Multi-process, multi-threaded | Low | PHP-FPM, mod_lsapi, mod_fcgid | Niche — old kernels without proper kqueue/epoll |
| prefork | One process per connection | Highest | All of the above + non-thread-safe mod_php (gone from EA4) | Legacy boxes that haven't moved off DSO |
If you're on EasyApache 4 with PHP-FPM (the default since EA4 11.96), the correct answer is event. worker exists for completeness; prefork is a tax you keep paying because of an obsolete handler choice. The full handler discussion is in PHP handlers in cPanel: LSAPI vs PHP-FPM vs suPHP — read that first if you're still on suPHP or mod_php DSO, because the MPM choice is downstream of the handler choice.
Check which MPM you're running
httpd -V 2>/dev/null | grep -i mpm
Output should be Server MPM: event on a healthy box. If it says prefork, finish this article and switch — there is no good reason to be on prefork in 2026 unless you have a specific module that requires it (and almost none do).
The module file that selects the MPM:
cat /etc/apache2/conf.modules.d/00-mpm.conf
Exactly one LoadModule mpm_*_module line should be uncommented.
Switching MPM in EasyApache 4
Two ways. The UI is safer if you're new to EA4; the CLI is faster if you know what you're doing.
WHM UI: Home → Software → EasyApache 4 → Customize on the current profile → Apache MPM → tick event → Review → Provision. EA4 handles the package swap and the 00-mpm.conf update atomically; Apache reloads at the end. Expect 30-90 seconds of build time, no downtime.
CLI:
yum -y swap -- remove ea-apache24-mpm-prefork -- install ea-apache24-mpm-event
/scripts/restartsrv_httpd
The swap form is important — installing the event package without removing prefork leaves both MPM modules dropped on disk, and 00-mpm.conf ends up with two LoadModule lines. Apache refuses to start with a Cannot load more than one MPM error.
The MaxRequestWorkers math
This is the setting that decides your real concurrency ceiling. The EasyApache default is 256 — fine for a developer's laptop, far too low for a busy shared-hosting server.
For event and worker MPMs, the formula is:
MaxRequestWorkers = ServerLimit × ThreadsPerChild
You want MaxRequestWorkers to be high enough that Apache can absorb a traffic spike without rejecting connections, but bounded by something the box can actually serve — every accepted connection eventually proxies to an FPM worker or LSAPI worker, and those have their own RAM cost. A useful starting point for a 16 GB box running PHP-FPM:
<IfModule mpm_event_module>
ServerLimit 40
StartServers 4
ThreadsPerChild 50
ThreadLimit 64
MaxRequestWorkers 2000
MinSpareThreads 100
MaxSpareThreads 400
MaxConnectionsPerChild 10000
</IfModule>
Drop these into a Pre-Main include rather than editing the shipped MPM config — WHM → Service Configuration → Apache Configuration → Include Editor → Pre-Main Include → All Versions. Anything in there survives EasyApache rebuilds. Direct edits to /etc/apache2/conf.d/mpm_event.conf do not — the next EA4 provision overwrites them.
A few notes on the numbers:
ThreadLimitmust be ≥ThreadsPerChild, and it sets the hard ceiling for the life of the process. Set it slightly higher than you think you need; changing it later requires a full restart, not a reload.MaxConnectionsPerChild = 10000recycles processes after that many connections. Useful against slow memory leaks in third-party modules. Set to0to disable recycling on a box you trust.MinSpareThreads/MaxSpareThreadskeep idle threads available for traffic bursts. Too low and the first 50 visitors after a quiet period pay a fork-and-thread-start tax; too high and you waste RAM on empty threads.
For prefork (if you're stuck on it temporarily):
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 10
MaxSpareServers 20
ServerLimit 300
MaxRequestWorkers 300
MaxConnectionsPerChild 4000
</IfModule>
Prefork has no thread settings because each process handles one connection. The RAM cost is per-process — a typical prefork worker with mod_php loaded is 30-80 MB, so MaxRequestWorkers = 300 on a 16 GB box is the ceiling, not a target.
The KeepAlive trap
This is where event MPM earns its place. With prefork, every keepalive connection ties up a whole process — a browser holding open a connection for 5 seconds occupies a worker that could be serving a different visitor. Many old cPanel guides tell you to disable KeepAlive entirely on prefork; that advice is correct for prefork and actively wrong for event.
On event MPM, idle keepalive connections move to a dedicated listener thread, freeing the worker thread for the next request. KeepAlive becomes cheap. Settings that work:
KeepAlive On
KeepAliveTimeout 5
MaxKeepAliveRequests 100
If you still see KeepAliveTimeout 1 (or KeepAlive Off) on an event-MPM box, that's a setting copied from the prefork era. Raise it back up — TCP and TLS handshakes are expensive, and reusing the connection saves real CPU on TLS-heavy traffic.
Watch what's actually happening
Enable mod_status to see the live scoreboard. EA4 ships it disabled by default. Add a Pre-Main Include:
ExtendedStatus On
<Location /whm-server-status>
SetHandler server-status
Require ip 127.0.0.1 203.0.113.42
</Location>
Then:
curl -s http://127.0.0.1/whm-server-status?auto | head -20
The fields that matter: BusyWorkers, IdleWorkers, ConnsTotal, and the scoreboard string. Scoreboard letters: _ waiting, W writing reply, K keepalive, R reading request, D DNS lookup, C closing, G graceful finish, . slot open. A healthy server has mostly _ and K with bursts of W. A server that's pegged shows W filling the entire scoreboard and Server reached MaxRequestWorkers in /etc/apache2/logs/error_log.
That error line is the one to grep for proactively:
grep -i "MaxRequestWorkers\|MaxClients" /etc/apache2/logs/error_log
If it shows up even once an hour, raise ServerLimit (and re-derive MaxRequestWorkers). Do not just raise MaxRequestWorkers without raising ServerLimit — the formula above caps it, and the new ceiling is silently ignored.
Common mistakes worth checking
- Running prefork "because PHP needs it" — only true for mod_php DSO, removed from EA4 long ago. With PHP-FPM or LSAPI, every MPM works.
- Setting
MaxRequestWorkerswithout raisingServerLimit— the value is clamped toServerLimit × ThreadsPerChildand your high number does nothing. - Disabling KeepAlive on event MPM — undoes one of the main reasons to be on event.
- Tuning MPM and not tuning PHP-FPM pools — Apache can accept 2000 concurrent requests; if PHP-FPM tops out at 50, all you've moved is the bottleneck.
- Editing
/etc/apache2/conf.d/mpm_event.confdirectly — the next EasyApache provision overwrites the file. Always use the Include Editor.
If your bottleneck is actually concurrency at the request level rather than at the Apache layer, the answer is often LiteSpeed Web Server, which collapses the MPM-and-handler distinction and serves more concurrent requests per GB of RAM than Apache plus PHP-FPM. The LSAPI handler benchmarks make that case in detail.
Verify before walking away
httpd -t # config syntax check
httpd -V 2>/dev/null | grep MPM # confirm the right MPM is active
/scripts/restartsrv_httpd
curl -s http://127.0.0.1/whm-server-status?auto | grep -E "BusyWorkers|IdleWorkers"
ab -n 1000 -c 50 https://example.com/ # quick smoke test under concurrency
ab is crude but enough to catch a misconfigured tuning — if Failed requests is non-zero on a static page or Time per request is in the seconds, something is wrong (usually MaxRequestWorkers too low, or PHP-FPM pool exhaustion downstream).
Which Apache MPM is best for cPanel?+
Can I run mod_php with the event MPM?+
What does 'Server reached MaxRequestWorkers' mean?+
How do I change the MPM without breaking the running server?+
Where do I put MPM tuning so EasyApache doesn't overwrite it?+
Next steps
- Pair the MPM tuning with right-sized PHP-FPM pools — Apache concurrency without FPM concurrency just moves the bottleneck.
- If you're still picking a handler, read PHP handlers in cPanel compared before locking in the MPM.
- Considering a move off Apache entirely? See install LiteSpeed on cPanel and the LiteSpeed license tiers.
- Sizing a new server? Tier mapping is in the cPanel license tier breakdown.