How to Tune Apache’s Worker and Thread Settings

How to tune Apache's worker and thread settings

I used to think Apache “just worked” out of the box, until I put a moderately busy WordPress site on a small VPS and watched it fall over during a traffic spike. That was my introduction to the world of Multi-Processing Modules (MPMs), workers, and threads. Tuning these settings properly turned out to be one of the highest-leverage things I could do for server stability and performance.

Understanding Apache’s MPMs

Apache handles concurrent requests using a Multi-Processing Module, and the MPM you choose fundamentally changes how “worker” and “thread” settings behave. There are three main MPMs:

  • Prefork MPM: Spawns a separate process for each connection. No threading involved. Safer for non-thread-safe modules like older versions of mod_php, but heavier on memory.
  • Worker MPM: Uses multiple child processes, each managing multiple threads. More memory-efficient than prefork.
  • Event MPM: An evolution of Worker MPM, optimized for handling Keep-Alive connections more efficiently by dedicating a thread to managing them separately.

You can check which MPM is currently active with:

apache2ctl -M | grep mpm

or

httpd -V | grep -i mpm

If you’re running a modern stack (PHP-FPM, or any language handled via mod_proxy_fcgi), Event MPM is almost always the right choice today. Prefork is really only necessary if you’re stuck with legacy modules that aren’t thread-safe.

Why Tuning Matters

Out of the box, Apache’s defaults are conservative and designed to work reasonably well on a wide range of hardware — which also means they’re rarely optimal for your specific server. If your settings are too low, Apache will queue or reject connections during traffic spikes. If they’re too high, Apache can spawn more processes/threads than your RAM can support, leading to swapping, which is often worse than rejecting a few connections outright.

I learned this the hard way — I once set MaxRequestWorkers far too high on a 2GB RAM server, and a moderate traffic bump caused the entire box to grind to a halt as it tried to swap memory to disk.

Prerequisites

  • Root or sudo access to your server.
  • Knowledge of which MPM you’re running (see above).
  • An idea of your average process memory footprint (I’ll show you how to calculate this).
  • Apache installed and currently functioning normally.

Step 1: Identify Your MPM Configuration File

On Debian/Ubuntu:

/etc/apache2/mods-available/mpm_event.conf
/etc/apache2/mods-available/mpm_prefork.conf
/etc/apache2/mods-available/mpm_worker.conf

On CentOS/RHEL, MPM settings usually live directly in /etc/httpd/conf.modules.d/00-mpm.conf or within httpd.conf itself.

Step 2: Understand the Key Directives

Here’s the Event MPM block as an example, since it’s the most commonly used today:

<IfModule mpm_event_module>
    StartServers             3
    MinSpareThreads         75
    MaxSpareThreads        250
    ThreadLimit             64
    ThreadsPerChild         25
    MaxRequestWorkers      400
    MaxConnectionsPerChild   0
</IfModule>

Let me explain what each of these actually does:

  • StartServers: How many child processes Apache spawns immediately at startup.
  • MinSpareThreads / MaxSpareThreads: The range of idle threads Apache tries to maintain, scaling child processes up or down to hit this target.
  • ThreadsPerChild: How many threads each child process manages.
  • ThreadLimit: A hard ceiling on ThreadsPerChild that can only be changed by restarting Apache (not just reloading).
  • MaxRequestWorkers (formerly MaxClients): The absolute maximum number of simultaneous connections Apache will serve. Once this cap is hit, new connections queue up.
  • MaxConnectionsPerChild (formerly MaxRequestsPerChild): How many connections a child process handles before it’s recycled. Setting this to 0 means never recycle, which can be risky if you have memory leaks in your stack.

For Prefork MPM, it looks a bit different since there’s no threading:

<IfModule mpm_prefork_module>
    StartServers             5
    MinSpareServers          5
    MaxSpareServers         10
    MaxRequestWorkers      150
    MaxConnectionsPerChild   0
</IfModule>

Step 3: Calculate Your Ideal MaxRequestWorkers

This is the single most important number to get right. The formula I use is:

MaxRequestWorkers = (Total RAM available for Apache) / (Average size of an Apache process)

To find your average Apache process size, run this while your server is under typical load:

ps -ylC apache2 --sort:rss | awk '{sum+=$8; count++} END {print sum/count/1024 " MB"}'

(Replace apache2 with httpd on CentOS/RHEL systems.)

Say your average process uses 40MB, and you want to dedicate 3GB of your server’s RAM to Apache (leaving room for MySQL, PHP-FPM, and the OS itself):

3000MB / 40MB ≈ 75 workers

That means setting MaxRequestWorkers to around 75 for a Prefork setup, or calculating the equivalent using ServerLimit × ThreadsPerChild for threaded MPMs.

Step 4: Apply Your Configuration

Edit the appropriate MPM file:

sudo nano /etc/apache2/mods-available/mpm_event.conf

Update the values based on your calculations. Then test your syntax:

