Tips & Tricks for Technologists & System Administrators | About & Contact

How to Turn PHP into an RPM The Easy Way with FPM



So you have a custom PHP binary that you wish to distribute across multiple servers.

The “old” way to do this is to create a bash script in order to pull down the source code, run ./configure and then make; make install. But this is 2013, we have a much more civilized way to create this package now, so that you can distribute that RPM across multiple nodes or even include it in your own yum repository.

We’re going to use FPM (Effing Package Manager) to do the job for this. It is a little bit confusing because PHP also includes the acronym FPM for FastCGI Process Manager. Our package manager FPM is a ruby gem, so install the prerequisites first (CentOS 6 directions here), along with fpm:

1
2
# yum install ruby ruby-devel rubygems
# gem install fpm

To start, compile PHP the way you normally would, ie:

1
2
3
4
5
6
'./configure'  '--program-prefix=' '--prefix=/usr' \
'--exec-prefix=/usr' '--bindir=/usr/bin' '--enable-ftp' \
'--enable-calendar' '--with-libxml-dir=/usr' '--enable-mbstring' \
'--enable-mbregex' '--with-gd' '--enable-zip' '--with-mcrypt' \
'--with-mhash' '--with-libdir=lib64' '--enable-fpm' '--with-fpm-user=www' \
'--with-fpm-group=www' '--with-mysql=mysqlnd' '--with-mysqli=mysqlnd'

Then make it as normal.

1
2
# make
... Lots of output

Now instead of running make install like normal, we tell it to output to another directory, just some temporary directory on the server:

(Note, if compiling another program, the switch to make it install into another directory may be different, such as DESTDIR=/tmp/installdir)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
make install INSTALL_ROOT=/tmp/installdir
Installing PHP SAPI module:       fpm
Installing PHP CLI binary:        /tmp/installdir/usr/bin/
Installing PHP CLI man page:      /tmp/installdir/usr/share/man/man1/
Installing PHP FPM binary:        /tmp/installdir/usr/sbin/
Installing PHP FPM config:        /tmp/installdir/etc/
Installing PHP FPM man page:      /tmp/installdir/usr/share/man/man8/
Installing PHP FPM status page:      /tmp/installdir/usr/share/fpm/
Installing build environment:     /tmp/installdir/usr/lib/build/
Installing header files:          /tmp/installdir/usr/include/php/
Installing helper programs:       /tmp/installdir/usr/bin/
  program: phpize
  program: php-config
Installing man pages:             /tmp/installdir/usr/share/man/man1/
  page: phpize.1
  page: php-config.1
/usr/local/src/php-5.3.25/build/shtool install -c ext/phar/phar.phar /tmp/installdir/usr/bin
ln -s -f /usr/bin/phar.phar /tmp/installdir/usr/bin/phar
Installing PDO headers:          /tmp/installdir/usr/include/php/ext/pdo/

Now PHP is happily installed in /tmp/installdir. Which is great, but we can’t use it until it we package it up for regular installation. Run the FPM command to pack it all up:

1
2
3
4
5
# fpm -s dir -t rpm -n php-fpm -v 5.3.25 -C /tmp/installdir \
   -p php-fpm_VERSION_ARCH.rpm -d openssl -d pcre -d bzip2 -d curl \
   -d libjpeg -d libpng -d freetype -d gmp -d libmcrypt -d libmhash \
   -d libxml2 usr/bin usr/share/man/man1 usr/sbin etc \
   usr/share/man/man8 usr/share/fpm usr/lib/build usr/include/php

What do the options mean?

I’ll go through these options:

  • -s dir: source is a directory
  • -t rpm: we are creating an rpm
  • -n php-fpm: we are creating a package with the name php-fpm
  • -v 5.3.25: the version of the rpm (so you can manage upgrades)
  • -C /tmp/installdir: the base location to take the files from
  • -p php-fpm_VERSION_ARCH.rpm: fpm will replace the VERSION and ARCH macros automatically with your version number and architecture
  • -d openssl -s pcre …: these are all of the rpm dependencies that you’ll want on the machines installing the package. If you don’t include the proper ones (to match your source system, and which are included in ./configure, you’ll get library errors when you try to launch the program.
  • usr/bin usr/share/man/man1 usr/sbin etc usr/share/man/man8 usr/share/fpm usr/lib/build usr/include/php: the directories you wish to include in your package file

When all is done, you should have your rpm file located in /tmp/installdir. You can install it directly on any system with rpm -i <rpm name>

Before you package up your files, you can include any other files you want. For example, you could include the init script or php.ini file. But if you are managing the system using puppet or chef, it would be better to manage this configuration file through there rather than from the rpm file. This works really well as a ‘building block’ for your programs when you add these custom rpms into your own repository. That way, you can have your system install custom versions of the binaries where applicable but still have the niceties that come from an systems automation with puppet or chef.

Enjoy!


Originally posted 20130509 and last touched 20130827
Dave Drager+ is the Chief Technology Officer at XDA-Developers, where he keeps the server farm running efficiently for millions of visitors per day. He has written previously for the technology blogs Lifehacker and MakeUseOf.


  • http://www.biztechconsultancy.com/php-development-india.htm php development india

    This was the post which i looked out from many days. Now, I found it here and also in-depth and understandable language impacts a lot to easily get. Thanks, Dave.

  • http://www.mylifepuzzle.com/ Nalinda Karunartna

    Hi there, wonderful post and feel truly grateful to you. I was actually using the same procedure to build a different package, precisiouly openscap for RHEL 6 and RHEL 5 systems. All above steps worked for me except “make install INSTALL_ROOT=/tmp/installdir”, it never get installed to the location what I wanted. Did some digging and found following solution,

    make DESTDIR=/tmp/installdir install

    Hope this helps someone.

  • http://davedrager.com Dave Drager

    Yes, INSTALL_ROOT is specific for PHP. Most other install scripts use DESTDIR, I will make a note of this in the post. Glad to help!