WP Manifestindependent plugin directory
manifest / updates / wp-migrator-app

WP-CLI migrate_app

Imports an uploaded WordPress package (Duplicator or plain) into this installation, rewrites URLs, and merges themes/plugins/uploads.

by dgaitan · github.com/dgaitan/wp-migrator-app

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/dgaitan/wp-migrator-app/archive/refs/heads/main.zip

Readme

wp migrate_app

Move a WordPress site into an existing, working WordPress installation — rewriting URLs safely and merging the source's themes, plugins and uploads into the site that is already there.

Everything reduces to two steps.

# 1. Get the site into a folder.
wp migrate_app_pull ./old-site --from=@old      # ...from a server you can SSH to
                                                 # ...or just unzip a Duplicator package

# 2. Install that folder.
wp migrate_app ./old-site                        # ...into WordPress on this machine
wp migrate_app_remote ./old-site --to=@new       # ...into WordPress on another server

A folder is the only thing the two steps share. Step one knows nothing about installing; step two knows nothing about where the folder came from. That is what makes the combinations free: server-to-server migration is step one against A, then step two against B. There is no third command for it, and there does not need to be one.

Step 1 — get the site into a folder

wp migrate_app_pull <folder> --from=<origin> You have SSH to the source server. It brings the database and wp-content home and writes the manifest for you.
Unzip a Duplicator package You have no access to the source at all — only the export. Extract it and run --generate-config. See Preparing the package. On a machine with no WordPress of its own, that is wp migrate_app_remote <folder> --generate-config.
Unzip a Fiction Drafts export Same, for a site running that plugin. Its manifest.json is read directly. See Fiction Drafts exports.

All three produce the same thing: a folder with a migration.yaml, a .sql dump, and wp-content/.

Step 2 — install that folder

migrate_app migrate_app_remote
You run it on the destination server on your laptop
Needs WordPress where you type it yes no
Gets the folder there you do, by SFTP or File Manager it does, over rsync
Folder ends up in the webroot, publicly reachable until you delete it outside the webroot, never web-reachable
Backup ends up on the destination server on your machine, before the import starts
Needs SSH no yes, key-based

If you can SSH to the destination, prefer migrate_app_remote — fewer manual steps, and it never leaves a copy of your database sitting under a live webroot.

Which combination is yours?

Your situation The two commands
Duplicator export, destination you can SSH to unzip, then migrate_app_remote ./pkg --to=@new
Duplicator export, you are already on the destination unzip into the webroot, then migrate_app ./pkg
Server to server migrate_app_pull ./site --from=@old, then migrate_app_remote ./site --to=@new
Production down to your laptop migrate_app_pull ./site --from=@prod, then migrate_app ./site

migrate_app_pull needs SSH to the source. If you only have access to the destination — the case this tool was originally built for — you cannot pull. Use a Duplicator export as step one instead. Everything from step two onward is identical either way.


Contents

Start here — read this and nothing else on your first run

Step 1 — getting the site into a folder

Step 2 — installing the folder

Reference

When it is done, or when it goes wrong

Other


Two scenarios, start to finish

Almost everything in this README is detail on one of these two. Find the one that matches where you are sitting, follow it top to bottom, and ignore the other.

A — the destination is this machine B — the destination is another server
Where you type the commands on the destination itself on your own machine
Is WordPress installed where you type? yes — that is the site being migrated into no — and it does not need to be
Command you use wp migrate_app wp migrate_app_remote
How the package gets there you put it there (SFTP, File Manager, git) the command uploads it over rsync
Do you need SSH? no yes, key-based
Where the safety backup lands on the destination on your machine, before anything is touched

If you can SSH to the destination, prefer B even when A is possible. Fewer manual steps, the backup comes home before the risky part, and the package never sits under a live webroot.


Before either scenario: install the tool

Pick whichever fits the machine you will be typing on. For A that is the destination server; for B it is your own machine.

# Best default — makes `wp migrate_app` available everywhere on that machine.
wp package install /path/to/wp-cli-migrate-app
# No install — point at the bootstrap each time. Handy on a host you just uploaded to.
wp --require=/path/to/wp-cli-migrate-app/migrate-app.php migrate_app ./my-site

Confirm it is there before going further:

wp help migrate_app          # scenario A
wp help migrate_app_remote   # scenario B

Full details, including the plugin-folder route: Install.


Scenario A — importing into WordPress on this machine

You are on the destination server. WordPress is installed and working there, and you want the old site merged into it.

1. Put the package folder in the WordPress root, next to wp-admin/. However you like — SFTP, File Manager, unzip. You should end up with:

/var/www/html/            ← the working WordPress
├── wp-admin/
├── wp-content/
├── wp-config.php
└── my-site/              ← the package you just put there
    ├── database.sql
    └── wp-content/

