1. Determine how much RAM is available to Apache
  2. Determine how much RAM each Apache process uses
  3. Set MaxClients to: (RAM available to Apache) / (RAM per Apache process)

How To Change the Value

The MaxClients value is located in the main Apache configuration file, typically /etc/apache2/apache2.conf or /etc/httpd/httpd.conf.

The relevant section looks like this:

# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves
<IfModule mpm_prefork_module>
    StartServers        5
    MinSpareServers     5
    MaxSpareServers     10
    MaxClients          150
    MaxRequestsPerChild 0
</IfModule>

After changing the value, save the file and restart Apache with apache2ctl graceful-stop && apache2ctl start.

Why It’s Important

One of the most common causes of a public Web server crash is that it’s consumed all available physical memory (RAM). When this happens, the operating system attempts to free RAM by moving some of it to disk. In Linux this is called the “swap” space; in Windows “page file.” This process defeats the purpose of RAM (which is to provide very fast access to data) and it requires resources (CPU power, disk I/O) to perform the “swapping” of data between the disk and RAM. The swap space can also become completely consumed, but the real problem is that in a very fast-paced environment like a Web server, the system can quickly become overwhelmed and effectively crash just by trying to manage its own memory. This is called thrashing.

The name of the game is to avoid thrashing and the only way to do so is to keep Apache from using more physical RAM than is available to it.

How To Determine How Much RAM is Available to Apache

The amount of RAM available to Apache is the total amount of RAM on the system minus the amount that will be used by all other processes. Unfortunately, it’s difficult to determine how much RAM is being used on the system.* It’s even harder to determine how much maximum possible RAM will be used by other processes when the server is under heavy load.

And it gets yet more difficult if you’re running MySQL on the same machine. Typically, each request handled by Apache will open at least one connection to MySQL, so as the Apache requests increase, so will MySQL’s RAM usage. In short, MySQL’s memory usage depends on how it’s being used by your application. You can use the mysqltuner script to get an absolute maximum value that MySQL will use, but once you have Apache tuned properly it will rarely hit this amount. Your best bet is to move MySQL to a separate server. Other than that, use the max value initially, watch the server under load, and make adjustments.

For all the other processes, use a value of 256 MiB. If you’re using a lot of other services, such as on a virtual hosting machine, you may want to use a higher value.

So, initially, for a typical system, reserve 256 MiB + max MySQL RAM usage.

How to Determine How Much RAM a Single Apache Process Uses

Again, due to shared memory (especially if you’re using APC), buffering, caching, etc. it’s very difficult to determine this. In our experience, the best tool for determining memory usage per process is ps_mem.py.

ps_mem.py will produce output like this:

$ sudo ./ps_mem.py
 Private  +   Shared  =  RAM used       Program 

  4.0 KiB +  22.5 KiB =  26.5 KiB       upstart-udev-bridge
  4.0 KiB +  61.0 KiB =  65.0 KiB       login
  0.0 KiB +  70.0 KiB =  70.0 KiB       udevd (3)
[...]
123.0 MiB + 221.0 KiB = 123.2 MiB       mysqld
  3.3 GiB +  27.9 MiB =   3.4 GiB       apache2 (123)
---------------------------------
                          3.6 GiB

Here you can see that there are 123 apache2 processes, consuming a total of 3.4 GiB, so each Apache process is using roughly 28 MiB of RAM. This system has about 8 GiB of RAM, and although mysqltuner.pl reports that MySQL’s maximum memory use is about 1 GiB, we can see that it’s using much less. To be safe though, I’ll reserve 1.5 GiB for all other processes and round up Apache’s average usage to 32 MiB:

(6.5 x 1024) / 32 = 208

To be extra safe, round this down to 200. It’s better to under-estimate.

Watch The Server

Keep an eye on the number of Apache processes, and the total RAM used. Here’s a command that wraps this into a single output that updates every second:

watch -n 1 "echo -n 'Apache Processes: ' && ps -C apache2 --no-headers | wc -l && free -m"

It produces output like this:

Every 1.0s: echo -n 'Apache Processes: ' && ps -C apache2 --no-headers | wc -l && free -m

Apache Processes: 27
            total       used        free        shared      buffers     cached
Mem:        8204        7445        758         0           385         4657
-/+ buffers/cache:      2402        5801
Swap:       16383       189         16194

Use the -/+ buffers/cache row. The values are in MiB. You can see that at this point in time, about 2.3 GiB is in use and 5.7 GiB is free. If your Apache processes get close to the MaxClients setting and there’s plenty of extra RAM available, adjust the value up in small steps. If for some reason the value in the free column gets small (say less than 500), reduce Apache’s MaxClients value and restart.

Leave a Reply

Your email address will not be published. Required fields are marked *