198

I do development on one box and use a second for production. Right now I just dump the database and then do a find a replace for the URL changes; then copy over the files and import the new SQL.

Are there better ways of doing this?

27 Answers 27

121

@Insanity5902: Deployment of a WordPress site from one box to another has been a PITA since day one I started working with WordPress. (Truth-be-told it was a PITA with Drupal for 2 years before I started with WordPress so the problem is certainly not exclusively with WordPress.)

It bothered me that every time I needed to move a site I'd have to spend so much often duplicated effort and it kept me from deploying to test as frequently as I would have preferred. So about 4-6 months ago I started working on a plugin to solve the webhost migration problem and I mentioned my ideas on the WP Tavern forum.

Well fast forward to today and I've pretty much got it working and I'm conveniently calling it "WP Migrate Webhosts." Even though the plugin is still very much beta (probably even alpha) given your question I think I'm ready to let people start banging on it.

The envisioned use-case is that:

  1. first the developer handles uploading all the changed theme and plugin files via FTP,
  2. then uploads the development MySQL database to the testing server in its entirety and finally
  3. then runs the plugin to migrate any references from the prior domain to the new one. (My plugin does not attempt to solve the merging of new database fields or tables with live data; THAT is a much bigger problem that I'm not sure how to solve.)

You can download the plugin from my website and unzip into your plugins directory (if you don't know how to do this then this plugin is not for you because it requires someone who knows what they are doing to use it.) I'll keep this plugin online until I release it to WordPress.org after which you should look for it there.

To use it you take a different approach in your wp-config.php that normal by commenting out the four (4) defines DB_NAME, DB_USER, DB_PASSWORD and DB_HOST and instead registering the defaults for webhosts and then registering info about each webhost itself. Here's what that segment of wp-config.php might look like (note the first section is the unneeded code commented out and also note that I set up my hosts file on my local machine with non-routable .dev top level domains to make day-to-day development easier. On the Mac VirtualHostX makes this a breeze):

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
//define('DB_NAME', 'wp30');

/** MySQL database username */
//define('DB_USER', 'wp30_anon');

/** MySQL database password */
//define('DB_PASSWORD', '12345');

/** MySQL hostname */
//define('DB_HOST', '127.0.0.1:3306');

require_once(ABSPATH . 'wp-content/plugins/wp-migrate-webhosts/wp-webhosts.php');
register_webhost_defaults(array(
 'database'  => 'example_db',
 'user'      => 'example_user',
 'password'  => '12345',
 'host'      => 'localhost',
 'sitepath'  => '',        // '' if WordPress is installed in the root
));
register_webhost('dev',array(
 'name'      => 'Example Local Development',
 'host'      => '127.0.0.1:3306',
 'domain'    => 'example.dev',
 'rootdir'   => '/Users/mikeschinkel/Sites/example/trunk',
));
register_webhost('test',array(
 'name'      => 'Example Test Server',
 'rootdir'   => '/home/example/public_html/test',
 'domain'    => 'test.example.com',
));
register_webhost('stage',array(
 'name'      => 'Example Staging Server',
 'rootdir'   => '/home/example/public_html/stage',
 'domain'    => 'stage.example.com',
));
register_webhost('live',array(
 'name'      => 'Example Live Site',
 'rootdir'   => '/home/example/public_html/',
 'password'  => '%asd59kar12*fr',
 'domain'    => 'www.example.com',
));
require_once(ABSPATH . 'wp-content/plugins/wp-migrate-webhosts/set-webhost.php');

Hopefully this is (mostly) self explanatory. I attempted to make the code as clean as I could but unfortunately it requires those two cryptic require_once() lines before and after the block of webhost registration code since there was no way for me to "hook" WordPress before wp-config.php is called.

Once you have updated your wp-config.php then you can simply use the URL shortcut wp-migrate-webhosts to go to the admin screen like so:

http://example.com/wp-migrate-webhosts

The above will take you to an admin screen like the following which has a fair bit of description text and allows you to migrate FROM any of the other webhost domains with a single click after selecting the domains to migrate from (NOTE: this example shows going DOWN from test/stage/live servers to local development but rest assured it can migrate TO any domain where it happens to be located. This also means the plugin will be great for taking an existing live site and quickly getting a local development environment working!):

enter image description here

If it's not clear "migration" in this context means to update all the references in the current database to be appropriate for the currently defined webhost (and "current" is sniffed by inspecting $_SERVER['SERVER_NAME'].)

What's cool about the plugin is that it implements some basic migrations but anyone can hook it and perform their own migrations. For example, if you add a gallery plugin that stored full paths to images in the database you could hook the migrate_webhosts action which will be passed the "from" webhost and the "to" webhost each as an array of metadata and you'll be allowed to perform whatever you need to do in the database using SQL or any applicable WordPress API functions to do the migration. Yes any of us could do this without the plugin but without the plugin I found that writing all the code needed was more effort than it was worth. With the plugin it's just easier to write these tiny hooks and get it over with.

You may also find my migrations fail in edge cases I've not tested and maybe you can help me improve the plugin? Anyone who wants to can email me via my gmail account (my alias is "mikeschinkel.")

Also, the plugin was designed to accept user-define webhost metadata in addition to the ones it recognizes like database, user, password, host, domain etc. A perfect example might be googlemaps_apikey where you can store a the different API keys for each domain that your Google Map's plugin needs to operate correct (who among you who has used a Google Maps plugin hasn't deployed an app to a live server and forgotten to change the code to the correct API key? Come on, be honest... :) With this plugin, a googlemaps_apikey element in your register_webhost() array and a small custom migrate_webhosts hook you can effectively eliminate that as a concern!

Well that's about it. I'm launching this plugin here on WordPress Answer's Exchange because @Insanity5902's question triggered it. Let me know if it's helpful, here if appropriate or via email if not.

P.S. If you do decide to use this remember it is alpha/beta and that means it will change so be prepared for some minor surgery if you want to use it now and then use the released version once its been beaten on by many hands.

P.P.S. What are my goals with this? I've love to see this migrate into WordPress core so that everybody would have access to it. But before that can even be considered lots of people have to be interested in using it to ensure it actually solves more problems then it potentially could create. So if you like the idea then by all means use it and help me gain momentum with it for eventual hopeful inclusion into WordPress core.

  • Nice solutions I've got a couple of questions though 1) Do you still need to define the WP_SITEURL to get into the admin area? 2) Does the tool on display for only Administrator Users? (not sure if the Tool section displays for non-admin's) – Ryan Gibbons Aug 12 '10 at 15:14
  • Hi @Insanity5902: 1) No need to set WP_SITEURL, the plugin does for you. You actually are setting it when you "register" a "domain" and "sitepath" for a webhost. In normal WordPress operation WP_SITEURL is required to be set either in code or the database to ensure nobody spoofs the URL and does nefarious things because on an unexpected value in $_SERVER['SERVER_NAME']. The WP Migrate Websites plugin sets WP_SITEURL indirectly based on $_SERVER['SERVER_NAME'] but it will ONLY do so if the current domain matches one of the domains you've defined in your wp-config.php file, nothing else. – MikeSchinkel Aug 12 '10 at 16:32
  • 2.) The URL shortcut I mentioned actually does a redirect into the admin console so it is only for people logged into the admin. I don't have specific checks for administrator only built in yet though. I've never added capabilities to a plugin but will be needing to fully research how in the next few weeks so can work on that over the next month. However the plugin is not destructive; it can ONLY migrate to the current domain and the process is repeatable so even if a non-admin got in there's really no harm they could do with it, at least not that I can envision. – MikeSchinkel Aug 12 '10 at 16:32
  • 1
    /wp-migrate-webhosts produces a 404, and /wp-admin produces 'error establishing a database connection' – Steve Oct 11 '12 at 1:37
  • 4
    So, what's the state of affairs for this plugin? It looks mighty appealing but I would like something mature and well-vetted. This post is the only information I can find on it. – Kevin C. Nov 1 '12 at 18:27
