All product names, logos, and brands used in this post are property of their respective owners.
Over the last several years, I’ve worked a lot with cPanel (phenomenal product if you are in the market for a web hosting control panel - easy for sysadmins and developers). I’ve run a variety of web workloads on the platform and it never ceases to amaze me (plus, you cannot beat the EDU/non-profit licensing).
At one point, I was tasked with tuning apache on a cPanel server with a slightly different workload (1 very heavily trafficked application) and was provided with a high-performance virtual machine (example setup: 4 CPUs, 12 GB memory) to accommodate. The application was to use SSL (HTTPS), so I opted for mpm_worker (mpm_event is nice, but performs similarly to worker when SSL is in use).
Much literature exists regarding tuning mpm_worker for small (low memory, low end) virtual private servers (VPS), but I was not able to find much information about tuning apache for a larger (high end, high memory) server. For the remainder of this post, I will assume that you have already configured httpd to use the worker mpm (with EasyApache) on your cPanel server. Also note, this process is generalized and will apply best to well-balanced VPS (CPU count scaled to match the amount of memory - say 1 CPU core for every 2-3 GB of RAM).
- cPanel/WHM does not allow adjustment of ThreadsPerChild via the web interface - it defaults to 25 (you can change this in httpd.conf and distill the configuration BUT I advise against that)
- Given the above, MaxClients must be divisible by 25 (ThreadsPerChild)
- Most tuning is achieved by changing MaxClients (MaxRequestWorkers) and ServerLimit
Prework (initial calculations)
To tune httpd and mpm_worker, a little background information is required; the information gathering process will vary a little from server to server.
First, you must calculate the average amount of memory (RAM) that each apache process uses. When performing this calculation, the server should be under “normal” load. If it is idle, the calculation will be inaccurate.
I use top -u and specify the username that httpd runs as (on cPanel, this is “nobody” but the user varies on other distributions depending on your configuration). We are most interested in the RES column - more specifically, the average RES for all apache processes.
As an example:
# top -u nobody ------------------------------------------------------------------------------- Tasks: 255 total, 1 running, 254 sleeping, 0 stopped, 0 zombie Cpu(s): 4.0%us, 0.3%sy, 0.0%ni, 95.6%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 12303646k total, 6088432k used, 9259430k free, 520936k buffers Swap: 2097148k total, 0k used, 2097148k free, 3804344k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 27550 nobody 20 0 2126m 113m 74m S 7.0 0.5 0:56.28 httpd 27366 nobody 20 0 2125m 115m 74m S 5.6 0.5 1:00.96 httpd 29001 nobody 20 0 2126m 45m 6788 S 5.3 0.2 0:25.52 httpd 28127 nobody 20 0 2126m 48m 6228 S 5.0 0.2 0:48.01 httpd 28048 nobody 20 0 2126m 114m 74m S 4.7 0.5 0:48.76 httpd 28827 nobody 20 0 2125m 44m 6072 S 2.7 0.2 0:30.49 httpd 30106 nobody 20 0 2112m 31m 3668 S 2.7 0.1 0:03.35 httpd 2922 nobody 20 0 108m 14m 796 S 0.0 0.1 1:47.00 httpd -------------------------------------------------------------------------------
Then, calculate the average memory (RES) consumed per process by adding each RES value and dividing by the number of processes:
113 + 115 + 45 + 48 + 114 + 44 + 31 + 14 = 524 / 8 = 65.5
This means each apache/httpd process consumes ~66 MB of memory on average. cPanel uses the default value of 25 for ThreadsPerChild, so we can conclude that for every 25 clients, ~66 MB of memory is required.
Calculating your server’s value for MaxClients (aka MaxRequestWorkers)
Next, you must decide how much memory you can allocate for httpd and impose that limit with Max Clients. To get a baseline, I recommend stopping apache and assessing the amount of available memory on the system.
On the example server, let’s say that 9259430k (~9.5 GB) of memory is available when httpd is not running. Say you are comfortable allocating 75% of that (~7 GB) to Apache. Use your judgment here - depending on your system, you may need to adjust your allocation (to account for things like cPanel/WHM, PHP, MySQL, and other processes). Several factors can affect memory usage as load increases so it is not safe to assume that what is available when apache is stopped will scale linearly. Under no circumstances should you allow apache to swap. If that occurs, your estimate/allocation is too high.
Above, we decided that we can spare 7 GB of memory for httpd. We also know that our average apache memory usage per process is 66 MB.
7 GB * 1024 = 7168 MB / 66 MB (from above) = 108.6 processes Then round down = 100 processes
This means ~100 apache processes can run in the 7 GB we allocated. We know that each apache process can handle 25 clients (threads), so multiply by ThreadsPerChild to obtain Max Clients:
100 * 25 = 2500
So for this server, a Max Clients setting of 2500 is reasonable (and since Server Limit simply caps the upper limit, set it to 2500 or greater). Since we rounded down to the nearest 100, MaxClients will be divisible by 25 (and it needs to be). If your calculation is different, round down appropriately so that your Max Clients setting is divisible by 25.
Updating cPanel’s httpd configuration
Implementing these settings in WHM is easy:
WHM -> Service Configuration -> Apache Configuration -> Global Configuration
Then, update the 2 settings based on your calculation:
Finally, restart apache, and you should be good to go.
It could not hurt to perform a load test against your server as a sanity check (JMeter is free and well documented). This will ensure your estimate/calculation from above is within spec. Again, keep httpd from swapping at all costs. A load test will also help identify other potential bottlenecks on your server (CPU, disk performance, network bandwidth, etc.). At this point, you may also consider putting your extra threads to work (or wait) by enabling KeepAlive and increasing your Keep Alive Timeout (if you have not already).