2. Write the config:

cd /var/www/html
wp migrate_app ./my-site --generate-config

This reads the package and writes my-site/migration.yaml. Because WordPress is loaded around it, it fills target_url with this site's own URL.

3. Read the file it wrote. Genuinely read it — it is seven lines and it decides what happens:

cat my-site/migration.yaml

4. Rehearse. Nothing is written:

wp migrate_app ./my-site --dry-run

5. Do it:

wp migrate_app ./my-site --yes

A backup is exported first, to the WordPress root. 6. Then delete the package folder — it is sitting in your webroot where the internet can read it:

rm -rf my-site   # or run step 5 with --cleanup

Scenario B — importing into WordPress on another server

You are on your own machine. It does not need WordPress, PHP-with-a-database, or anything but WP-CLI, rsync and an SSH key. The destination has the WordPress.

1. Tell WP-CLI about the server, once. This is the step that answers "how does it know the server?" — you name it here, in WP-CLI's own alias file, ~/.wp-cli/config.yml:

@new:
  ssh: deploy@example.com:22/home/deploy/public_html
  key: ~/.ssh/id_rsa

Read that as <user>@<host>:<port>/absolute/path/to/the/wordpress/root — substitute your own. The port is optional, and so is key:, which you can leave out if the key is already in your ssh-agent or ~/.ssh/config.

Check the connection works before going any further, with plain ssh:

ssh deploy@example.com 'ls /home/deploy/public_html/wp-config.php'

If that prints the path, you are good. If it asks for a password, stop and fix your keys — this tool will not accept one.

Do not test with wp --ssh=@new. It looks equivalent and is not: WP-CLI's own --ssh resolves aliases differently and may fail on an alias this tool reads perfectly well. --to=@new is what matters here, and step 5's --dry-run is the real test of it.

2. Have the package folder anywhere on your machine. Any folder, not a webroot:

~/Sites/my-site/
├── database.sql
└── wp-content/

3. Write the config — no server involved yet:

wp migrate_app_remote ./my-site --generate-config

Note there is no --to here, and that is not an oversight. This step only reads the package on your disk. The file it writes describes the source site — its old URL, its table prefix, which folders to bring — and contains nothing at all about the destination. So there is no server for it to know about yet.

target_url is left empty on purpose, and that is the piece worth understanding: at import time the destination fills in its own URL, read live from the far end. That is why the same package can be installed anywhere without editing it, and why a hardcoded value would be a bug waiting to happen.

4. Read the file it wrote:

cat my-site/migration.yaml

5. Now bring in the server. Rehearse first — this connects, checks the far end and reports, but transfers nothing and changes nothing:

wp migrate_app_remote ./my-site --to=@new --dry-run

Read the summary it prints. It resolves @new to a real host, path and home URL — check they are the ones you meant.

6. Do it:

wp migrate_app_remote ./my-site --to=@new

It uploads to a staging directory outside the webroot, takes a database backup and pulls it home to your machine before anything destructive, then runs the import on the far end.

7. Remove the staging copy once you are happy — it holds a dump of the old database:

wp migrate_app_remote ./my-site --to=@new --cleanup-only

The one thing that trips people up

migration.yaml describes the package, never the destination.

generate-config  ──reads──>  the folder on your disk        (no server, no --to)
                 ──writes─>  migration.yaml

the migration    ──reads──>  migration.yaml  +  --to=@new   (server named here)

That separation is why step 3 needs no --to and step 6 does. It is also why one package folder can be installed on staging, then on production, with no edits in between.


Requirements

WordPress An already installed and working single-site install at the destination. Not multisite.
WP-CLI 2.5+ (developed and tested against 2.12).
PHP 7.4+ (linted and unit-tested under real 7.4).
Database user Needs DROP, CREATE, INSERT on the destination schema.
Disk About 3× the dump size free — the dump, a prefix-rewritten copy, and the backup.
Optional rsync for faster merges. Without it a PHP walk is used, with identical results.
Remote mode See Running it against a remote server. Your machine needs WP-CLI and SSH; it does not need WordPress.

No Composer install is needed at runtime. WP-CLI is the only dependency.


Install

As a WP-CLI package — the command is then available everywhere:

wp package install /path/to/wp-cli-migrate-app

Without installing — point at the bootstrap directly. Usually the easiest option on a host where you just uploaded the folder over SFTP:

wp --require=/path/to/wp-cli-migrate-app/migrate-app.php migrate_app my_site_to_migrated

To make that permanent, add it to the site's wp-cli.yml in the WordPress root:

require:
  - wp-cli-migrate-app/migrate-app.php

As a plugin — drop the folder into wp-content/plugins/. It registers nothing outside WP-CLI, so it stays inert for web requests and does not need activating.

Confirm it loaded:

wp help migrate_app

Read the full README on GitHub →