35

When possible, I set WP_HOME and WP_SITEURL in wp-config.php. This, combined with a database dump and import, is the most simple of all solutions I'm familiar with.

http://codex.wordpress.org/Changing_The_Site_URL#Edit_wp-config.php

  • 1
    When would it not be possible to set these? This sounds slightly simpler than changing things in the database. – jfklein Oct 20 '11 at 14:10
  • 2
    @jfklein I'm almost always working with a WordPress Network, which is incompatible with these constants. – Annika Backstrom Oct 20 '11 at 17:44
  • 1
    Doing the same. Regrettably, not all themes honor this. i.e. 'Repsonsive Theme' from ThemeID. Searching the dump/all tables for 'localhost' (or whatever you chosen local name), especially wp_options and making search&replaces is often unavoidable. – Frank Nocke May 26 '13 at 8:42
  • @FranKee I've create a plugin wordpress.org/plugins/pitta-migration that uses the constants to update the wp_options table which should cover most themes and plugins – icc97 Aug 21 '15 at 10:39
  • @icc97: Lovely. Will look at it. PS: Nice header image, pictures the situation. – Frank Nocke Aug 21 '15 at 12:05
27

My favourite hack; add a setting to your /etc/hosts to make the production domain point to your development box, just on your machine. To deploy to production you rsync all the files and push the database over.

