Run php-fpm with separate user/uid and group on linux

By | June 18, 2013

Php-FPM

Php fpm is the new way to setup php to run with your webserver. Php-fpm is a fastcgi process manager for php that is totally separate from the webserver. The webserver communicates with fpm through a socket and passes the name of the script to execute. So fpm can run with any web server that is fastcgi compatible.

I recently moved from my old shared hosting to linode. Linode provides linux vps hosting at economic prices. However the servers are totally unmanaged are just raw linux machines that have shell access. So through the shell you have to setup everything including the web server, php and the web files.

So this time I decided to go with the combination of nginx and php-fpm. I had multiple sites to setup on this new webserver. Nginx deals with these through separate server blocks (aka vhost in apache). However there was another thing needed. Php on each site should run with its own user and not the nginx common user named www-data.

Running each site with its own uid/gid is more secure and easier to deal with. If all sites ran with same user, then php on one site could read/write the files of other users. This is a security concern. Moreover having separate users gives benefits like separate crontabs, ability to identify processes from each user etc.

Separate Pools

Php-fpm creates and manages a pool of php processes, also called workers that receive and server requests to execute php files from the web directory. Now fpm can run multiple separate pools each with its own uid/gid. This is actually very easy to setup.

On ubuntu, the directory that contains the pool configuration files is

/etc/php5/fpm/pool.d/

A file called www.conf already exists which can be copied to create more pool configuration files. Each file must end with .conf to be recognised as a pool configuration file by php fpm.

The file is very long but starts with something like this.

; Start a new pool named 'www'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('www' here)
[www]

; Per pool prefix
; It only applies on the following directives:
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or /usr) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = enlightened
group = enlightened

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on                                                                       
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /var/run/php5-fpm.sock

; Set listen(2) backlog.
; Default Value: 128 (-1 on FreeBSD and OpenBSD)
;listen.backlog = 128

So create a copy of this file and name it to anything like the site which will be using this pool. The important fields to edit are

1. Pool name. It is on the top [www]. Rename it to [mysite].

2. Next, change the user and group field and put the username and group to run it with.

user = mysite_user
group = mysite_user

3. Change the socket file name. Every pool should have its own separate socket. And the particular site should use this particular socket file to connect to fpm.

listen = /var/run/php5-fpm-mysite.sock

Now restart php-fpm

$ sudo service php5-fpm restart

Now check the processes in htop. You should see the new pool running with separate username.
Check the documentation for details about all the configuration parameters. Each pool can have different configuration depending on the needs of the site.

Configure Nginx

If you are using nginx, then put the socket descriptor in the right place and it will use it.

location ~ \.php.*$ {
	fastcgi_split_path_info ^(.+\.php)(/.+)$;

	# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

	# With php5-cgi alone:
	# fastcgi_pass 127.0.0.1:9000;
	# With php5-fpm:
	fastcgi_pass unix:/var/run/php5-fpm.sock;
	fastcgi_index index.php;
	include fastcgi_params;

	fastcgi_param PATH_INFO $uri;
}

Put the socket descriptor path in the fastcgi_pass line.

fastcgi_pass unix:/var/run/php5-fpm-mysite.sock;

Thats it. Nginx will now use this socket and hence its pool for mysite.

Configure Apache (mod_fastcgi)

Apache can use php-fpm through mod_fastcgi which can talk to external fastcgi servers and get the requests processed. When using external fastcgi server like fpm, mod_fastcgi becomes a mere proxy that relays requests and responses between apache and fpm.

A typical fpm configuration in apache with mod_fastcgi looks like this

<IfModule mod_fastcgi.c>

	# all .php files will be pushed to a php5-fcgi handler
	# $_SERVER['REDIRECT_HANDLER'] in php will contain this name
	AddHandler php5-fcgi-handler .php

	# redirect handling of type "php5-fcgi" to /php5-fcgi-uri uri
	# $_SERVER['ORIG_SCRIPT_NAME'] will contain this name
	Action php5-fcgi-handler /php5-fcgi-uri

	# map the /cool-uri uri to a location (the location need not be real) which is the fcgi application
	# $_SERVER['ORIG_SCRIPT_FILENAME'] will contain this name
	Alias /php5-fcgi-uri fcgi-application

	# define the fcgi application
	# Make sure to use -flush, otherwise php output gets buffered 
	FastCgiExternalServer fcgi-application -socket /var/run/php5-fpm.sock -pass-header Authorization -idle-timeout 30000 -flush

</IfModule>

This configuration goes inside the VirtualHost block of the site that wants to use fpm to run php scripts.

Note the last line. It declares an external fastcgi server using the FastCgiExternalServer directive of mod_fastcgi. Over here we specify the fpm socket we want to use. So change it to

FastCgiExternalServer fcgi-application -socket /var/run/php5-fpm-mysite.sock -pass-header Authorization -idle-timeout 30000 -flush

Restart apache and now the particular vhost should use that fpm socket (and hence that fpm pool) to run php scripts.

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

6 Comments

Run php-fpm with separate user/uid and group on linux
  1. codealfa

    If you’re using an application that makes use of PHP sessions you may need to add your user to the apache group. The sessions folder is configured for access only to root and the apache group and restricts access to everyone else, for security reasons presumably.

  2. dan

    What’s the memory consequences of this? If I have 100 different sites, doesn’t that mean that there are at minimum 100 processes staying open? It seems like there should be a module like suphp that will work with FPM so that we don’t have to spawn so many idle processes, but still get the user isolation

  3. Chris

    I’m still getting permission errors. Do I need to set a “umask” in the php-fpm script? What does your file/folder permissions look like? Thanks for any help.

  4. Locke

    everything is working perfectly thank u … but now I’m getting this errors

    [03-May-2014 22:54:26] ERROR: An another FPM instance seems to already listen on /tmp/php5-fpm.holin.sock

    [03-May-2014 22:54:26] ERROR: FPM initialization failed

    do you know why this is happening??

Leave a Reply

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