Take the 2-minute tour ×

I'm not a Wordpress developer, but I'm trying to determine the optimal way for a team of folks to work with Wordpress. For a Rails project or most anything else, it's easy to work locally and deploy upstream, but my understanding is that Wordpress doesn't make this quite as easy. Maybe this is a myth?

From what I gather, it's not uncommon for URLs and file paths to be stored in the database which seems like it would make it difficult to deploy a WP project from dev --> stg --> prd (where each environment has it's own URL and possibly different file paths), much less for individual developers to have their own dev environment that would need to be "merged" into a unified copy for deployment.

I could configure all developer sandboxes to use a single database, but here again, if URLs and file paths are stored then nothing is gained.

There are a series of smaller questions here, but the more I think about those, the more I realize that what I'm really asking for is advice about how to structure things for optimal development of a WordPress site that will be hacked on by a team of developers. I'd prefer the sandboxed approach we use for other projects, but I have no idea if/how things can be unified once all development is complete.

Li'l help here?


share|improve this question
add comment

4 Answers

It easy we build on a dev server and move to live server with this sql query:

UPDATE wp_posts SET guid = REPLACE(guid, 'devserver.com', 'liveserver.com');
UPDATE wp_posts SET post_content = REPLACE(post_content, 'devserver.com', 'liveserver.com');
UPDATE wp_options SET option_value = REPLACE(option_value, 'devserver.com', 'liveserver.com');
share|improve this answer
So that would lead me to believe that only the base URL and/or TLD is ever stored in the database. Is that safe to say? Is that a WP convention that is generally followed in your experience? –  Rob Wilkerson Oct 18 '12 at 14:11
When you call a function like bloginfo('template_directory'); or site_url(); within Wordpress it references whatever is saved as the site_url within the database table wp_options. So that includes page & post links. Unfortunately everything added using the editor within the admin panel saves a hardcoded link (I think performance is the reason for this) but its easy enough to change with SQL find and replace. –  dciso Oct 18 '12 at 22:02
It's a kludge to have to run SQL queries like that every time you promote to production. WP comes with enough hooks in the admin area to include the type of functionality you need to circumvent the issue. Have a look: URL Token Replacement. Also look at the related SO post in the sidebar, where the OP developed a solution to deal with multisite configurations. –  user916011 Oct 19 '12 at 19:30
FYI, WordPress stores data in serialized PHP arrays in the wp_options table. A MySQL REPLACE() will break all those serialized arrays and your site. Check out interconnectit.com/products/… which unserializes, searches/replaces, and rebuilds the serialized arrays. But doesn't help the OP :( –  James Jun 5 '13 at 22:44
add comment

Last year I wrote a bash script to mirror a live MU installation into a sandbox. It's not perfect and not ideal, but a good starting point. It consists of mirroring the databases, files and rewriting the mirrored database to reflect the sandbox.

See http://pp19dd.com/2011/01/bash-script-to-mirror-wordpress-mu-installation-into-a-sandbox/

It's important for developers to be able to take live and exact content snapshots to replicate conditions.

share|improve this answer
So what happens if a developer adds a plugin that modifies their sandbox database? Or do you use a unified database? –  Rob Wilkerson Oct 18 '12 at 14:09
This is a copy utility, but WP-aware. So, whatever's on the main live site gets mirrored exactly. Idea is to reduce the gap between theoretical conditions and reality. –  pp19dd Oct 18 '12 at 19:00
add comment

Warning: incoming wall of text..

@Rob, WP is hell when it comes to working in teams; however, with a little work (and some symlink magic) you can set up your WP projects so that your working files for your themes or plugins can reside separately from the WP core. Some of this uses WP's built in mechanisms, some of it is related to SVN externals (hint). I'll let you google that since it's outside the scope of your question.

A note on WP GUIDs

WARNING: DO NOT replace guids. WP GUIDs are there for external feed readers. Feed readers use the GUID to determine if the content is recent. Changing it basically tells those readers that every entry in the feed is new (especially for posts.) That introduces a lot of extra overhead for legacy content that you just don't need. GUID are a legacy feature that should have been changed a long time ago to UUIDs. Technically, you can use anything int he guid field, but WP uses the permalink to populate that field -- legacy.

The only time it is ever acceptable to change the GUID is for new wp projects where content is brand-spankin' new.

To answer your question:

WP stores explicit references to the current domain in a dozen places in it's DB. These locations are a pain to track down and change, and the last thing you want to do is deal with manual edits to a *.sql dump file that you're going to import into production. It just smacks of bad development practices.

There's a couple ways to get around this, but it means a little bit of work if you're already further down your development lifecycle. I'll address the first case.

Case 1: Project Onset

When you're starting the project, you'll likely have a development sandbox and DB ready. You'll likely have WP already installed by now, so it's essentially clean for all intents and purposes.

