Hosting a WordPress site on AppFog, without persistent file storage

Hosting a WordPress site on AppFog, without persistent file storage

Earlier I wrote a very positive post about my experience of migrating from a shared web host to AppFog’s cloud platform.  However, there are some quirks and caveats with my WordPress site for which I’ve had to find workarounds.

Chief among them is that AppFog currently does not offer a persistent file system.  As with most PaaS providers right now, all persistent data is expected to go in a database.

WordPress logoWhat this means is that every time your application restarts, all files revert to the state they were in when last uploaded.  Most WordPress activity, such as posts and comments, lives in the database and requires no special consideration.  However, if you install some plugins or themes, or any other updates which write to files, then all those changes will be rudely rolled-back.  Even worse, the database will not be rolled-back, so your WordPress files and data may be out of sync and broken.

AppFog is working on a persistent distributed filesystem with FTP access, and this might solve the problem altogether.  In the meantime, it’s still easier than you might think to work around the volatility, once you get an initial setup in place.  AppFog has some documentation for WordPress users, but the page is fairly high-level and leaves you to work out the details on your own.  Below is a more thorough overview of steps that will make it work.

One-time setup:

  1. Install a LAMP or WAMP stack, the AppFog command-line tools, and (optionally) Git or some other source control system.
  2. Download your WordPress app code from AppFog, and (optionally) place it in source control.
  3. Point your main Apache document root (or a virtual host) at your WordPress location, and setup permissions so that WordPress can write to its filesystem.
  4. Download an export of your WordPress database from AppFog, and import it into MySQL locally.

Workflow for maintenance tasks:

  1. Refresh your local code and database from their AppFog counterparts.
  2. Temporarily update “wp-config.php” and “/etc/hosts”, and fire up your local servers.
  3. Perform your WordPress maintenance tasks, and revert your “wp-config.php” and “/etc/hosts” when done.
  4. Refresh your AppFog code and database from their local counterparts, and (optionally) commit your changes to source control.

Some more detail about these steps…

Installing a LAMP or WAMP stack

A LAMP is a software bundle including Linux, Apache, MySQL, and usually PHP.  A WAMP is the same thing, running on Windows instead of Linux.  You can install and configure all of these components separately, but if that sounds like fun, then you are not the target audience for a PaaS provider!

There are several all-in-one LAMP or WAMP packages available, which are more or less ready out of the box, and allow you to easily run all of the services only when needed.  On Windows, I’ve had the best experience with Uniform Server, and on Linux I’ve had the least trouble with XAMPP.

Downloading your WordPress source code

The AppFog documentation assumes that you will create your WordPress site on your local machine, and then upload it via the command-line interface.  That’s certainly an option, but I think it’s much easier to let AppFog create the app for me using their browser-based console.  If you’re migrating an existing site, then you can easily export your posts and comments and migrate them using the WordPress dashboard.

You will need to download AppFrog’s command-line interface to copy files to and from your local computer.  This set of Ruby-based tools is fairly simple to use, and AppFog has good installation instructions here.  With the interface installed, you can download your current files with these two commands:

af login <your-email-address>
af pull <your-app-name>

I chose to place this directory under source control with Git.  Even though I don’t plan on pushing it to any remote repository, this still gives me the ability to monitor and undo changes locally.

Apache config

NOTE:  There are few things in this world that I hate more than tinkering with Apache, and this section is by far the most painful part of the one-time setup.  If any admin wizards want to take me to school and suggest improvements, then I would love to hear your comments!

In a more production-like situation, you might add a virtual host to your Apache configuration, and point its document root at the directory containing your AppFog app.  However, since I’m using XAMPP on my laptop, I decided to keep things simple by replacing the /opt/lampp/htdocs directory with a symbolic link to my app directory.

I then set my app’s file permissions to 777, so the Apache process can write to them even though they’re owned by my user account.  This is generally a very bad practice, but your local server should be firewalled from the open Internet.  You could try changing Apache to run under your user account instead, but this just caused me problems with phpMyAdmin and other issues.

sudo mv /opt/lampp/htdocs /opt/lampp/original-htdocs
sudo ln -s /home/myuser/myapp /opt/lampp/htdocs
chmod 777 -R /home/myuser/myapp

