With 18% of the internet running off WordPress and between 50–100k sites launching on it every day using WordPress seemed the obvious choice. Where to host it however, was a more difficult decision. With such a large market share comes a myriad of deployment options. From large turnkey blog as a service providers like WordPress.com and WP Engine to complete roll your own WordPress.org on AnyHostUSA. In the end I decided to go with a happy median, running WordPress on a PaaS cloud provider (I hear it’s web scale that way) for the extra flexibility. This also has the benefit of giving me an excuse to learn more about the internals of cloud services.
Being one of the first PaaS providers Heroku is one of the largest. Originally a RoR provider they have expanded to many other languages including PHP, which is as of yet undocumented. A quick google search reveled that there are already others running with this setup and there’s even a repo for it on GitHub. True to the promise of the cloud, a couple of minutes and a handful of commands later I saw the following scroll by my terminal.
-----> PHP app detected -----> Bundling Apache version 2.2.22 -----> Bundling PHP version 5.3.10 -----> Discovering process types Procfile declares types -> (none) Default types for PHP -> web -----> Compiled slug size: 14.2MB -----> Launching... done, v6 http://XXXXX.herokuapp.com deployed to Heroku
A single git push and a full LAMP stack gets bootstrapped & starts serving this very WordPress site within seconds. It’s the future!
Trouble in Paradise
One of the first plugins I wanted to install was Jetpack which bundles a bunch of neat features together. However just installing and pushing to production proved fruitless. While the plugin showed up in the control panel correctly, enabling it and trying to connecting to a WordPress.com account would always produce a
register_http_request_failed error. Checking the Heroku access logs showed the following.
2013-06-03T13:15:21.391493+00:00 heroku[router]: at=info method=HEAD path=/wp-admin/admin.php?page=jetpack&action=register&_wpnonce=XXXXXXXXXX host=www.xyu.io fwd="184.108.40.206" dyno=web.1 connect=1ms service=16488ms status=200 bytes=0 2013-06-03T13:15:21.511473+00:00 heroku[router]: at=info method=POST path=/xmlrpc.php?for=jetpack host=www.xyu.io fwd="220.127.116.11" dyno=web.1 connect=13ms service=15066ms status=200 bytes=207 2013-06-03T13:15:21.810721+00:00 app[web.1]: 10.68.182.136 - - [03/Jun/2013:13:15:04 +0000] "HEAD /wp-admin/admin.php?page=jetpack&action=register&_wpnonce=XXXXXXXXXX HTTP/1.1" 200 - 2013-06-03T13:15:21.810721+00:00 app[web.1]: 10.241.82.47 - - [03/Jun/2013:13:15:21 +0000] "POST /xmlrpc.php?for=jetpack HTTP/1.1" 200 207
It appears the connect to WordPress.com request triggers a XML-RPC callback from the Jetpack servers to complete the setup. However Heroku web dynos are by default single threaded and the initial connect request is blocking the callback from executing.
With each dyno being allocated 512MB of memory this hard limit seemed very weird but the
httpd.conf on the dynos indeed showed
ServerLimit 1 MaxClients 1
This seems like a highly inefficient allocation of resources, spinning up an entire stack for a single Apache child process. Luckily, Heroku allows custom bootstrapping of dynos via a
Procfile. Using this method I added an include to the end of
httpd.conf to load app specific overrides, allowing in my case up to 8 concurrent connections.
Securing the Server
Normally all files in the repo pushed will live in
/app/www/ which is also the
DocumentRoot. This in effect makes all files in the repo web accessible unless protected via an
.htaccess file. Although there is nothing too sensitive being pushed I would rather not expose all my config files. Given that we are already parsing an app specific
httpd.conf file it was a no brainer to move web files to an
/app/www/htdocs/ subdirectory and set the
With these changes in place Jetpack now connects properly and the site is a bit snappier due to the concurrent connections to boot. I’ve pushed my branch to GitHub and initiated a pull request perhaps others will also find it helpful.