HOWTO: mod_fcgid in CB 2.0, switchable to suphp

Jan_E

Verified User
Joined
Apr 29, 2011
Messages
132
Location
Amsterdam, NL, EU
HOWTO: mod_fcgid in CB 2.0, switchable to suphp and mod_php

This post will show you how to install mod_fcgid, using custombuild 2.0, and create a setup where the admin can choose for every individual DA-user between mod_php, suphp and mod_fcgid. I am using this on my Centos server with Apache 2.4.3, PHP 5.3 as mod_php and PHP 5.4 as suphp/mod_fcgid. The initial options.conf contains
Code:
php1_release=5.3
php2_release=5.4
php1_mode=mod_php
php2_mode=suphp
As first step on a running server we have to build mod_fcgid. Perform these steps as root:
Code:
cd /usr/local/src
wget http://mirrors.issp.co.th/apache/httpd/mod_fcgid/mod_fcgid-2.3.7.tar.bz2
tar xjf mod_fcgid-2.3.7.tar.bz2
cd mod_fcgid-2.3.7
APXS=/usr/sbin/apxs ./configure.apxs && make && make install
/usr/local/directadmin/custombuild/build rewrite_confs
service httpd status
mkdir -p /var/lib/httpd/sock
chmod 755 -R /var/lib/httpd
Remarks:
1. The build rewrite_confs will remove the mod_fcgid from the Apache config, but we will need this later on.
2. The operations on /var/lib/httpd prepare the system for the fcgid socket.

Next step: create a handler for mod_fcgid. We will use /etc/httpd/conf/extra/httpd-includes.conf for that. Please mind the fact that this is loaded before httpd-suphp.conf in Custombuild 2.0 and is empty on a clean install. Add this to httpd-includes.conf:
Code:
LoadModule fcgid_module /usr/lib/apache/mod_fcgid.so

SharememPath /var/run/fcgid_shm
SocketPath /var/lib/httpd/fcgid/sock

<IfModule mod_fcgid.c>
  FcgidIdleTimeout 3600
  FcgidProcessLifeTime 7200
  FcgidMaxProcesses 128
  FcgidMaxProcessesPerClass 4
  FcgidConnectTimeout 60
  FcgidIOTimeout 90
# moved to fcgid.sh
# FcgidInitialEnv PHP_FCGI_CHILDREN 0
# MaxRequestsPerProcess 0
  AddHandler fcgid-script .fcgi
</IfModule>    
#.
Next step: create a fcgid.sh to start php as fcgid. See the official Apache docs:
http://httpd.apache.org/mod_fcgid/

There are a lot of tutorials on how to create this fcgid.sh. One of the most structured is:
http://woshka.com/blog/apache/compi...i-in-directadmin-installed-control-panel.html

The trick is to create an fcgid.sh for every user that can be loaded from within the Apache config. Most tutorials use this directory:
mkdir -p /fcgi/${username}/public_html
The directory needs to have the user and group of each DA-user:
chown -R ${username}:${username} /fcgi/${username}

I am using this fcgid.sh:
Code:
#!/bin/sh

# http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
# Set desired PHP_FCGI_* environment variables.
# Example:
# PHP FastCGI processes exit after 500 requests by default.
# JE: Do not limit it to facilitate XCache (or set it really high, like 10000)
PHP_FCGI_MAX_REQUESTS=0
export PHP_FCGI_MAX_REQUESTS
# See http://www.webhostingtalk.com/archive/index.php/t-1165678.html
PHP_FCGI_CHILDREN=0
export PHP_FCGI_CHILDREN
exec /usr/local/php54/bin/php-cgi54
Note that up until now you did not make any real changes to the system. But with a working fcgid.sh you can test your setup now, without interrupting your users (if you forget the restart of Apache). Add this to the Custom HTTPD Configuration of a domain:
Code:
<IfModule mod_fcgid.c>
	FcgidWrapper /fcgi/|USER|/public_html/fcgid.sh
	<FilesMatch "\.fcgi$">
		Options +ExecCGI
		SetHandler fcgid-script
	</FilesMatch>
</IfModule>
Wait for the (hopefully graceful) restart of Apache, while creating three test files with phpinfo() inside:
phpinfo.php
phpinfo.php54
phpinfo.fcgi

With my setup these test files will use the following PHP's:
phpinfo.php - php1_release = PHP 5.3 as mod_php with /usr/local/lib/php.ini
phpinfo.php54 - php2_release = PHP 5.4 as suphp
phpinfo.fcgi - php2_release = PHP 5.4 as mod_fcgid