The first thing you're going to want to do is change how your config file works. Most folks keep with the standard wp-config.php file (beyond a team production project, there's not really any reason to edit it.) However, you can set it up with some logic to include developer-specific or environment-specific config files. For example:


switch( $current_environment )
 case 'jack.local'  : include( 'wp-config-jack.php' )  break; // Jack's sandbox
 case 'jill.local'  : include( 'wp-config-jill.php' ) break; // Jill's sandbox
 default : ...  break; // Staging & Production

The next thing you're going to want to do is include the normal contents of the wp-config.php file in a wp-config-remote.php file for use on staging/production. Next, edit your wp-config-remote.php file so that you can use 1 config file across multiple environments (staging,production). An if(...) or switch(...) block is all you need, e.g.

if( (strpos( $_SERVER[ "HTTP_HOST" ], "localhost" ) !== false) || (strpos( $_SERVER[ "HTTP_HOST" ], "local" ) !== false) )

(There are better ways to write that condition... this is just a crude example.)

Configure all of your WP settings specific to each of your remote environments. Hopefully you'll be checking this into a source control repository.

That basically frees you up to let your team have config settings specific to their environment, while letting you check in settings for each of the remote environments once.

The second thing you're going to want to do is build a mechanism to intercept and filter domain-specific links. The intent behind this mechanism is to replace any references to the current domain with a token/placeholder. I've outline the technique to do this here: http://www.farfromfearless.com/2010/09/07/url-token-replacement-techniques-for-wordpress-3-0/

It basically amounts to creating a filter that acts on the content before it's submitted to the DB and before the content is rendered to the page. The technique is transparent in that it won't affect normal editing practices. You can still create your content in the editor, reference other pages, posts, images, etc. and they'll show up just fine while editing in different environments.

In recent projects, I've wrapped all of this and a few other WP "normalization" features into a single bootstrap plugin that I set & forget.

Case 2: Project Ongoing

Now, in your case, you're further along in your development lifecycle. It's going to take some work to replace those domain references, but if you follow the steps I've outline above you should only ever have to do this once. The link I supplied above gives you the SQL you'll need to do that job. It's important to note that in a multi-site environment, you'll need to do this for every "sub-site" you've created.

Once you've updated your DB, I suggest implementing the steps in CASE 1 so you don't have to repeat the steps again.

Bonus: synchronizing content

Synchronizing content is a pain. What I've done in recent projects is had clients work on the staging server and promote changes upstream to production. So then, that leaves you with synchronizing downstream to your sandbox(es). Write a shell script that dumps a copy of SPECIFIC content tables from your staging DB, and imports them into your sandbox DB (effectively replacing content tables.) You should be able to see the benefit of the domain-token-replacement technique.

Images that aren't checked into source control, e.g. client images should be pushed to a common location, e.g. an S3 Bucket. There are WP plugins that can help you with that. That'll save a lot of time having to synchronize assets across environments.

I hope this helps you out -- if not, there's always SilverStripe ;)

share|improve this answer
Just a note on the config files for your individual sandboxes: exclude them from source control (I use SVN), e.g. svn propset:svn-ignore wp-config-jill.php . –  user916011 Oct 18 '12 at 15:55
Another SO user with a similar problem came up with a very nice solution to handle downstream mirroring of wp_options tables based on the domain token replacement technique mentioned here. I'm adding the link to his solution for completeness: link –  user916011 Oct 18 '12 at 18:14
add comment

I just ran into this issue myself launching a new website. My solution was to use Vagrant. Vagrant is also platform agnostic so you could be developing on a Mac and a teammate is using Windows. Same Vagrant project runs on both.

I wrote a guide on how to set Vagrant up with Wordpress from a production environment running locally on your machine. I don't use Wordpress that often but every time I do its always a hassle to setup Apache and PHP on my Mac, then make sure all the Wordpress site urls are updated in the database.

Once you've configured your Vagrant project, its a single command for any developer on your team to be up and running with a local instance of Wordpress. In short, Vagrant will mount your project directory from your host machine in the guest machine and run Apache, MySQL, PHP through the guest machine. You still use your host machine's IDE (as you normally would) and your host machines browser. There is no uploading of files anywhere, its just code, save, refresh browser all on your local machine.

http://www.distilnetworks.com/wordpress-development-with-vagrant/ <- Explains how to set it all up with Vagrant

https://gist.github.com/markmalek/fd2e6e65385400d9cd47 <- the shell script when provisioning Vagrant

The shell script could probably be a lot better, this is what worked for me but I would love to hear some better suggestions or ideas. I'm new to Vagrant and now use it for some of our other projects so thought it would be a good fit here as well.

I do update the GUID in the shell script, which I've read in another answer that you shouldn't do because feed readers use this. In this case its irrelevant since its just for your local instance of Wordpress but I wouldn't make this change in production. See this answer for better explanation.

share|improve this answer
add comment

Your Answer


