Packaging PHP in Habitat. A Tale Of Two Drupals.

(This post was originally published at

Early in my Habitat journey I packed a
super-simple Python application that I had developed. It served no
purpose other than to be packaged. Once I started to get my head
around the fundamentals of packaging with Habitat, my mind turned to
more useful things.

These are interesting in their own right and you’ll be hearing more
about them after ChefConf. For now,
I want to talk about Drupal.

Drupal is “just” some PHP. How hard can it be? 1 Well, I saw enough
raised eyebrows from my friends at Chef, to understand this might not
be as easy as it seems.

Packaging Any PHP

Let’s forget Drupal for now. The first step is working out how to
package any piece of PHP for Habitat. Well, we really only need
three ingredients:

  • NGINX (or Apache HTTPD, if you so wish)
  • PHP
  • The sources of our PHP application

No rocket science here. We just need to glue it all together. Most of
the hard work is already done for us, since Habitat already has
core/nginx and core/php packages available.

All I really wanted to do was unpack my PHP in a location where NGINX
could access it and then point NGINX to my PHP-FPM socket/ip. This is
all easy enough when you know how.

I didn’t know how. Thankfully, the Habitat public
has a plan available for WordPress 2
which gave me some insight. The “trick” here is to launch PHP-FPM and
then use an init hook to also launch NGINX with a config you give it.

OK, so now Drupal.

Drupal: First Attempt

The first attempt a Drupal was easy enough:

  • The sources of the plan can be found
  • The final package is available

This plan was built exactly as I described above: I download the
source tarball, unpack it in a specific location and configure NGINX
to use that location as a site root. When it is run, the user is
presented with a shiny new Drupal install page:

Drupal Install Page

So, there you have it, a successful Drupal package for Habitat!

Or is it?

Drupal: A Better Attempt?

The problem with my first attempt is that it is not very Habitat-y. Or
Drupal-y for that matter. The cool kids use the Drupal shell
(drush) to deploy/configure Drupal and here
comes my first problem: there’s no Habitat package for drush.

Not to worry: drush could not be any simpler to install; it is used
as a dependency in composer. Thankfully, there is a core/composer
package I can use for this purpose. I rolled out a baggerspion/drush
package with minimal effort. It can be found here.

Drush is important for one particular reason: having installed the
base Drupal (“core”?) system, drush can be used to build a
preconfigured site, including default user credentials and database
configuration. This means we do not need to force the user of the
Drupal package to go through the post-install install script. They’d
be presented with a ready-made site, all configured and ready to go.

Providing most of the details needed to configure a Drupal site using
drush is simple, we can simply dump the info in default.toml to be
pulled out anytime. Mildly trickier is the database config.

Using Habitat Binds For The Drupal Database

Just as with the default account credentials, I could simply dump all
the default database config into default.toml. This would not be a
very Habitat-y thing to do.

Much better is to use service binding, to bind my Drupal service to an
existing database service. In Habitat a service bind is the mechanism
that allows me to say, “this service relies on this other service in
order to run properly”. At runtime, I simply tell my service which
other service to bind to:

$ hab start baggerspion/drupal --bind database:mysql.default

I will spare the full details on how binds work. There is plenty of
on the
Habitat website. What’s important here is that, because I have bound
my Drupal service to a Mysql service, I can now configure my service
to use that database automatically. What does the user see now?

Drupal Login Page

This is much better. I think.

I’m offering 10 points 3 for the first comment telling me where the
default password (account_password) in the
plan comes from! No Googling! Answers below!


  1. I actually said/thought this. I am not proud.
  2. The github repo for this is here. A huge “thank you” to Stark and Wayne for basically teaching me everything I know about packaging PHP for Habitat.
  3. “Paul Adams Points”™ have no monetary value and are non-transferable.
Posted in: