In our last tutorial, we showed you how to install Apache’s mod_fcgid and provided Linux scripts to assist in transitioning from mod_php. In this next section, we’ll be discussing how to configure a baseline setting for PHP optimization.
The default settings for mod_fcgid are often overwhelming to many servers. Due to this, it is not recommended to run mod_fcgid purely on the default settings. To appropriately optimize, the server will require a baseline FCGI configuration. Luckily, after years of working with FCGI, we’ve managed to figure out a baseline configuration template that works well in most situations. This will be the starting configuration, but it is not intended as a “set and forget” configuration as it’ll require some adjustments to perform optimally.
FCGI Baseline Configuration Template
First, let’s look at the template, which has comments (indicated with ##) provided for the understanding of each directive.
## BEGIN - FCGI Baseline Configuration Template
## Set buffers to 1G to ensure buffered sites are loaded fully.
## Increase if page output may be larger than 1G.
## Match to FcgidOutputBufferSize (ensures large volume input works)
## Disable process termination based on number of requests
## (rely on lifetime for termination not request count)
## Protects against the FCGI random 500 ISE caveat.
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0
FcgidInitialEnv PHP_FCGI_CHILDREN 0
## Total allowed active PHP Processes server-wide
## Calculated by: SystemAvailableMemory/PHP memory_limit
## Total allowed active PHP Processes per user
## Dedicated Servers should Match FcgidMaxProcesses
## Shared Servers use a fraction of FcgidMaxProcesses
## Should Match PHP: cgi.fix_pathinfo (which defaults to 1)
## How long to wait for I/O processes to complete
## Match Apache: Timeout
## How long to wait for an active PHP process to finish before forced recycle.
## Should Match PHP: max_execution_time
## Kill off idle PHP processes
## Max lifespan of idle process is: FcgidIdleTimeout + FcgidIdleScanInterval
## How long an active PHP processes lives before forced recycle.
## Lower this if PHP memory leaks become a problem
## Prevents idle PHP processes from lingering
## FINISH - FCGI Baseline Configuration Template
FCGI Directive Breakdown
In this sectional we’ll breakdown the FCGI Baseline Configuration, step by step, to explain the logic behind these settings. To address the needs of each server we’ll explain how to attune them to your specific environment. Click on the directive’s box to see the official documentation for each directive.
The output buffer should be large enough to allow even the largest of pages to be fully buffered before FCGI flushes the buffer. This can be set even higher than 1G, but that should not be necessary unless the application/website produces pages with more than 1G of data in the output. Setting the buffer too low can cause only partial page rendering. At minimum, this directive should be as large as the PHP memory_limit directive.
The request length needs to be at least as large as the post_max_size directive being used in PHP. Otherwise, only partial data is passed through FCGI to PHP, resulting in unexpected input. Matching this value to the FcgidOutputBufferSize directive ensures both input and output are large enough for most applications.
To obtain optimal synergy between Apache & PHP processes, we disable the termination of PHP processes based on the number of requests processed. The configuration will rely on PHP lifetime to determine when to recycle a PHP process instead of an arbitrary request limit.
As part of disabling premature termination of PHP processes, we need to set both PHP_FCGI_MAX_REQUESTS and PHP_FCGI_CHILDREN environment variables to zero. This is done using the FcgidInitialEnv directive to apply these settings in the environment Apache passes along to FCGI.
This setting is a ceiling limit over the top of all users. The default setting seldom applies to a real setup, unless the server in question is running very beefy hardware with some reasonable limits in place on PHP memory usage. Allowing the execution of 1,000 PHP processes is difficult for even the largest of multi-core servers. A setting of 100 is a generic ceiling that works well in most situations. However, it is possible to calculate the theoretical upper limit of PHP processes by taking the system’s available memory and dividing it by the PHP memory_limit setting (rounding down).
This setting is the per-user limitation setting. When running on a Dedicated server, where only a few domains exist, it’s generally okay to match this directive to the FcgidMaxProcesses setting. This way all sites on the server have access to all authorized PHP processing slots. However, on a shared hosting environment, in general, one user should not be allowed to monopolize all available PHP processing slots. In a shared environment, the FcgidMaxProcesses directive should be a reasonable fraction of the spawn processes for a single user.
In older versions of PHP, the default setting of 0 is no longer supported in the cgi.fix_pathinfo directive. All modern versions of PHP now default this setting to 1. This means that the default value of the FcgidFixPathinfo directive no longer matches that of the default PHP setting. To remedy the problem, we need to set cgi.fix_pathinfo to 1 as well. However, if site/application software requires the cgi.fix_pathinfo setting of 0, then it will be necessary to match FcgidFixPathinfo to it as well.
This directive is the equivalent of and should match the Apache Timeout directive. It’s a good practice to keep both settings as low as possible. However, some more demanding applications require extra time to complete their execution, and if this is lower than your Apache Timeout directive, the execution will not be able to finish.
The busy timeout equals the length of time FCGI will wait on PHP execution. This directly relates to the PHP max_execution_time settings. If FcgidBusyTimeout is lower than your max_execution_time, longer running scripts will be prematurely terminated after the allocated time.
Determining the idle timeout differs from dedicated to shared setups. In a dedicated environment, allowing PHP processes to linger during traffic surges can reduce the server’s load. It accomplishes this by having PHP processes available without the overhead of spinning up new ones. However, on a shared setup it becomes a problem because every PHP processing slot needs to remain open for all users. Thus, leaving processes to linger as they are bound to the owner that created them. In these cases, a lower idle timeout is desired.
It becomes necessary to reduce FcgididleScanInterval when the idle timeout or lifetime timeout are smaller than the default FcgididleScanInterval. Since the checks run on this interval setting, the max FcgidIdleTimeout and FcgidProcessLifeTime durations are their configured timeout settings plus the scan interval. So with an idle timeout of 60 seconds and an idle scan interval of 60 seconds, the longest an idle process could live is 120 seconds.
The longer a process runs without being terminated, the more susceptible it is to memory leakage. For this reason, a suitable amount of time should be allotted before a process is terminated. A setting of 1 hour (3600) is an excellent general setting to keep memory leaks under control.
There is a known caution that when this directive is positive, it will supersede the related FcgidIdleScanInterval timeout directives. This allows processes to live indefinitely, undermining the reliance on FcgidProcessLifeTime and FcgidIdleTimeout for memory leak protection. Thus, FcgidMinProcessesPerClass should always remain 0 on any shared server.
The information outlined in this article should not only help you to install mod_fcgid onto your cPanel server but also give you a basic understanding of module configuration. Each server may face some unique issues that are not addressed in this article. However, these are usually resolved by observing the fcgid related error and warning messages printed in the Apache error log file: /etc/apache2/logs/error_log. Let us not forget that these settings are examples, and you should adjust them as your needs dictate. Optimization is an ongoing process, what works for you now, may not work for you in six months. Remember to check your system’s average processes and consider expanding or upgrading your hardware when hitting its limits.