sudo apache2ctl configtest

And restart Apache (a full restart is required for MPM changes, not just a reload):

sudo systemctl restart apache2

Step 5: Monitor and Iterate

After applying changes, watch how Apache behaves under real traffic:

apachectl status

Or, if mod_status is enabled, visit http://yourserver/server-status (restricted to localhost or your IP for security) to see live worker states — busy, idle, reading, writing, and so on.

Enable mod_status if it isn’t already:

sudo a2enmod status

Add this block to your config, ideally restricted to trusted IPs:

<Location "/server-status">
    SetHandler server-status
    Require ip 127.0.0.1
</Location>

Real-World Use Cases

  • High-traffic WordPress sites: Tuning Event MPM alongside PHP-FPM to handle traffic spikes from ad campaigns or viral posts.
  • API backends: Ensuring enough threads are available to serve concurrent client requests without artificial bottlenecks.
  • Shared hosting environments: Balancing worker limits across multiple sites so no single site can exhaust server resources.
  • Low-memory VPS instances: Deliberately capping MaxRequestWorkers low to prevent out-of-memory crashes.

Troubleshooting Common Issues

Problem: “server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting” in error logs. This means Apache hit its concurrency ceiling. Either raise the value (if you have RAM headroom) or investigate why requests are taking so long to complete in the first place — slow backend queries are a common culprit.

Problem: Server randomly becomes unresponsive under moderate load. This is almost always a sign that MaxRequestWorkers is set higher than your available RAM can support, causing swapping. Lower the value and recalculate using the memory formula above.

Problem: Changes to ThreadsPerChild don’t seem to apply. Remember that ThreadLimit caps ThreadsPerChild, and ThreadLimit requires a full restart (not reload) to take effect.

Common Mistakes to Avoid

  • Copy-pasting MPM settings from a blog post (including this one!) without recalculating for your own server’s RAM and workload.
  • Confusing reload with restart — MPM changes need a full restart.
  • Setting MaxConnectionsPerChild to 0 on a server running modules with known memory leaks, which lets memory usage creep upward indefinitely.
  • Ignoring mod_status and tuning blind, purely by guesswork.
  • Forgetting that switching MPMs (say from Prefork to Event) may also require adjusting your PHP handler, since mod_php isn’t compatible with threaded MPMs.

Security Best Practices

  • Restrict access to /server-status and /server-info to trusted IPs only — exposing these publicly leaks internal server details.
  • Keep MaxConnectionsPerChild at a reasonable non-zero value (e.g., 10,000) in production to guard against slow memory leaks from third-party modules.
  • Pair worker tuning with request timeouts (Timeout directive) to prevent slow or malicious clients from holding threads open indefinitely.

Performance Optimization Tips

  • Combine MPM tuning with KeepAliveTimeout adjustments (covered in my dedicated Keep-Alive post) — a too-long timeout ties up threads unnecessarily.
  • If you’re running PHP, pair Event MPM with PHP-FPM rather than mod_php, since PHP-FPM manages its own worker pool independently and plays much nicer with threading.
  • Use caching (page-level or object-level) to reduce the actual work each request requires, which indirectly reduces how many concurrent workers you need.
  • Regularly re-run your average process size calculation after major software updates, since memory footprints can shift.

Frequently Asked Questions

Which MPM should I use in 2026? Event MPM is the modern default and the right choice for the vast majority of setups, especially when paired with PHP-FPM or other FastCGI-based backends.

Do I need to restart or just reload Apache after changing MPM settings? A full restart. Reloading only re-reads certain configuration aspects; MPM-level settings require the master process to restart entirely.

How do I know if my current settings are too low? Check your error logs for “MaxRequestWorkers” warnings, and check /server-status to see if workers are consistently maxed out during peak hours.

Can I change MPM settings without downtime? A graceful restart (apachectl graceful) minimizes disruption by finishing in-flight requests before restarting, but there’s technically a brief moment where the master process restarts.

Summary and Key Takeaways

Tuning Apache’s worker and thread settings isn’t about following a universal formula — it’s about understanding your server’s actual memory budget and matching your MPM configuration to it.

Key points to remember:

  1. Identify your active MPM (Event is recommended for most modern stacks).
  2. Calculate your average Apache process memory footprint under real load.
  3. Set MaxRequestWorkers based on available RAM divided by process size, not arbitrary defaults.
  4. Always test configuration syntax before restarting, and remember MPM changes require a full restart.
  5. Monitor continuously with mod_status and adjust as your traffic patterns evolve.

Once I started treating this as an ongoing tuning exercise rather than a one-time setup task, my servers became dramatically more stable during traffic spikes.

References

Total
1
Shares

Leave a Reply

Previous Post
How to reduce Apache's memory usage

How to Reduce Apache’s Memory Usage

Next Post
How to create name-based virtual hosts in Apache

How to Create Name-Based Virtual Hosts in Apache

Related Posts