We’ve all been there at some point in our WordPress web development career. We need to upgrade a WP site to a newer version, and possibly restructure the information architecture. Lots of things go wrong at this point that make developers scratch their heads. I want to present to you several things you’ll face during a migration and help you through them.

Serialized Data

Simply doing a search and replace won’t fix your URL troubles when you move a database from one location to another one. You will eventually run into a serialization data problem at some point. What is serialized data you may be wondering? WordPress stores arrays and/or objects in various places in the database (often times in wp_options and wp_postmeta). When it does, it stores the length of the data. So, if you simply search and replace the string you’re looking for (ie., the domain name or what have you), you can change the data without changing it’s length, and run into problems where some theme configurations and other items don’t show up correctly. Here’s an example of what it would look like in a database setting:

a:2:{s:3:"url";s:18:"http://old.test";s:5:"label";s:4:"Home";}

If I blindly changed “http://old.test” from the above example, to another domain like “https://helloworld.com”, without changing the “s:18” part, I would end up having issues with the content. I would end up losing data or things just not working right on the frontend. So, we want to be careful moving data from one database to another one. One thing you should NOT do: do NOT run a raw SQL replace against a WordPress database unless you are completely certain the target data is not serialized.

What kind of plugins and CLI commands can help me move serialized data?

Great question! Better Search Replace is a good plugin that will be able to unserialize data before moving it into the database again. There’s also WP Migrate Lite / WP Migrate, Update URLs, etc. I would be careful with the following recommendation but Interconnect/it Search Replace DB script still works. You need to get rid of it after completing the database work you’re doing on the server.

Are there any CLI commands that can do this as well?

Why yes there is. I’m going to list several commands to use. They are as follows:

1) Dry run first

This is the safest first step:

wp search-replace 'http://oldsite.com' 'https://newsite.com' --dry-run

That shows what would change without actually writing to the database. WP-CLI documents --dry-run for previewing replacements.

2) Replace an old domain with a new one

wp search-replace 'http://oldsite.com' 'https://newsite.com'

This is the standard migration case and is safe for serialized values because the command handles serialization correctly.

3) Include all tables in the database

Useful when plugins store data in custom tables not registered with $wpdb:

wp search-replace 'http://oldsite.com' 'https://newsite.com' --all-tables

WP-CLI supports --all-tables when you need to go beyond the standard WordPress table list.

4) Only target specific tables

Useful when you want to be conservative:

wp search-replace 'http://oldsite.com' 'https://newsite.com' wp_options wp_postmeta wp_posts

WP-CLI lets you specify tables explicitly after the search and replacement strings.

5) Skip columns you do not want touched

Helpful if you want to avoid GUIDs or another sensitive column:

wp search-replace 'http://oldsite.com' 'https://newsite.com' --skip-columns=guid

--skip-columns is documented by WP-CLI and is commonly used when you want to avoid rewriting certain fields.

6) Export the transformed SQL instead of writing immediately

Great for review or CI-style workflows:

wp search-replace 'http://oldsite.com' 'https://newsite.com' --export=changed.sql

WP-CLI supports exporting transformed SQL rather than applying changes directly.

7) Precise mode for edge cases

WP-CLI documents --precise as forcing the PHP-based approach, which can be slower but may be useful in tricky cases:

wp search-replace 'http://oldsite.com' 'https://newsite.com' --precise

WP-CLI also notes that it can use a faster SQL approach when possible and fall back when serialized data is involved.

8) Multisite network replacement

If you are intentionally changing URLs across a multisite network:

wp search-replace 'http://oldsite.com' 'https://newsite.com' --network

WP-CLI documents --network for multisite operations.

Environment-Specific Config

This happens all the time. You move your files to another hosting provider but your configuration is for another database setup. Siteground for instance doesn’t have a great way of dealing with moving configs (wp-config.php) to different environments, so you have to use something like the vlucas/phpdotenv composer package, and .env files. I love that approach by the way. Pantheon on the other hand, you won’t have this exact problem for the most part. You’ll have to consult their documentation but moving files from one environment to the next is really great with the way they have that setup. I recommend Pantheon for larger websites, government ones, etc.

Email Delivery

Depending on the hosting change, you may encounter problems with emails being sent from your WordPress installation. The reason is because sometimes hosting providers have problems sending mail because they have a low reputation rating on their server setup, or they may not even have mail sending setup (new VPS solution, etc.). Ultimately, you can’t take that chance, you need something that will make sure your system emails arrive to your users. Transactional email services were created for this very problem. Instead of sending email from your server the email service provider sends it from theirs instead. That’s why I would suggest always using a transactional email service like Mailtrap, Brevo, Mailgun, Postmark, Mailersend, and many others. That way, you don’t have to worry about your current hosting setup because your site won’t be using it, it will be using trusted providers that make sure your email arrives perfectly.. most of the time – nothing is perfect. 😉

Build Artifacts

This isn’t a huuuuge problem, but I’ve experienced it before. Sometimes we’re using a fresh installation locally from a repository, and we’re trying to get things from one place to another, and we forget to run a build runner like Webpack, Laravel Mix, Vite, NPM, etc., and we miss vital build folders, and/or other files. You essentially have 2 options. Run the build on your local machine, add the build folders and what have you to the repository OR use a build runner script via a CI/CD pipeline. Personally, I prefer building on my machine and pushing to the repo, but with certain setups, it makes sense not to deal with merge conflicts and other headaches that come with pushing everything in source, especially when working with a team. That’s a problem you’re going to have to think about, and a decision you’re going to have to make.

Create a Staging Environment & Environment Pipeline

A typical, professional website with WordPress should look like the following setup:

Local → Dev → Staging → Production

It just so happens that Pantheon does this out of the box. I’ve never seen another company do that so well like they do it. WP Engine isn’t bad, and neither is Flywheel (now bought by WP Engine, I believe), but Pantheon has been a life saver in the way they force users to adopt this standard operating procedure. If you’re using Pantheon, they have a way of getting your files into your local machine. I typically use Laravel Valet (it’s the best – for real!) for my local development, but WP Engine/Flywheel has produced a great tool as well called Local. To each their own.

If you’re like me, and are trying to keep your costs down and are using a VPS solution like Digital Ocean, or are using a hosting solution like Siteground (previously mentioned), than there’s ways of doing this same setup but it’s more manually done than automatically baked into the process. I guess that’s why Pantheon and the rest are making the big bucks :).

Few of my closing thoughts

  • Actually read the documentation for WP migrations.
  • WordPress migrations aren’t just file transfers—they’re environment, infrastructure, and build pipeline transitions.
  • The 3 main parts of any WP migration are: Code (usually version controlled), Image & File Uploads (Media Library – wp-content/uploads/ folder), and the database.
  • Again, do NOT run a raw SQL replace against a WordPress database unless you are completely certain the target data is not serialized!!