MPMs and Workers

prefork, worker, event and MaxRequestWorkers

Exactly one MPM is loaded (Modules). prefork gives each connection a process and exists for modules that are not thread-safe, today mod_php. worker runs threads in processes, but an idle keep-alive connection still holds a thread. event adds a listener thread that parks idle connections, so workers serve only active requests; use it everywhere else. MaxRequestWorkers (150 in Ubuntu 225 's prefork and event files) caps simultaneous requests: prefork counts processes, sized by dividing free RAM by one child's size, while threaded MPMs cap it at ServerLimit (default 16) times ThreadsPerChild (25), so going past 400 means raising ServerLimit too. At the cap the log says AH00161: server reached MaxRequestWorkers setting. Provoke it with 300 clients that send one request and go quiet:

300 idle keep-alive clients, then one new visitorApache config
for i in $(seq 300); do
  ( exec 3<>/dev/tcp/127.0.0.1/80
    printf 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n' >&3; sleep 12 ) &
done
sleep 8
curl -s -o /dev/null -w "new visitor: %{http_code} in %{time_total}s\n" http://localhost/
Output
new visitor: 301 in 3.939994s
new visitor: 301 in 0.000679s

The first line is prefork, the second event (301 is the port-80 redirect). Prefork's 132 children each sat on a silent connection until KeepAliveTimeout (5 seconds) freed it; event's listener parked all 300 sockets. h2load 5,056 over TLS, third of three runs each:

prefork + mod_php versus event + PHP-FPM, Apache 2.4.66 129 and PHP 8.5.4
Measurement prefork + mod_php event + PHP-FPM
Static 2.7 KB file, 50 clients 34,528 req/s 45,971 req/s
Hello-world PHP, 50 clients 37,857 req/s 12,663 req/s
Processes with 300 idle clients (memory) 132 (59 MiB PSS) 3 (11 MiB PSS)

PSS splits shared pages fairly; summed RSS claimed 2,439 MiB. PHP figures moved between sessions (an earlier one gave 29,901 against 18,220), since the FPM pool is shared. Event runs lost up to 13% of connections: with every worker busy, event closes keep-alive connections early so clients reconnect elsewhere, which h2load counts as failures and browsers simply retry.