The risks of this strategy are clear; you might confuse your development environment with your production environment.

It still an easy fix though.

  • 5
    Yes! I'm so glad I wasn't the only person to have thought of that! any difference between dev and prod is bad. Removing that difference altogether is far better than trying to work around it. And this setup takes no work at all. One can even do testing on a virtual machine with modded hosts file if needed. – Alexander Bird Nov 21 '11 at 19:19
  • 2
    I like this method a lot, but how do you handle the database push/pull? – Nenotlep Apr 26 '15 at 20:41
  • As for the risk of confusing dev with prod, I use a Chrome plugin that displays the IP address of the webpage. You'll know you're on local when its 127.0.0.1 – kosinix May 12 '16 at 0:12
9

I wanted something similar when I migrated to WP a few months back, so I wrote a pretty simple shell script that uses rsync and mysqldump over ssh:

http://snarfed.org/sync_wordpress

It's not sophisticated or web based, but I'm happy with it.

8

WP Engine is a new service that offers "One-Click Staging":

WPEngine has an exclusive feature called “staging.” Here’s how it works: Before you make a scary change to your blog, click the “snapshot” button. We make a complete copy of your blog and set it up in a separate, safe area. You can play with anything you want; nothing’s live. Only when you’re ready to make it live do you touch your main site.

Looks like a very easy way to quickly move from development to production, especially with an already live site.

  • 3
    That's a very nice option indeed and will be great for many people! That of course doesn't work for embedded URLs nor does it help for people who develop locally so they can use an IDE with a debugger. Now if WPEngine can create an interaction that merges a local deploy too then it will really be something (Technosailor, you listening?) – MikeSchinkel Aug 12 '10 at 16:35
  • Agreed, that would be a fantastic addition. – Travis Northcutt Aug 20 '10 at 11:55
  • The snapshot feature copies only from production to staging, not the other way around. It's great for testing changes, but it won't help for deploying to production. – sam May 2 '13 at 20:44
  • 2
    @sam actually, they just recently started rolling out the ability to copy from staging to production. wpengine.com/2013/04/user-portal-v2-and-staging-to-production – Travis Northcutt May 2 '13 at 20:56
7

Duplicator Plugin: Here is a plugin that I have been working on. It's currently in beta but it gets the job done for most sites. Right now it is targeted at smaller WordPress installs. http://wordpress.org/extend/plugins/duplicator/

Resources: Additional resources for the plugin can be found here: http://lifeinthegrid.com/duplicator/

Community: Please let us know about your successes or any issues you might run into! In an effort to more easily manage the various threads please post issues to the WordPress.org plugin forums. Please do not post any logging data from the plugin into the online forums. Logging data can be submitted to our support site.

6

You might take a look at a product from iThemes, called BackUpBuddy. I've only used it twice, each time had a hitch or two, but overall it looks promising.

5