If you are using Git, then you might not want Git to treat these permission changes as file updates.  You can suppress this by running this command from your app directory:

git config core.filemode false

Fooling with permissions in Apache’s http.conf file has been a nightmare, and other configuration choices might make more sense.  However, I have been able to make all this work by editing the document root section to look like this:

<Directory />
    AllowOverride none
    Require all denied

DocumentRoot "/opt/lampp/htdocs"
<Directory "/opt/lampp/htdocs">
    AllowOverride All
    Require all granted
    Order Allow,Deny
    Allow from All

Database config

The AppFog documentation talks about using their command-line interface to tunnel in to your remote database, and then using the standard MySQL client to make an export.  That’s fine, but AppFog also offers phpMyAdmin as one of their app templates (just attach your WordPress database as an attached service).  Most LAMP and WAMP packages include phpMyAdmin as well.  So I found it easier to import and export using this familiar tool.

WordPress stores your database connection info, among other things, in the wp-config.php file.  The AppFog version fetches your MySQL credentials by parsing JSON data stored in an environment variable.  For your local installation, the easiest approach is simply hard-coding those values.  So save a backup copy of wp-config.php, and edit the edit the original to look like this (read the comments):


define('DB_NAME', 'abcdefghijklmnopqrstuvwxyz');  // Get your database name from the AppFog command-line interface, or an export file, and create a local MySQL database with the same name
define('DB_USER', 'root');  // Not a good practice in general, but 'root' is fine for a local XAMPP not exposed to the Internet
define('DB_PASSWORD', '');  // Probably blank, unless you went out of your way to set a MySQL 'root' password when installing your LAMP/WAMP
define('DB_HOST', 'localhost');
define('DB_PORT', '3306');

define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
define ('WPLANG', '');
define('WP_DEBUG', false);


$table_prefix  = 'wp_';

if ( !defined('ABSPATH') )
	define('ABSPATH', dirname(__FILE__) . '/');

require_once(ABSPATH . 'wp-settings.php');

define('FS_METHOD', 'direct');  // Add this line to tell WordPress that it can write updates to its own filesystem

The last line is a very important addition.  Without it, you won’t be able to install updates from the WordPress dashboard.

If you keep your application in source control, take care not to commit these wp-config.php changes permanently.  When you push your updated files back to AppFog later, you will need to revert to the original version of this file.

DNS config

Your WordPress application is configured to think it is on a certain domain name (e.g. “”).  WordPress uses this domain name on every page, to dynamically generate links.  Now that you’re running on “localhost” instead, WordPress is out-of-sync with reality and won’t generate correct links.

You might edit your WordPress database by hand, and temporarily change the site address to “localhost”, but that sounds like more hassle than it’s worth.  The easier approach is to temporarily edit your local DNS, to create the same effect as running WordPress on the real domain.

This is as simple as editing your hosts file, which is located at /etc/hosts on UNIX derivatives, and C:\Windows\System32\drivers\etc on Windows.  Add two lines resembling the following:

That’s It?

Piece of cake, right?  Okay, I freely admit that the steps above are a huge pain.  I hope that this information is helpful to someone, because it was even more painful working out all the kinks on my own.

However, once you have this one-time setup complete, the regular process for maintenance tasks is pretty simple.  Update your local database with a fresh AppFog export, and tweak wp-config.php and /etc/hosts to local settings.  Perform your maintenance tasks.  Revert wp-config.php and /etc/hosts, and push a local database export and your updated files back to AppFog.  The file update command should be run from within your application’s directory, and looks like this:

af login <your-email-address> 
af update <your-app-name>

The only nagging issue is hosting images and other media assets.  The easiest approach is to keep images outside of your WordPress media library, and host them instead on Amazon S3 or some free image host.  However, there are some WordPress functions that really want your images to be in the local media library, such as the “featured posts” slider on the front page of this site.

I’ve gotten in the habit of using an off-site image host while writing drafts, taking a quick outage to update my media library, and then changing the links before publishing posts.  I typically use only one or two images per post, so this is manageable, but it is definitely far from ideal.  Of course, once AppFog gets its persistent filesystem solution in place, hopefully all of these extra efforts will become unnecessary.