Migrating from Valet, Herd or Local
Four stages, each one opt-in, with one rule holding across all of them. Valet, Herd and Local (WP Engine’s, formerly Local by Flywheel) all go through the same four; what is different about Local has its own section below.
Stage 1 — Scan and import
Section titled “Stage 1 — Scan and import”-
rexenv scans for Valet, Herd and Local installations and lists what it found: sites, their paths, their PHP versions, their domains. A Valet project registered under several names — a link farm — is listed as one site with extra domains, not as several sites that would then refuse each other for sharing a folder. The primary is the shortest name; the rest are aliases the imported site answers on, with one certificate covering all of them. The scan also lists a leftover resolver file with no site behind it — an
/etc/resolver/testfrom an uninstalled Valet, say — so there is something to consent to before the first.testdomain you type meets a refusal. -
You review the list and choose. Nothing is imported because it was found.
-
Imported sites are created in rexenv pointing at the same folders. Your code is not copied.
-
If the sites used a TLD rexenv is not serving yet —
.testis the usual one — it asks for consent before installing that resolver, because taking over a TLD another tool is answering for is exactly the kind of change that should not happen quietly.
Stage 2 — Copy the databases
Section titled “Stage 2 — Copy the databases”Dumps from the source database server and restores into rexenv’s, recording where each database came from.
The source database is read and never modified. Both copies exist afterwards, which is the point: you can compare them, and going back costs nothing.
Stage 3 — Rewrite connection config
Section titled “Stage 3 — Rewrite connection config”Optional, and the most invasive thing in the flow, so it is also the most gated.
An imported site’s wp-config.php or .env still points at your old database server, on the
old port. rexenv can update those to point at its own — and before it does:
- It shows you a diff of exactly what will change.
- It takes a backup of every file it will touch.
- It waits for you to agree.
Declining leaves your config alone, and the site keeps talking to whatever it talked to before. That is a supported outcome, not a failed migration.
Stage 4 — Verify
Section titled “Stage 4 — Verify”Open the sites. They resolve on rexenv’s DNS, serve through rexenv’s stack, and have real HTTPS from rexenv’s CA.
Coming from Local
Section titled “Coming from Local”Local keeps its sites differently from Valet and Herd, and each difference below changes what an import does. The rules above still hold: Local’s folders are linked in place, its databases are copied and never written, and none of Local’s servers is ever started or stopped by rexenv.
What rexenv reads
Section titled “What rexenv reads”Local’s own registry, ~/Library/Application Support/Local/sites.json — each site’s folder,
domain, PHP version, multisite mode and the port of its database server. WordPress is always in
the site’s app/public folder, and that is the folder rexenv serves. Local’s
site-statuses.json is not read: “running” in a file is a label, not a server that answers.
.local becomes your default TLD
Section titled “.local becomes your default TLD”rexenv refuses .local — macOS uses it for Bonjour, and taking it over breaks printers and
AirDrop. So a Local site keeps its folder but not its hostname: shop.local imports as
shop.rex (or whatever your default TLD is), and the row shows was shop.local beside the new
name. A Local site on a TLD rexenv allows, shop.test say, keeps its name exactly as a Valet site
does.
If the new name is already taken by a site in rexenv, the row does not pick another one for you.
It asks, with an import as field suggesting shop-local, and stays unticked until the name
you type is free.
PHP versions rexenv does not ship
Section titled “PHP versions rexenv does not ship”Local pins a full version per site, 7.3.5 for example. When rexenv ships that minor, the site
gets it. When it does not, the row says so and offers a picker:
PHP 7.3 isn’t one rexenv ships — choose a version to import it on
Nothing is substituted silently; the row stays unticked until you choose.
The database: start the site in Local first
Section titled “The database: start the site in Local first”Local runs one database server per site, and only while that site is started in Local. rexenv
copies from that server — over its own socket, found through the registry, because the site’s
wp-config.php says only localhost. If the site is stopped, its row fails with Local named:
localis the database of the “Shop” site in Local, and its server isn’t running — Local runs a site’s database only while that site is started. Start “Shop” in Local, then retry. rexenv never starts or stops Local’s servers.
Start it in Local, then Retry. app/sql/local.sql is never used instead: it is a snapshot
from whenever Local last exported, and importing it would quietly roll your data back.
Every Local database is called local, so the copy is named after the site from the start —
local_shop_rex for shop.rex — and the next Local import never collides with this one.
URLs move in rexenv’s copy only
Section titled “URLs move in rexenv’s copy only”A Local database says http://shop.local everywhere, and WordPress would redirect every request
back to a name rexenv does not serve. So the import ends with a search-replace on rexenv’s
copy: http://shop.local and https://shop.local become https://shop.rex, through WP-CLI’s
serialization-aware search-replace. Local’s own database is only ever read.
Connecting is on by default, and the site needs it
Section titled “Connecting is on by default, and the site needs it”A Valet or Herd site keeps reading its old database server until you connect it. A Local site cannot: its config names Local’s server, which only Local can reach, so under rexenv it reads no database at all. Until it is connected, the app says so on the site’s Database tab:
This site can’t load under rexenv until you connect it — its config still names Local’s own database server, which only Local can reach, so until the change below is applied it shows WordPress’s database error here. The site keeps working in Local.
That is why the Import page has an also connect Local sites box, ticked by default whenever
databases are copied. It does Stage 3 for every Local site in the same run: each wp-config.php
is backed up first, then pointed at the copy, and the site’s Database tab reverts it in one click.
Untick it to connect each site yourself later, from its Database tab, with the diff in front of
you. Two things to know before you leave it ticked:
- The project folder is shared, so while a site is connected, Local reads rexenv’s copy too. Revert on the site’s Database tab puts the file back.
- Valet and Herd sites are never connected by this box; they keep their per-site choice.
Multisite networks
Section titled “Multisite networks”Subdomain and subdirectory networks import as networks — adopted as they are, never converted.
The URL pass moves every subsite onto the new name along with the network. Connecting a network
also moves its DOMAIN_CURRENT_SITE to the new name, which is in the diff you approve; while it
is connected the network will not load under its old name in Local, and Revert puts the line
back.
Two network shapes are listed and not imported, each with its reason on the row: a network with a subsite on its own mapped domain rather than on or under the network’s name, and a multisite kind rexenv does not recognise.
What stays behind
Section titled “What stays behind”Local’s router, certificates, Mailpit, Live Links, Blueprints and Connect (host sync) — rexenv serves the site with its own stack and issues its own certificate. Sites from Local by Flywheel 2.x, the version that ran sites inside a virtual machine, cannot be linked at all; if it is still installed, the scan says so above the rows from current Local.
Running both at once
Section titled “Running both at once”You can, for a while — the ports are offset precisely so that rexenv does not collide with another local environment. See Ports.
80 and 443 are the exception. Only one thing can hold them, so only one environment can
serve https://site.test with no port number at a time. In practice that means quitting the
other tool when you want rexenv to serve, and vice versa. See
Port 80 or 443 is in use.
Local does not have to be quit. What holds the ports there is Local’s router, and rexenv names it with the setting that frees them while Local and its sites keep running:
Local’s router (nginx, pid …; or set Local → Preferences → Advanced → Router Mode to localhost to free the port without quitting Local)
What is not migrated
Section titled “What is not migrated”Global Valet or Herd configuration — parked directories, custom drivers, per-tool PHP extensions — and, from Local, everything listed under What stays behind. Sites, databases and connection config are what move; anything you customised at the tool level is worth reviewing by hand.