I am personally addressing this issue with my project on Github, called Autopress. I don't have a perfect solution yet, but I'm getting closer, especially with the wpstage plugin from the wpengine folks.

  • Just checked out your script. Nice. If I understood it installs a fresh WP on the server. The question here is how to migrate from development to production. Can it help with that? – Sruly Aug 11 '10 at 23:18
  • 17
    Is this it? github.com/vluther/Autopress I suggest creating links in your answers so people can click right through! – artlung Aug 12 '10 at 0:42
  • 4
    @Mike Lee: Yes, you can up-vote comments. See, I upvoted artlung's comment. Look for the uparrow on hover to the left of the comment. – MikeSchinkel Aug 12 '10 at 4:06
  • I'm working on a way to keep everything in version control, and then pushing from dev to production. I've been able to do it without any issues for a few sites already, but there are still some tweaks that I need to address. – Vid Luther Aug 14 '10 at 13:51
  • 1
    Not sure if this is it, but there is a WP Engine plugin which is used for site migration across hosts. It's called Snapshot (Direct link). – joelhaus Mar 13 '11 at 20:23
5

This looks promising. We are working on some scripts to handle migrating some of the data, wp-options for example, changing paths in the db, a copying over media.

The issue I have is that the live site continues to grow while the other is in development. One site we work on has 20 posts a day and over 3,000 comments per day. That is too much data to move over with phpmyadmin or via the command line. Also, moving the data around always causes UTF issues for some reason.

Also, now that it looks like menu options are stored in the DB, I have even more to deal with.

I check all my code into SVN and deploy the code via FTP from the server (Beanstalk). This does not make the changes to the DB for me though or activate new plugins.

My plan right now is to create a manifest file while I am developing to do all my changes to the live site.

For example the file would have human readable lines

It would include plugins to activate, wp-options to move over, images to move, pages to move. Then my plugin, would detect the manifest file and make all the changes to the staging site.

Once I tested that and was sure I got everything, I could be sure it would work on production.

This plugin is still just an idea, but I have some code written for it.

Also, if you want to make changes to just the URL in you DB, you can use the following SQL.

just replace $old$ with the old domain and $new$ with the new

update wp_postmeta set meta_value = replace(meta_value, '$old$' , '$new$') ;
update wp_posts set post_content = replace(post_content, '$old$' , '$new$') ;
update wp_options set option_value = replace(option_value, '$old$' , '$new$') ;
  • 2
    Just a note, my sql call may break you serialized data. s:14:blogs.prod.com has the length encoded as 14. After running the code, we now have s:14:dev.prod.com which is corrupt. Should be s:12:dev.prod.com use with caution. – Andrew Aug 25 '10 at 0:04
4

Two Google Summer of Code projects that have a similar goal:

  • It doesn't look like either of these projects got off the ground? – icc97 Oct 21 '14 at 14:02
3

I use subversion's export command to install the WordPress files (http://core.svn.wordpress.org/tags//) as well as all plugins in the repository (http://plugins.svn.wordpress.org//tags//), then just zip the theme and custom plugins and install them normally. Once all of that is up and running without content, I export the test DB and do a search/replace for the URL AND the filepath (stored for media) and import into an empty database, then just switch the database info in wp-config.php. Generally takes me about 10 - 20 minutes.

3

Normally I login to phpMyadmin upload the database and edit the contents of wp_options>siteurl and wp_options>home to the expected domain. If you need to update URLs within your posts and pages content you can do a search/replace for the URL and the media/uploads path on the .SQL file prior to uploading. It's a quick job.

3

While there's no shortage of good solutions here, in the spirit of sharing I thought I would add my bash deploy script to the pile: https://github.com/jplew/SyncDB

SyncDB is bash deploy script meant to take the tedium out of synchronizing local and remote versions of a Wordpress site. It allows developers working in a local environment (eg. MAMP) to rapidly "push" or "pull" changes to or from their production server with a single terminal command.

This script works well with Mark Jaquith's WP-Skeleton, and harnesses mysqldump, git and rsync to synchronize your entire site—database, code, and media—in two easy steps:

./syncdb
git push hub master
3

