Tips for Hosting WordPress on Pantheon

Pantheon has long been hosting Drupal sites, and their entry into the WordPress hosting marketplace is quite welcome. For the most part, hosting WordPress sites on Pantheon is a dream for developers. Their command line tools and git-based development deployments, and automatic dev, test, live environments (with the ability to have multiple dev environments on some tiers) are powerful things. If you can justify the expense (and they’re not cheap), I would encourage you to check them out.

First, the good stuff:

Git-powered dev deployments

This is great. Just add their Git repo as a remote (you can still host your code on GitHub or Bitbucket or anywhere else you like), and deploying to dev is as simple as:

git push pantheon-dev master

Command-line deployment to test and live

Pantheon has a CLI tool called Terminus that can be used to issue commands to Pantheon (including giving you access to remote WP-CLI usage).

You can do stuff like deploy from dev to test:

terminus site deploy --site=YOURSITE --env=test --from=dev --cc

Or from test to live:

terminus site deploy --site=YOURSITE --env=live --from=test

Clear out Redis:

terminus site redis clear --site=YOURSITE --env=YOURENV

Clear out Varnish:

terminus site clear-caches --site=YOURSITE --env=YOURENV

Run WP-CLI commands:

terminus wp option get blogname --site=YOURSITE --env=YOURENV

Keep dev and test databases & uploads fresh

When you’re developing in dev or testing code in test before it goes to live, you’ll want to make sure things work with the latest live data. On Pantheon, you can just go to Workflow > Clone, and easily clone the database and uploads (called “files” on Pantheon) from live to test or dev, complete with rewriting of URLs as appropriate in the database.

No caching plugins

You can get rid of Batcache, W3 Total Cache, or WP Super Cache. You don’t need them. Pantheon caches pages outside of WordPress using Varnish. It just works (including invalidating URLs when you publish new content). But what if you want some control? Well, that’s easy. Just issue standard HTTP cache control headers, and Varnish will obey.


