Currently, Heroku supports running custom 3rd party languages on their Cedar stack through the use of custom “build packs”. Build packs are git repos that contain instructions for the Heroku “slug compiler”, which tell the slug compiler how to deploy the HTTP stack. This way, with a custom build pack, one can instruct the slug compiler to download and deploy a custom build of Nginx+PHP-FPM stack with any number of PHP extensions, if so desired.
To this end, as part of my internship, I’ve created a custom build pack for the Heroku Cedar stack that incorporates Nginx, PHP–FPM, with APC, memcache, memcached, phpredis, and newrelic modules. You can find the repo here.
To use, fork, make your own changes, and read the included README file.
Anatomy of a Build Pack
In all Heroku build packs, there is a
bin/ directory containing
release bash scripts. These scripts are executed at different stages of slug compilation.
detect script is run whenever a push is received to check that the app that is pushed is compatible with the current build pack. For PHP apps, the
detect script checks for the presence of an
Following which, the
compile script executes. It generally fetches Nginx, PHP–FPM, and any other required libraries. Then, a
boot.sh script is generated for later use, which contains instructions on how to start the various executables in the deployed HTTP stack.
release script, which sets up the app variables in YAML format, is loaded, but only for the initial deploy. Should you want to modify any variable subsequently, use the
heroku config command instead.
For more details on the slug compilation process, refer to Heroku’s Buildpack Reference page.
Customising a Build Pack
The Heroku slug compiler has a hard coded list of official build packs—the starting point for all customisations. Pick the one that is architecturally the most similar to what you want to build and fork it on GitHub.
I forked this build pack to add other important PHP extensions and to switch to a Nginx web server to reduce memory usage. After all, each web dyno has only 512 MB of memory and the key to reducing costs is to squeeze as many PHP workers as possible into a dyno.
Packaging Build Pack Binaries for Use
After customising a build pack, you must precompile the binaries before they can be deployed onto the stack during the slug compilation phrase. To do so, refer to the
README file and the relevant scripts under the
support directory. Refer to Heroku’s Packaging Binary Buildpack Dependencies page for more details.
Generally, it involves using the Heroku environment to compile the binaries into a tar.gz, uploaded to a S3 bucket, and then later fetched and deployed during the slug compilation phrase. The vulcan gem is used for this. Refer to
support/package_nginx for the details on how to use the Vulcan command.
However, as the PHP compilation is not as straightforward as Nginx, spinning up an AWS EC2 instance (ami-04c9306d) within ` us-east-1
AZ is required. Refer to the support/ec2-up.sh
script for hints on how to programmatically bring up an instance. The support/ec2-build-php.sh` script is to be run on the EC2 instance to automate the compilation routine. Finally, remember to upload the artefacts onto S3 before shutting down the EC2 instance as all data will be lost.
Finally, update the
bin/compile to ensure that the S3 bucket and version numbers are specified correctly.
That’s it! This is how a custom build pack is created and made ready for use. Go forth and run awesome things on the Heroku platform!