I've been using http://wordpress.org/plugins/wp-clone-by-wp-academy/. It works nicely!

Just 3 steps:

  1. Install the plugin on both sites.
  2. Use the plugin to generate a backup on the old site.
  3. Take the backup URL it gives you and plug it into the plugin page on the new site, hit go, and your migration is complete in just a few seconds!

It adjusts all the URLs automatically - including serialized string replacements - so no risk of losing widget configurations, etc.

The only issues I've had are with some websites with larger databases (~300MB), which caused PHP script execution timeouts during import of the site backup.

3

As of 2017 here are the two best ways I've found to handle the transfer of a WordPress database from development to production.

WP Migrate DB Pro / WP Sync DB

https://wordpress.org/plugins/wp-migrate-db/

These WordPress plugins let you push, pull, and sync database tables between WordPress installations. This is much better than a find/replace for many reasons because it:

  • Exports your database as a MySQL data dump (much like phpMyAdmin)
  • Does a find and replace on URLs and file paths
  • Handles serialized data
  • Allows you to save it to your computer as an SQL file

I'm a fan of being paid for the work I do, so I recommend you support Mr. Brad Touesnard and buy a licenses copy of the real thing. WP Sync DB is a replicate and it's always behind in support as a result. With this plugin the process is dead simple:

  1. Install/activate the plugin on your localhost and production environment
  2. Configure a push transfer from your localhost/development server to your production
  3. Fill in rules for which tables to transfer, and define find and replace rules to perform
  4. That's it!

Database Search & Replace for WordPress Databases by InterconnectIT

https://interconnectit.com/products/search-and-replace-for-wordpress-databases/

This free tool is not a plugin, but is installed in your root directory of your WordPress production install. This isn't as good as WP Migrate DB Pro because it requires a few manual steps, but nonetheless it's a great option that consistently works. When using this approach the process looks like this:

  1. Backup your local database, this is absolutely needed as we'll be re-importing it soon
  2. Add the script to a folder in your installs root directory
  3. Run the find and replace on your database
  4. Export your database and save it for your production environment
  5. Re-import your backup from step #1 to restore your localhost
  6. Connect to your production database and back it up (as you always should before doing these things)
  7. Import the export we made AFTER running the find/replace routine from step #4

You can use a faster approach, but it involves downtime for your production site which in my opinion is unacceptable. That's why we call it production, right?

1

since I run my sites in IIS (I also run asp.net, so I need windows) I use WebPI from Msft to install a new instance, then I copy the template and use the import/export to transfer the data.

It's not perfect but the whole thing takes less than an hour.

Obviously it would be nice to have a one-click solution, but this is what I found to be easiest for me.

1

Another paying solution: the Xtreme One theme framework released version 1.2 with Xtreme Backup which allows you to "export or import the settings of your Childthemes, Layouts or Widgets with all their settings/content as XML file."

1

A coworker found this. Interesting concept, though it doesn't work cross server it looks like. I'm still exploring it, but looks like it could work great for a staging instance

http://code.google.com/p/deploymint/

  • Four months ago, I couldn't make this plugin work... And it's still at version 0.1 in code.google – brasofilo May 4 '12 at 14:46
1

This may not have been around when you asked the question, but I've been using a service called Blogvault for a couple months and it has done this flawlessly. I've probably done over 50 migrations (crossing domains, sub-domains, and web hosts), not a hitch and takes no time at all.

It's a paid service (per domain/month), but not that much.

1

RAMP is a new content deployment plugin from Crowd Favorite, and it looks really slick. It's $250, though, so I haven't tried it out yet. Might just pay for itself in the amount of time saved, though, so I'm considering it.

The big benefit that it has over most of the other methods mentioned, is that it can intelligently merge posts, comments, etc. It's not just importing a mysqldump, it's more like source control for the database. For example, when deploying a post, it'll also deploy the tags for that post, if they don't already exist in production.

  • RAMP is for content deployment, as opposed to code deployment, but I agree, it looks superb. They now have a demo of RAMP set up so you can try out the features. – Emzo Nov 19 '12 at 14:17
  • The question was about content deployement, not code deployment, and I started my answer off by saying "RAMP is a new content deployment plugin..." – Ian Dunn Nov 19 '12 at 16:33
