Skip to content

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.

  1. 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/test from an uninstalled Valet, say — so there is something to consent to before the first .test domain you type meets a refusal.

  2. You review the list and choose. Nothing is imported because it was found.

  3. Imported sites are created in rexenv pointing at the same folders. Your code is not copied.

  4. If the sites used a TLD rexenv is not serving yet — .test is 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.

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.

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.

Open the sites. They resolve on rexenv’s DNS, serve through rexenv’s stack, and have real HTTPS from rexenv’s CA.

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.

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.

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.

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:

local is 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.

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.

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.

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.

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)

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.