Tuning MaxRequestWorkers sets the upper limit on how many HTTP requests Apache can serve at the same time. A ceiling that is too low leaves requests queued behind busy workers and can trigger 503 responses under bursts, while a ceiling that is too high can push the host into swap or out-of-memory kills.
The active MPM (multi-processing module) decides how that ceiling is enforced. prefork handles one request per child process, while worker and event spread requests across threads inside multiple child processes, so the usable limit is shaped by ServerLimit, ThreadLimit, and ThreadsPerChild as well as the memory footprint of the workload behind each request.
Examples use current Debian and Ubuntu packaging with apache2ctl, the apache2 service, and /etc/apache2/mods-available/mpm_*.conf. On Red Hat-family systems, the same directives are usually managed with apachectl or httpd in /etc/httpd/conf.modules.d/00-mpm.conf. If the new target also needs higher ServerLimit or ThreadLimit values, plan a full restart because a graceful reload does not apply those hard-limit changes.
Related: How to improve Apache performance
Related: How to enable the event MPM in Apache
Steps to tune MaxRequestWorkers in Apache:
- Identify the active Apache multi-processing module.
$ apache2ctl -V 2>/dev/null | grep -E '^Server MPM:' Server MPM: event
- Read the live worker limits from the active MPM configuration link.
$ sudo grep -E '^(ThreadLimit|ThreadsPerChild|MaxRequestWorkers|MaxConnectionsPerChild)' /etc/apache2/mods-enabled/mpm_event.conf ThreadLimit 64 ThreadsPerChild 25 MaxRequestWorkers 150 MaxConnectionsPerChild 0
On Debian and Ubuntu, /etc/apache2/mods-enabled/mpm_event.conf usually points back to /etc/apache2/mods-available/mpm_event.conf for editing.
- Calculate whether the new ceiling still fits inside the current hard limits.
For event and worker, divide MaxRequestWorkers by ThreadsPerChild and round up to get the required child-process count; if that result exceeds ServerLimit, raise ServerLimit too because the effective cap is ServerLimit * ThreadsPerChild. If ServerLimit is not set explicitly, event and worker default to 16 child processes, so the package values above top out at 400 requests.
- Check the error log for signs that the current ceiling is already saturating.
$ sudo grep -n 'MaxRequestWorkers' /var/log/apache2/error.log || echo "No MaxRequestWorkers warnings found" No MaxRequestWorkers warnings found
- Measure the Apache child-process RSS during representative traffic so the new ceiling stays inside real memory limits.
$ ps -o pid,rss,cmd -C apache2 --sort=-rss | sed -n '1,6p' PID RSS CMD 3443 5748 /usr/sbin/apache2 -k start 3442 5080 /usr/sbin/apache2 -k start 3440 4848 /usr/sbin/apache2 -k startEstimate the working set from busy child processes, not from an idle service, before multiplying it across the planned worker count.
- Edit the active MPM configuration file with the new ceiling.
<IfModule mpm_event_module> StartServers 2 MinSpareThreads 25 MaxSpareThreads 75 ThreadLimit 64 ThreadsPerChild 25 MaxRequestWorkers 200 MaxConnectionsPerChild 0 </IfModule>Add or raise ServerLimit only when the new MaxRequestWorkers target no longer fits inside the current ThreadsPerChild math.
- Test the updated configuration before applying it.
$ sudo apache2ctl configtest Syntax OK
The AH00558 message about a missing global ServerName is a warning, not a syntax failure.
Related: How to test Apache configuration
- Reload Apache when only MaxRequestWorkers changed and the existing ServerLimit plus ThreadLimit still cover the new value.
$ sudo systemctl reload apache2
Current Debian and Ubuntu packages map systemctl reload apache2 to apachectl graceful.
- Restart Apache when the change also raises ServerLimit or ThreadLimit.
$ sudo systemctl restart apache2
ServerLimit and ThreadLimit changes are ignored during a graceful reload, so a full restart is required when either hard limit changes.
- Confirm that the tuned MPM file now shows the new ceiling.
$ sudo grep -E '^(ThreadLimit|ThreadsPerChild|MaxRequestWorkers|MaxConnectionsPerChild)' /etc/apache2/mods-available/mpm_event.conf ThreadLimit 64 ThreadsPerChild 25 MaxRequestWorkers 200 MaxConnectionsPerChild 0
- Read the live worker counters from server-status?auto after the change.
$ curl -sS http://127.0.0.1/server-status?auto | grep -E '^(ServerMPM|BusyWorkers|IdleWorkers):' ServerMPM: event BusyWorkers: 1 IdleWorkers: 49
Current Debian and Ubuntu packages usually enable mod_status with Require local on server-status, which is why the localhost check works here.
- Benchmark a representative URL with the intended concurrency after the change.
$ ab -n 200 -c 20 -k http://127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 1903618 $> ##### snipped ##### Concurrency Level: 20 Time taken for tests: 0.018 seconds Complete requests: 200 Failed requests: 0 Keep-Alive requests: 200 Requests per second: 11229.65 [#/sec] (mean) Time per request: 1.781 [ms] (mean) Time per request: 0.089 [ms] (mean, across all concurrent requests)
Use a representative endpoint and a safe maintenance window because overly aggressive ab settings can overload backends, caches, or application workers behind Apache.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