1

Let me give away one of my favourites :-)

// proven local<->live codefork (covers local network testing, i.e. from mobile devices):
$GLOBALS['is_local'] =  
    in_array( $_SERVER['REMOTE_ADDR'], array("127.0.0.1","::1")) || // simple localhost (IPv4 IPv6)
              $_SERVER['HTTP_HOST'] == 'local.workblog'          || // call by local name (adjust)
       substr($_SERVER["REMOTE_ADDR"],0,8) == '192.168.';           // (mobile) device in local network

$table_prefix  = NULL; // ensure scope

if ( $GLOBALS['is_local'] )  // LOCAL fork ------------------------
{
        ....
}
else  // STAGE/LIVE fork -------------------
{

...and then you work your way from there. DB_NAME, DB_USER... table_prefix. Personally I switch on ALTERNATE_WP_CRON on local (to avoid some annoying warnings), WP_DEBUG of on both (if you're not a developer) or on live-only (if you are), another ini_set('display_errors', '0'); for live could also do good, ant lastly, as mentioned above: WP_HOME and WP_SITEURL to the respective local/actual url.

That pretty much all, nothing left above the classic WordPress 'That's all, stop editing!' line...

The 192.168. part allows you to do some local testing (i.e. from pads or phones) within your local network)

The $GLOBALS['is_local'] can come in handy in your theme development, too, for some extra debug output, etc...

1

I have been using the backupbuddy plugin for a while now. It lets you make a backup of database and all files, download it as a zip or send it directly to another server via FTP. It also does the URL find and replace for you. It usually takes me about 5 minutes to go through the entire process. And because all files are zipped the uploading/downloading process is much faster. And no, I do not work for them, but this plugin has really made this whole process much easier.

1

Another useful tool for handling server migrations for sites is the WordPress CLI, this article has a good overview of what it can do but specifically the section for "Search and Replace" is useful for finding all of the references to the old / dev site url:

Advanced WordPress Management With WP-CLI

1

This is the easiest way ever: https://themes.artbees.net/docs/website-migration/
It only takes two clicks. One to export, one to import.

It's possible by using the All in one WP Migration plugin. Above link shows how to use it.

0

If you are trying to achieve continuous synchronization, I suggest using rsync along with a custom cron job to rewrite any url or site-specific data.

0

After having followed this answer for a while I've created my own small plugin - Pitta Migration. The reasons being:

  1. Of all the ideas tried here - the simplest is the WP_HOME and WP_SITEURL options
  2. I then use these to set the two matching wp_options URLs - which covers when plugins /themes ignore these
  3. This gives me 100% confidence in what is being changed in my database
  4. This also works cross-platform (all those bash scripts don't play nicely on Windows)
  5. It's easy to understand what the plugin is doing
  6. There's no config beyond the two constants - do a mysqldump and a mysql import to your local database and the plugin sees that the constant and the table differ and updates them to match
  7. No text search and replace
  8. No chance of knackering your database - I use the WordPress Database Object to do two updates and nothing more
  9. It plays nicely with things like WordPress Skeleton where you can have everything in source control and set a local config
  10. I've put it in the WordPress plugins directory and on Github so that it's free, fully opensource, easy for you to fork and easy to install
  11. Once it's installed you can forget about it and it should 'just work' - it gives you a little notice to say that the database has been modified
  12. It should work with any backup / FTP / restore process
0

In my opinion the easiest way I follow is the manual transfer.. Just copy wp-content folder and wp-config.php file to the new host. Export the database from old host and import it in a new database of new host..

In the new host database go to wp-option table and there change the site URL and Blog URL to New host address from old host. like from http://localhost/wp to http://example.com

Now in the wp-config file just change the information of database and user with new host info.

Now login to new wp-admin and go to settings and save the permalink.

You are done. I think this is simple without using any plugins.

I have tried different kind of plugins and all of these have many kinds of problems..

So I prefer this simple manual transfer which is easier I think.

protected by fuxia Jun 1 '12 at 0:36

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.