How-to: local WordPress development

This article summarizes how I set myself up to develop WordPress websites locally – that is, on my computer with a LAMP (Linux, Apache, MySQL, PHP) stack. This way I can do all the work I want without fear of exposing the site to potential attackers if I break something. There are three components to this: the WordPress files, the database, and some method to publish updates to the site without too much hassle. We’ll actually start from the former.

Note: with this technique, permalinks don’t seem to work correctly. I’m not sure why, and I haven’t looked into it yet. At any rate, it suffices to disable permalinks on the local version of the site (once it’s up and running). Since this setting is stored in the database, this won’t affect the live version of the site at all.

1. Synchronization system

I personally use git a lot when working on C++ and Python projects, so it was a natural extension for me to apply it to web development too. I achieved this using git hooks as described in this article I wrote a while back. For now, we only need to set up the server-side with a blank repository and a hook for the updates. We’ll handle the client-side next.

2. The WordPress files

WordPress is infamous for being somewhat hard to migrate to a different domain, because the absolute URL of the site is embedded all over the place in the database. So simply copying the files and database from the server to some local development environment just won’t work.
Instead, I use a virtual host. The idea here is to “trick” WordPress into thinking it’s on the live site, while I’m actually working on my own machine. A virtual host allows me to “block” outgoing requests from my computer to my-site.com and “redirect” them to my local Apache server, where my development website is located.

2.1 Creating a virtual host

  1. Create a directory for your site. Apache’s web directory is /var/www/. First, create a subdirectory of that to host the site:
    $ sudo mkdir -p /var/www/my-site.com/public_html/
    
  2. Next we need to change ownership of the folder to our user (by default, it’ll be owned by root):
    $ sudo chown -R my-user:my-user /var/www/my-site.com/
    
  3. Now we can create a welcome page for the local site (we’ll use this to test if our virtual host is working):
    $ echo '<h1>Hello from my-site.com!</h1>' > /var/www/my-site.com/public_html/index.php
    
  4. Create virtual host file. This is a configuration file that instructs Apache where to find the website files when it gets a request for the domain my-site.com. First we’ll copy the “template”:
    $ cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/my-site.com.conf
    

    Then edit the ServerName, ServerAdmin and DocumentRoot so they fit our website:

     $ cat /etc/apache2/sites-available/my-site.com.conf
    <VirtualHost *:80>
        ServerName my-site.com
        ServerAdmin webmaster@my-site.com
        DocumentRoot /var/www/my-site.com/public_html
    
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
    
  5. Now enable the website using the a2ensite tool:
    $ sudo a2ensite my-site.com.conf
    $ sudo service apache2 restart
    
  6. This is where the magic happens. So far, we’ve told Apache to “redirect” requests to my-site.com “into” the /var/www/my-site.com/public_html folder. Now we need to intercept outgoing requests to my-site.com and redirect them to Apache. Edit the file /etc/hosts and add the line 127.0.0.1 my-site.com to it. Then restart your network manager and test it out!
    $ sudo vim /etc/hosts
    $ cat /etc/hosts
    127.0.0.1   localhost
    127.0.0.1   zumguy.com
    $ sudo service network-manager restart
    

    Navigating to my-site.com with a browser should take you to the welcome page we created earlier!

Usually, I will leave the “magic line” commented out (with a #) most of the time, so I can visit the online version of the site. Before starting work on the site, I’ll restore the line and restart network-manager.

2.2 Copying the WordPress files

First off, download the entire website into the /var/www/my-site.com/public_html folder. Now copy your wp-config.php file outside of the root (so in /var/www/my-site.com/) and rename it to website-config.php. Then replace the contents of the original wp-config.php with <?php require_once('../website-config.php'); ?>. This allows us to have different configurations for local and online versions of the site – for instance, setting the WP_DEBUG constant to true only in the development environment, or having different database settings. Alternatively to moving the entire file outside of the root, one could just move parts of it, or have just a single IS_LIVE constant declared.
So create two versions of website_config.php: one for the live site, and one for the local site, and tweak the contents accordingly. Then upload the version for the live site to the server via FTP (outside the website root).
Now we need to “link” the git repository we created earlier with the local development folder:

 cd /var/www/my-site.com/public_html  git init .
 git add . && git commit -m "Initial commit"  git remote add web my_user@my_server:/www/web/repos/my-site.git/
$ git push --set-upstream web master

This creates and initializes a git repository in the local website root, then adds a remote called web referencing our server’s git repository, and sets the local branch master up to track its remote counterpart.
Now we can easily work on our site, commit the changes we want, and push them to the website with a simple git push!

3. The WordPress database

This is a fairly simple process. Just enter phpMyAdmin on the live site, enter the correct database, and export it as an SQL file. Then, navigate to localhost/phpmyadmin and import the same file.
The only hiccup I’ve had is that some WordPress plugins produce content that seems to cause problems when importing (I’ve experienced this with PolyLang). The only solution I could find was to set up the database on the local side from scratch, and install the plugins separately. This does mean you won’t have the same content on local and live versions, though. Depending on the kind of site you’re running, this may or may not be a major problem.

4. Test!

My workflow when working on a website now looks like this:

  1. Edit /etc/hosts (requires root privileges – so use sudo!) and uncomment the line referencing the domain I want to work on.
  2. sudo service network-manager restart and navigate to the website. It is now the local version.
  3. Work on the website offline.
  4. git commit changes I want to apply to the website, then git push.
  5. Edit /etc/hosts again, then sudo service network-manager restart again and check the website works correctly!

Of course, steps 1, 2 and 5 could be automated with a simple script.