The difference between suphp and mod_fcgid is hard to tell from the phpinfo, but the clearest indication is the section Environment below Additional Modules, containing variables like:
Code:
PHP_FCGI_CHILDREN 	0 
PATH 	/usr/local/bin:/usr/bin:/bin 
PWD 	/fcgi/username/public_html 
SHLVL 	0 
PHP_FCGI_MAX_REQUESTS 	0
If these three testfiles run as expected, you can now switch a domain freely from mod_php (5.3) to suphp (5.4) or mod_fcgid (5.4)

MOD_PHP
This is the default mode, without any addition to the Custom HTTPD Configuration.
phpinfo.php will report PHP 5.3 as mod_php (Apache 2.0 Handler)

SUPHP
You can switch a domain to suPHP by adding these lines to the Custom HTTPD Configuration:
Code:
<IfModule mod_suphp.c>
	<FilesMatch "\.(inc|php|php3|php4|php44|php5|php52|php53|phtml|phps)$">
		SetHandler x-httpd-php54
	</FilesMatch>
</IfModule>
#.
The phpinfo.php will report PHP 5.4 as suPHP (check the Environment below Additional modules)

MOD_FCGID
You can switch a domain to mod_fcgid by adding these lines to the Custom HTTPD Configuration
Code:
<IfModule mod_fcgid.c>
	FcgidWrapper /fcgi/|USER|/public_html/fcgid.sh
	<FilesMatch "\.(inc|php|php3|php4|php44|php5|php52|php53|phtml|phps)$">
		Options +ExecCGI
		SetHandler fcgid-script
	</FilesMatch>
</IfModule>
#.
Note the |USER| in the FcgidWrapper. Directadmin will replace that by the actual username.
phpinfo.php will report PHP 5.4 as mod_fcgid (check the Environment below Additional modules).

OK, there you have it. A versatile way to switch userdomains between mod_php, suPHP and mod_fcgid. The setup will (as far as I can see) survive upgrades and rewrite_confs. I did not test it, but there is a chance this will also work with php1_release as php-fpm.

I will discuss custom php.ini's per user and automating the process in separate posts.
 
Last edited:
A custom php.ini per DA-user (under mod_fcgid)

Easy one: create a php.ini in /fcgi/username/public_html ...

As explained in the first post, under php as mod_fcgid is started by fcgid.sh in /fcgi/username/public_html. This makes it easy to use a custom php.ini for each user, because php scans /fcgi/username/public_html first for php.ini and uses that one if it is found.

If a php.ini is not present in the userdir, php uses the default one in its lib directory. That is /usr/local/php54/lib/php.ini in my example.

It is good practice to create a custom php.ini in /fcgi/username/public_html out of security reasons. All tutorials I have seen thus far uncomment the open_basedir setting in php.ini and restrict it as follows:

open_basedir = /home/username:/tmp:/var/tmp

Of course, the creation of such a php.ini with a user-specific open_basedir can be automated. That will be the topic of the next post.
 
Last edited:
Automating the setup

Of course you want to make the setup for choosing between the three modes a one time effort, which will automatically be applied while adding new users. This is possible as well.

Directadmin will invoke custom scripts before (pre) or after (post) the creation of a new user and the associated default domain. It will look for domain_create_post.sh in /usr/local/directadmin/scripts/custom. If this script exists and is executable, DA will run it after rhe creation of a user&domain.

First, create a php.ini in /usr/local/directadmin/scripts/custom which will be the template for all user php.ini's. This may be a copy of the php.ini of the suPHP, but you can change things at will. For instance, some php-extensions like XCache will not work under suPHP, but will have benefits under mod_fcgid. You can add those extensions to the template.

Moreover, uncomment the open_basedir line in the template php.ini and change it into:
Code:
open_basedir = PHPCFG_BASEDIR:/tmp:/var/tmp

Create the domain_create_post.sh with the following content:
Code:
#!/bin/sh
mkdir -p /fcgi/${username}/public_html
cp /usr/local/directadmin/scripts/custom/fcgid.sh /fcgi/${username}/public_html/fcgid.sh && chmod 0700 /fcgi/${username}/public_html/fcgid.sh
cp /usr/local/directadmin/scripts/custom/php.ini /fcgi/${username}/public_html/php.ini
perl -pi -w -e "s/PHPCFG_BASEDIR/\/home\/${username}/g;" /fcgi/${username}/public_html/php.ini
chown -R ${username}:${username} /fcgi/${username}
#
This script does the following:
1. create /fcgi/username and /fcgi/username/public_html
2. copy the fcgid.sh to /fcgi/username/public_html and make it read/write/executable for the user
3. copy the template php.ini to /fcgi/username/public_html
4. use perl to change PHPCFG_BASEDIR into /home/username in the user php.ini (the open_basedir)
5. make the fcgid.sh and php.ini owned by the user and the user-group
This will automate the process for new users.

