Run Multiple Versions of PHP-FPM on FeeeBSD

Let’s install two version of PHP-FPM on the same FreeBSD server! This is not something that Ports or Packages really supports, but with a few cheats, we can make Ports do most of the hard work, so we don’t have to install them from scratch like cavemen. I’m installing 5.6 and 7.2 but this should probably work with other versions.

The first and dumbest step is to install and immediately delete php 5.6 and 7.2 via ports. This gets all of the build and run dependencies installed, in a very normal way, before we start building things with weird flags that muck everything up when they cascade down to dependencies in the build process. No, you can’t (easily) use packages for this because you want those build dependancies installed. There is probably a smarter way to do this.

cd /usr/ports/lang/php56
make install clean
pkg del php56

cd /usr/ports/lang/php72
make install clean
pkg del php72

Now build them again, with flags that change where they are installed and disable the Ports system’s normal good sense about installing software that conflicts. This is the smart bit that I stole from the FreeBSD Forums.

cd /usr/ports/lang/php56
make PREFIX=/usr/local/php56 PHPBASE=/usr/local/php56 install clean DISABLE_CONFLICTS=1

cd /usr/ports/lang/php72
make PREFIX=/usr/local/php72 PHPBASE=/usr/local/php72 install clean DISABLE_CONFLICTS=1

So now you have everything for each of the two versions installed under /usr/local/php56 and /usr/local/php72, respectively. They install rc scripts that we will need to adapt so we can run them at the same time:

cp -p /usr/local/php56/etc/rc.d/php_fpm /usr/local/etc/r.cd/php56_fpm
perl -pi -e 's/php_fpm/php56_fpm/' /usr/local/etc/r.cd/php56_fpm
perl -pi -e 's/php-fpm.pid/php56-fpm.pid/' /usr/local/php56/etc/r.cd/php72_fpm
perl -pi -e 's/php-fpm.pid/php56-fpm.pid/' /usr/local/php56/etc/php-fpm.conf
perl -pi -e 's#listen = 127.0.0.1:9000#listen = /tmp/php56-fpm-www.sock#' /usr/local/php56/etc/php-fpm.conf
echo 'php56_enable="YES"' >> /etc/rc.conf
service php56-fpm start

The perl one-liners are just a compact and copy-paste-able way to say you need to edit files to replace certain things. The rc files need to be edited to differentiate them, so they each have their own rcvars, and pid files. The php-fpm.conf files are edited to sync that pid file change and differentiate the listen sockets.

cp -p /usr/local/php72/etc/rc.d/php_fpm /usr/local/etc/r.cd/php72_fpm
perl -pi -e 's/php_fpm/php72_fpm/g' /usr/local/etc/r.cd/php72_fpm
perl -pi -e 's/php-fpm.pid/php72-fpm.pid/' /usr/local/php72/etc/r.cd/php72_fpm
perl -pi -e 's/php-fpm.pid/php72-fpm.pid/' /usr/local/php72/etc/php-fpm.conf
perl -pi -e 's#listen = 127.0.0.1:9000#listen = /tmp/php72-fpm-www.sock#' /usr/local/php72/etc/php-fpm.d/www.conf
echo 'php72_enable="YES"' >> /etc/rc.conf
service php72-fpm start

In this example, I have opted to change the “listen” directive to a unix socket. You could use tcp sockets too, but you will have to choose a different port for each version of PHP-FPM that you are running.

cd /usr/ports/www/apache24
make install clean
echo 'apache24_enable="YES"' >> /etc/rc.conf
vi /usr/local/etc/apache24/httpd.conf

Obviously you’ll want to edit httpd.conf to suit your needs. That is beyond the scope of this post, but I will be uncommenting the “proxy_fcgi_module” line so apache can talk to PHP-FPM.

Now add FPM pools to one PHP-FPM or the other, or both. For example:

[haroldp.test]
user = haroldp
group = haroldp
listen = /tmp/php72-fpm-haroldp.test.sock
listen.mode = 0666
chroot = /home/haroldp
pm = ondemand
pm.max_children = 50
php_admin_value[doc_root] = /haroldp.test/htdocs
php_admin_value[cgi.fix_pathinfo] = 0
php_admin_value[sendmail_path] = /bin/mini_sendmail -t

And add vhosts to apache, pointing to a php-fpm 5.6 socket or a php-fpm 7.2 socket, at your option. Maybe something like:

<VirtualHost *:80>
  ServerName haroldp.test
  DocumentRoot /home/haroldp/haroldp.test/htdocs
  SuexecUserGroup haroldp haroldp
  ErrorLog /home/haroldp/haroldp.test/logs/haroldp.test.error_log
  CustomLog /home/haroldp/haroldp.test/logs/haroldp.test.access_log combined
  <Directory /home/haroldp/haroldp.test/htdocs">
    Order allow,deny
    Allow from all
    Options +Indexes +FollowSymLinks +ExecCGI +Includes +MultiViews
    AllowOverride All
  </Directory>
  <FilesMatch .php$>
    ProxyFCGIBackendType GENERIC
    SetHandler "proxy:unix:/tmp/php72-fpm-haroldp.test.sock|fcgi://localhost/home/haroldp/haroldp.test/htdocs$1"
  </FilesMatch>
</VirtualHost>

Switching between them is a two byte change to the apache vhost config.

service apache24 start
service php72-fpm restart
service php56-fpm restart

Upgrades

Running something like:
cd /usr/ports/lang/php56
make PREFIX=/usr/local/php56 PHPBASE=/usr/local/php56 deinstall reinstall DISABLE_CONFLICTS=1

for each version should upgrade it.

Concerns

My big worry with this approach is that a future update may create a requirement for incompatible dependancies between the two ports.

I would prefer an approach that builds each PHP-FPM in it’s own jail, but of course they need access to the file system that hosts the websites. Is there a smart way to do that? Put /home on its own ZFS volume and mount it in each PHP jail? And share a /tmp between all of them for the unix sockets? You’d need to keep user accounts synced or use LDAP for authentication.

One comment on “Run Multiple Versions of PHP-FPM on FeeeBSD”

  • Hi Harold,

    Thanks for the post.
    Not lost in the ether.

    Currently testing this out with FreeBSD 13.1-RELEASE-p1.
    Caddy will send requests to the respective sockets.

    Have a great day!
    Cheers again,
    Carl

    Reply

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>