Migrating a WordPress Site When All Options Fail

Moving a WordPress site from one server to another is a fairly straightforward process. Start by getting a copy of the WordPress files, specifically the wp-content folder, and a fresh database export. Import the files and database to the new server and then configure your domain with the new web server IP. Deploy fresh SSL. Simple right?

Well, generally it is. Often you can generate a full site backup using a service like ManageWP or a plugin like BackupBuddy. There have been a few times where I’ve needed to migrate a site and the existing web server was in very poor shape. In some cases it was so bad that generating a full site backup was not possible. Here are a few tips and tricks I used in those rare situations.

Overcoming the WordPress upload limit.

Sometimes you won’t be able to install a backup plugin due to a PHP file upload limit. A plugin like BackupBuddy is over 8MBs. This limit can typically be worked around by installing and using File Manager. Upload the plugin zip with File Manager and then extract the contents of the zip to the wp-content/plugins/ directory. Then activate the plugin. This trick allows you to install WordPress plugins that are larger than the PHP upload limitations as File Manager uses a Javascript based uploader.

Similarly you can use ManageWP to install larger then allowed plugins. First install ManageWP to WordPress and then connect to managewp.com. From ManageWP’s interface you can drag and drop a plugin zip. ManageWP will take care of transferring the files and getting the plugin installed.

When ManageWP and BackupBuddy fail at generating a full site backup.

If a full site backup isn’t possible then try backing up the website in batches. Typically excluding the uploads directory will do the trick allowing you to backup everything else. If generating a full database backup isn’t possible, use the same trick. Figure out which table is the largest and exclude it from backup. This can take a bit of trial and error. Another good alternative for a troublesome databases is WP Migrate DB Pro. That works amazing well on really slow web servers.

Manually fetching uploads using the command line.

So you’ve made it this far but what about the uploads? Files in the wp-content/uploads/ directory are typically publicly accessible. That means we can just manually retrieve the files, no need to package them up in a backup. This is ideal for sites with really large upload directories. Even better when you can have the new web server fetch the files directly from the old one.

Start by gather a list of files via ManageWP’s code snippets.

<?php

$upload_dir = wp_upload_dir();
$uploads    = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $upload_dir['basedir'] ) );
$files      = [];

foreach ( $uploads as $file ) {
    if ( $file->isDir() ){ 
        continue;
    }
    $files[] = $file->getPathname(); 
}

foreach ( $files as $file ) {
    $file = str_replace( $upload_dir['basedir'], "", $file );
    $file = ltrim( $file, '/' );
    echo "$file\n";
}

Running this code snippet will produce a list of files in the upload directory. These results should be copied over to a files.txt which will be used to retrieve them.

Getting a list of upload files using ManageWP’s code snippets.

Then use rclone to download these files with the following one-liner. Be sure to replace domain-name.tld with the domain of the site you are downloading from and the destination path ~/public/wp-content/uploads/ to wherever you’d like them to go.

while read f; do
    echo "$f" | rclone copy --files-from - --http-url https://domain-name.tld :http:wp-content/uploads ~/public/wp-content/uploads/ --no-traverse --progress
done < files.txt

In theory the following one-liner will do the same thing a bit more efficiently, however in testing I wasn’t able to get it working consistently with large lists of files. Might just be a bug with Rclone.

rclone copy --files-from files.txt --http-url https://domain-name.tld :http:wp-content/uploads ~/public/wp-content/uploads/ --no-traverse --progress