For existing users, you will have to mimick the process for new users. Example for the admin:
Code:
cd /usr/local/directadmin/scripts/custom/
username=admin ./domain_create_post.sh
echo setup for admin completed
For lots of existing users you will take automation a step further and run something like the above automatically. Find that out your self...

Note: this is only the initial setup for new and existing users. If you do not add anything to the custom httpd configuration, php will still be run as php1_release! If you want every user to use mod_fcgid, you will have to create custom templates for the virtualhost. That is beyond the scope of this how-to.
 
Last edited:
Side notes

The posts above only discuss the basic setup. I wouls like to make some additional remarks before the discussion diverts to these issues.

mod_fcgid setup
There are all kinds of discussions on the best mod_fcgid setup. Most discussd are the settings of PHP_FCGI_CHILDREN and PHP_FCGI_MAX_REQUESTS.

PHP_FCGI_CHILDREN might not be set at all, be set to 0 or be set to higher values like 4. XCache recommendation is to set it to a value > 1 (See XCache 3.0.0 diagnosis tab), but Cloudlinux recommends a 0: http://www.webhostingtalk.com/archive/index.php/t-1165678.html. Somewhere I even read a story of some guys who had analysed the stuff and discover that additional children were really lazy and only eating memory.

Edit: I regoogled this story:
http://wherethebitsroam.com/blogs/jeffw/apache-php-fastcgi-and-phpfcgichildren

Edit 2: On the other hand, there is an intriguing remark on this site:
http://www.brandonturner.net/blog/2009/07/fastcgi_with_php_opcode_cache/
Only the child process is restarted, the parent process remains. Since the parent process maintains the opcode cache, the opcode cache persists.
This remark on APC sets you thinking...

PHP_FCGI_MAX_REQUESTS defaults to 500 requests. After that amount of request the hph-fcgid is killed and errors 500 can happen. Should PHP_FCGI_MAX_REQUESTS be disabled (set to 0), set to a high number like 10000? Food for discussion. And close-reading the Apache docs.

Bottom line: you might want to experiment with the exported values in fcgid.sh. You can do that safely without disrupting other users, because the fcgid.sh is user-specific and the opcode cache is not shared between users.

Using only 1 PHP for all three modes
On my windows server I am sometimes experimenting with exactly the same PHP as mod_php and as mod_fcgid. The only difference is the php.ini. PHP as mod_php first tries to load php-apache2handler.ini. This might create the possibility to load the same PHP as mod_php, as suPHP with another php.ini and as mod_fcgid with a user-specific php.ini.

Wishlist for Custombuild 2.0: switching between 4 modes
What if .... a php3_release and a php3_mode was added to Custombuild 2.0? I see possibilities to switch between 4 modes: mod_php, suPHP, php-fpm and mod_fcgid. Or is that too far fetched?

Merry Christmas to all of you!
 
Last edited:
Fastcgi is now added as a 4th option for PHP mode in CB 2.0 development version (it's not uploaded to DA severs yet). Thank you for the request.
 
Fastcgi is now added as a 4th option for PHP mode in CB 2.0 development version (it's not uploaded to DA severs yet). Thank you for the request.

This is a great news. However, I came across the code in Custombuild below :

Code:
if [ "${WEBSERVER_OPT}" = "nginx" ]; then
			if [ "${HAVE_CLI}" = "yes" ] || [ "${HAVE_SUPHP_CGI}" = "yes" ] || [ "${HAVE_FCGID}" = "yes" ]; then
				echo "nginx webserver is only compatible with php-fpm PHP mode."
				exit 1;

I'm not sure what is it for but I'm pretty sure nginx can work with mod_fcgid without any problem. One of my server that is now switched to Custombuild 2.0. It used to run mod_fcgid + nginx without any problem. Then, after PHP-FPM is integrated into PHP 5.3, I then, set it up to run Apache + mod_fcgid on port 443 (SSL) while running nginx + PHP-FPM on port 80. The same socket file location has been use for both PHP-FPM / mod_fcgid.
 
Yes, it can, however we decided to start with PHP-FPM+nginx only and will probably add php-fastcgi later if there are any requests for it and if anyone sees benefit going with php-fastcgi+nginx instead of PHP-FPM+nginx.
 
Oh.. Now I see that Custombuild 2.0 will also support Nginx. Thanks for quick reply.
 
Yes, it can, however we decided to start with PHP-FPM+nginx only and will probably add php-fastcgi later if there are any requests for it and if anyone sees benefit going with php-fastcgi+nginx instead of PHP-FPM+nginx.

i have 1 big site work with cgi script so (php-fastcgi+nginx) will be good
 
Back
Top