function my_pantheon_varnish_caching() {
	if ( is_user_logged_in() ) {
	$age = false;

	// Home page: 30 minutes
	if ( is_home() && get_query_var( 'paged' ) < 2 ) {
		$age = 30;
	// Product pages: two hours
	} elseif ( function_exists( 'is_product' ) && is_product() ) {
		$age = 120;

	if ( $age !== false ) {
		pantheon_varnish_max_age( $age );

function pantheon_varnish_max_age( $minutes ) {
	$seconds = absint( $minutes ) * 60;
	header( 'Cache-Control: public, max-age=' . $seconds );

add_action( 'template_redirect', 'my_pantheon_varnish_caching' );

And now, some unclear stuff:

Special wp-config.php setup

Some things just aren’t very clear in Pantheon’s documentation, and using Redis for object caching is one of them. You’ll have to do a bit of work to set this up. First, you’ll want to download the wp-redis plugin and put its object-cache.php file into /wp-content/.

Update: apparently this next step is not needed!

Next, modify your wp-config.php with this:

// Redis
if ( isset( $_ENV['CACHE_HOST'] ) ) {
	$GLOBALS['redis_server'] = array(
		'host' => $_ENV['CACHE_HOST'],
		'port' => $_ENV['CACHE_PORT'],
		'auth' => $_ENV['CACHE_PASSWORD'],

Boom. Now Redis is now automatically configured on all your environments!

Setting home and siteurl based on the HTTP Host header is also a nice trick for getting all your environments to play, but beware yes-www and no-www issues. So as to not break WordPress’ redirection between those variants, you should massage the Host to not be solidified as the one you don’t want:

// For non-www domains, remove leading www
$site_server = preg_replace( '#^www\.#', '', $_SERVER['HTTP_HOST'] );

// You're on your own for the yes-www version :-)

// Set URLs
define( 'WP_HOME', 'http://'. $site_server );
define( 'WP_SITEURL', 'http://'. $site_server );

So, those environment variables are pretty cool, huh? There are more:

// Database
define( 'DB_NAME', $_ENV['DB_NAME'] );
define( 'DB_USER', $_ENV['DB_USER'] );
define( 'DB_PASSWORD', $_ENV['DB_PASSWORD'] );
define( 'DB_HOST', $_ENV['DB_HOST'] . ':' . $_ENV['DB_PORT'] );

// Keys
define( 'AUTH_KEY', $_ENV['AUTH_KEY'] );
define( 'LOGGED_IN_KEY', $_ENV['LOGGED_IN_KEY'] );
define( 'NONCE_KEY', $_ENV['NONCE_KEY'] );

// Salts
define( 'AUTH_SALT', $_ENV['AUTH_SALT'] );
define( 'NONCE_SALT', $_ENV['NONCE_SALT'] );

That’s right — you don’t need to hardcode those values into your wp-config. Let Pantheon fill them in (appropriate for each environment) for you!

And now, some gotchas:

Lots of uploads = lots of problems

Pantheon has a distributed filesystem. This makes it trivial for them to scale your site up by adding more Linux containers. But their filesystem does not like directories with a lot of files. So, let’s consider the WordPress uploads folder. Usually this is partitioned by month. On Pantheon, if you start approaching 10,000 files in a directory, you’re going to have problems. Keep in mind that crops count towards this limit. So one upload with 9 crops is 10 files. 1000 uploads like that in a month and you’re in trouble. I would recommend splitting uploads by day instead, so the Pantheon filesystem isn’t strained. A plugin like this can help you do that.

Sometimes notices cause segfaults

I honestly don’t know what is going on here, but I’ve seen E_NOTICE errors cause PHP segfaults. Being segfaults, they produce no useful information in logs, and I’ve had to spend hours tracking down the code causing the issue. This happens reliably for given code paths, but I don’t have a reproducible example. It’s just weird. I have a ticket open with Pantheon about this. It’s something in their custom error handling. Until they get this fixed, I suggest doing something like this, in the first line of wp-config.php:

// Disable Pantheon's error handler, which causes segfaults
function disable_pantheon_error_handler() {
	// Does nothing

if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
	set_error_handler( 'disable_pantheon_error_handler' );

This just sets a low level error handler that stops errors from bubbling up to PHP core, where the trouble likely lies. You can still use something like Debug Bar to show errors, or you could modify that blank error handler to write out to an error log file.

Have your own tips?

Do you have any tips for hosting WordPress on Pantheon? Let me know in the comments!

Ask Mark Anything

People ask me a lot of questions. About WordPress and web development for sure, but also about other topics. I’ve decided to try a little experiment: a public way to ask me questions. Zach Holman from GitHub had the idea to use a GitHub issue tracker for this very purpose, and I think it looks like a splendid idea.


  • Allows for more in-depth discussions than Twitter (but you can still talk to me on Twitter for quick questions).
  • Is public (as opposed to e-mail).
  • Forces me to deal with questions.

Now, note that this doesn’t mean I want you to treat me like your personal Google-searcher or WordPress code grepper! But if you think there is a WordPress (or other) topic that I am uniquely qualified to address, just ask.

Fragment Caching in WordPress

Fragment caching is useful for caching HTML snippets that are expensive to generate and exist in multiple places on your site. It’s like full page HTML caching, but more granular, and it speeds up dynamic views.

I’ve been using this fragment caching class for a few years now. I optimized it around ease of implementation. I wanted, as much as possible, to be able to identify a slow HTML-outputting block of code, and just wrap this code around it without having to refactor anything about the code inside.

Implementation is pretty easy, and you can reference the comment at the start of the code for that. The only thing to consider is that any variables that alter the output need to be build into the key. It should also be noted that this code assumes you have a persistent object cache backend.

Translating WordPress Plugins and Themes: Don’t Get Clever

When you use the WordPress translation functions to make your plugin or theme translatable, you pass in a text domain as a second parameter, like so:

<?php _e( 'Some Text String', 'my-plugin-name' ); ?>

This text domain is just a unique string (usually your plugin’s repository slug). Well, many plugin developers see code like this:

<?php _e( 'Another Text String', 'my-plugin-name' ); ?>
<?php _e( 'Yet Another Text String', 'my-plugin-name' ); ?>
<?php _e( 'Gosh, So Many Text Strings!', 'my-plugin-name' ); ?>

And they think to themselves “hm, I sure am typing the 'my-plugin-name' string a lot. I’ll apply the DRY (Don’t Repeat Yourself) principle and throw that string into a variable or a constant!”

Stop! You’re being too clever! That won’t work!*

See, PHP isn’t the only thing that needs to parse out your translatable strings. GNU gettext also needs to parse out the strings in order to provide your blank translation file to your translators. GNU gettext is not a PHP parser. It can’t read variables or constants. It only reads strings. So your text domain strings needs to stay hardcoded as actual quoted strings. 'my-plugin-name'.

Happy coding!

* Well, it won’t break your plugin, but it could make it harder to be used with automated translation tools. And trust me, you don’t want to be manually managing your translation files… we have a better solution coming.

How to write a WordPress plugin that I’ll use

I tend to be very fastidious about the WordPress plugins that I’ll install. I’ll often write my own simple version of a plugin rather than install one from someone else that does a bunch of stuff I don’t need. Here is my philosophy behind writing WordPress plugins, best witnessed through the plugins I’ve written lately, like Markdown on Save, Login Logo, Monitor Pages, and WP Help.

Fewer features as a feature

There are diminishing returns as you add features. That is, the more you add, the more likely you’re adding something that X % of your plugin’s users won’t ever use. Stick to the basics. I’ll often release a “0.1” version of my plugin with really obvious features missing. When I get a flurry of “You should add Y!” messages, that validates my assumption that Y is necessary. Start with the smallest version that gets the core job done. Iterate as needed.

Code the hell out of it

The best part of starting small is that you can code the hell out of the plugin. Do it right. Make each line of code beautiful. Make sure you’re using WordPress APIs properly, and while you’re at it, add i18n support (WP Help 0.2 shipped with support for Bulgarian, German, Spanish, Mexican Spanish, Macedonian, Dutch, Brazilian Portuguese, and Russian!)

Reduce UI

If you can do without UI, don’t make it. Make every bit of UI prove its necessity. As an example, look at my Login Logo plugin. It has zero UI. It looks for the presence of a file named login-logo.png in the wp-content directory. The rest is “magic.” It measures the image, generates appropriate CSS, and gives you an instantly and easily customized login screen. The plugin is invisible. It’s completely out of sight, and out of mind. Finally, UI screens are generally where plugin authors make security mistakes. By skipping them, you make it much more likely that your plugin is secure.

Code it for the future

Don’t use deprecated APIs. Plan features in future-forward ways. Implement it in such a way that a site that is using the plugin doesn’t break if the plugin suddenly goes away. One example of this is my Markdown on Save plugin, which offers per-post Markdown formatting. First, I decided that for performance reasons, I wanted to parse Markdown then the post was updated, not on display. The obvious place to store the generated HTML was in the post_content_filtered column that WordPress provides (but does not use). But then I considered what would happen if someone deactivated the plugin or deleted the plugin. The code that accessed post_content_filtered would not work. Their blog would spit out raw Markdown. And any exports they made would export raw Markdown. What if they were exporting to which doesn’t support Markdown? So I decided to store the Markdown in post_content_filtered, and store the generated HTML in post_content. When you edit a Markdown-formatted post, it swaps in the Markdown, so you can edit that. But if you deactivated the plugin, it would fall back to the HTML. So you can feel free to use this plugin and know that if one day you wake up and you hate Markdown, all you have to do is deactivate the plugin and all of your posts are back to HTML.

Secure it

Writing secure WordPress plugins isn’t hard. It just takes awareness. Take the time to do your research and code a plugin that will be an asset to its users, not a liability.

Developing on WordPress using Git

WordPress uses Subversion (SVN) for revision management. Before Subversion, it used CVS. Right now, Git is a hot option in the SCM category. It offers really nice features such as decentralization, speed, fast and cheap local branching, better merging, more offline capabilities, staging of commits, and lots more. It’s premature to talk about moving WordPress core and plugins to another SCM system — we have a lot invested with Subversion and Trac. But be of good cheer. You can have your Git and commit to Subversion too! Here’s how I do it.

First, tools. You’ll need Git, obviously. But you’ll also need git-svn-diff, a Bash script that generates Subversion-compatible diffs.

Download git-svn-diff, put it somewhere in your path, and make it executable. Like this:

curl -L > /usr/local/bin/git-svn-diff
sudo chmod +x /usr/local/bin/git-svn-diff

Next, to enable you to do git svn-diff instead of git-svn-diff, edit ~/.gitconfig and add this:

	svn-diff = !git-svn-diff

This next step is going to take a while. You’re going to pull down WordPress’ SVN history using Git’s SVN support.

git svn clone -t tags -b branches -T trunk

You might want to let that run overnight. Really. It’s going to go through each changeset.

Once you’re done, you should be in the Git master branch, which corresponds to WordPress SVN’s trunk. WordPress’ branches are in remotes/{name}

To pull in the latest changes from SVN, use git svn rebase. Important rule: never modify the SVN branches (remotes/{name}). Instead, create a new topic branch.

For example, say that I’m going to work on a ticket for trunk. I’d create a new branch from remotes/trunk like this:

git checkout -b ticket-12345 remotes/trunk

That will create a new local Git branch called ticket-12345 based on SVN’s trunk, and then check it out (i.e. switch to it).

If you’re working on a WordPress SVN branch, you can do something like this:

git checkout -b ticket-12345 remotes/3.1

Do your work in the branch you created. You can make multiple local Git commits if you want, to break up your work into smaller chunks that make sense to you.

When you’re ready to submit your patch, use git-svn-diff to produce it.

git svn-diff > ~/12345.diff

If you have commit access, you can commit to Subversion from this topic branch. But be careful! First you should do git svn rebase to bring your patch up to date. Next, you should squash your local git commits, otherwise each one of them will be individually committed to SVN (hello, flood). So rebase your commits into one commit, like so:

git rebase -i remotes/trunk

Use “reword” on the first commit. Use “fixup” on the subsequent ones. That will roll the commits up into one. You’ll then be prompted to enter your amended commit message for that commit amalgam.

Ready? You can now commit to SVN using:

git svn dcommit

Git knows which remote SVN branch it came from when you checked out your topic branch. You can verify which one it is attached to by doing:

git svn info

A few tips:

Create a .gitignore file. This lists files or directories that you want Git to ignore. First, you want Git to ignore the .gitignore file itself! Next, you want Git to ignore your local wp-config.php Finally, you want to ignore any additional plugins, must-use plugins, themes, uploads, etc. Just do a git status and add anything that you don’t want to commit to WordPress or put in your patches.

I hope you found this helpful! Let me know if you have any questions.

Just you and your thoughts

In 2007, I wrote this about the job of software:

That’s when I know WordPress is doing its job: when people aren’t even aware they’re using it because they’re so busy using it!

I cited that more as a direction, than a goal. If the job of software is to get out of the way, it never completely reaches it — it just gets closer and closer. Sort of how dividing a number in half an infinite number of times never quite gets you to zero.

Today, in 2011, I took this screenshot of the Distraction-Free Writing interface for the upcoming WordPress 3.2:

screenshot of WordPress Distraction Free Writing interface. A title, and a body.

How’s that for getting out of your way?