CLI reference
Ported from rex help, which is the source of truth. If this page and the binary ever
disagree, the binary is right — run rex help and
open an issue.
All commands accept --json for raw, machine-readable output. Exit codes are 0 ok,
1 command failed, 2 rexenv isn’t running — or rex refused the command line before it
ran anything.
Every flag-taking command refuses a flag it does not know, naming it, and exits 2
without doing the work:
rex: unknown flag `--dry-runn` for `wp search-replace`An unrecognised flag used to be dropped, so the command ran with the default that flag
existed to override. That is why this is a refusal and not a warning: on site create it
built the wrong site, and on wp search-replace a typo’d --dry-runn --yes turns a rehearsal
into a real replace across the database with nothing left to ask you — the typo removes the
dry run and the --yes removes the question. Each command checks its own list, kept in the
same place that reads the flags, so the two cannot drift apart.
rex status Services + DNS state (the app's ownership-and-liveness truth)rex open Bring the rexenv window to the front (it has no dock icon)rex start Start the shared stack (same as the app's Start all)rex stop Stop the shared stack (same as Stop all)rex restart stop, then startrex doctor Diagnose: DNS + resolver takeovers, edge wire identity, ports, CLI linkrex version App + CLI versions (needs the app; -v/--version works without)rex never starts the app. When it is not running, every command says so and prints the
command that does — open -a rexenv — rather than spawning a headless copy that would then
own your services. rex open is the way to the window once the app is up: rexenv
is a menu-bar app, and a window that has been closed is hidden, not gone.
rex status prints one extra line, and only when there is something to say:
update rexenv <version> can be installed from Settings → About. --json and the MCP
stack_status tool carry the same field. All three read an in-process snapshot — a status
call verifies no signature and touches no network — and there is deliberately no rex update:
installing one is a click in the app and nowhere else. See Updating.
rex doctor gained two findings after 0.4.0. TLDs in use names every TLD your sites answer on
that this Mac cannot resolve, with the cause and the fix — rex tld --repair <tld> when the
resolver file is simply missing, or the plain statement that another tool owns the file when
it does. PHP curl names the PHP minors whose bundled curl uses a resolver that cannot see
.rex hosts, with the count of sites on them: WordPress is patched, a plugin’s raw
curl_init() and non-WordPress PHP are not. Since rexenv’s 8.1–8.5 were rebuilt with a curl
that resolves .rex, the only minor it names is 8.0 — the one PHP rexenv does not build.
Runtime, Windows only, names a missing or too-old Microsoft Visual C++ Redistributable (x64) before it can fail a database or PHP start — see Requirements.
WP core names each WordPress site missing long-named core or default-theme files — the damage rexenv 0.4.0–0.7.1’s WordPress download did — and the fix, rexenv → <domain> → Repair core files. A site whose file list could not be fetched from wordpress.org is reported as not checked, never as clean. See WordPress.
rex --version answers without the app running, and names the commit the binary was built
from — rex <version> (<commit>). rex version adds the running app’s half, labelled as the app’s:
a rex from a mounted dmg reports its own build, not whatever is running on the machine.
rex site list All sites + whether each is actually servingrex site info <domain> Full detail: config, serving state, cert, resources, WPrex site open <domain> Open https://<domain> in the browserrex site login <domain> Open a logged-in wp-admin (magic link; --print to not open)
rex site create <domain> [--name N] [--type wordpress|php|laravel] [--php 8.3] [--server nginx|frankenphp|apache|openlitespeed] [--db mysql|mariadb|postgres|none] [--blueprint <name>] [--multisite subdomain|subdirectory] [--path <folder>]
rex site delete <domain> [--yes] Drops its database and docroot (asks first)rex site logs <domain> [--source K] [--lines N] [--follow]rex site stop <domain> | --all Stop serving THIS site, or every site — rexenv's services keep runningrex site start <domain> | --all Serve this site (or every site, --all) againrex site restart <domain> [--pool] Restart the site's own backend (--pool also bounces its shared PHP pool)rex site domains <domain> [--add N | --remove N] Extra hostnames the site answers onrex site rename <domain> <name> Display name only (domain unchanged)rex site domain <domain> <new-domain> Change the domain (URL rewrite; asks first)rex site move <domain> <dest-parent> Move the docroot under a new parent folderrex site relink <domain> <path> Re-point a LINKED site at a folder you moved (records only)rex site retry <domain> Finish a site whose setup stopped part-wayrex site env <domain> [set K=V | unset K]rex site cert <domain> [--regenerate] Certificate info / fresh leafrex blueprints Saved blueprints (for site create --blueprint)site list’s STATE column has four words, because they have four fixes: serving; stopped —
you stopped it, rex site start serves it again; incomplete — its setup never finished, so
nothing serves it, Retry or Delete it in the app; and down — the stack, or this site’s backend,
is not up. site info says the same thing as one line with the fix in it, and adds a fifth
case: blocked — another server answers :443, not rexenv’s edge (rex doctor). Through 0.8.10
the last three were all down, and a half-made site was even serving, because both commands
reported what the stack believed rather than what answers.
Defaults mirror the app’s New Site dialog — rex site create shop.rex names the site shop
unless --name says otherwise — and WordPress sites get the one-click install.
--starter-db on a blank PHP site (--type php) seeds a database, a starter_items table
and a db.php; on any other shape it is refused rather than silently ignored. --db postgres
is for --type laravel and --type php on a PHP whose build has a working pdo_pgsql —
anything else comes back with the app’s own refusal (see
PostgreSQL). A misspelt flag
is refused too — through 0.4.0 site create ignored a flag it did not recognise and built the
wrong site, and a forgotten value after --name could name a site --starter-db.
site stop removes one site from the serving surface and touches no shared process; site restart restarts only the site’s own backend unless you pass --pool, and reports how many
sites the pool covers either way. Both are explained on Sites,
and site domains on the same page under
More than one hostname. Every site verb finds a site
by any name it answers on.
rex php list Pinned versions: installed, default, pool portrex php default <minor> Default version for new sitesrex php install <minor>rex php uninstall <minor>rex php settings <minor> [set K=V] Whitelisted ini settings (set restarts the pool)
rex site php <domain> <minor> Switch a site's PHP versionrex site xdebug <domain> on|off Toggle the site's Xdebug debug poolrex site server <domain> nginx|frankenphp|apache|openlitespeedphp list prints a NOTE column carrying the two facts that can differ from the pinned
patch: serving 8.3.30 when the running pool has not moved onto it yet, and 8.3.33 exists
when php.net lists a newer patch. A CLI that printed only the pin would be the same silent lie
the app’s Settings row exists to prevent. --json has carried both fields all along.
Databases
Section titled “Databases”rex db export <domain> Dump to ~/Downloads (prints the path)rex db import <domain> <file.sql> [--yes] OVERWRITES the site's tables (asks first)rex db reset <domain> Drop + reinstall WordPress (type the domain)rex db versions [--set <engine> <version>]rex db browse Open Adminer in the browserWordPress
Section titled “WordPress”rex wp <domain> plugin list|install|activate|deactivate|update|delete [slug…] [--activate]rex wp <domain> theme list|install|activate|update|delete [slug…] [--activate]rex wp <domain> user list|create|set-password|set-role|delete …rex wp <domain> user delete <login|id> --reassign <login|id> | --delete-postsrex wp <domain> search-replace <from> <to> [--dry-run] [--yes]rex wp <domain> cache-flush | cron run | maintenance [on|off] | core updaterex wp <domain> core versions | core switch <version>search-replace --dry-run is honoured as typed. 0.4.0’s rex ran the replacement for real on a
typo’d --dry-run, and would have written the flag into the database as a value.
These are vetted WP-CLI operations rather than an arbitrary passthrough. For anything not
covered, the site’s Terminal tab has a full shell with the right PHP and wp on PATH.
Git-backed plugins and themes
Section titled “Git-backed plugins and themes”rex repo <domain> list [--status] --status adds live staterex repo <domain> status <dir> [--theme] Branch, changes, ahead/behind, remote, link targetrex repo <domain> branches <dir> [--theme] Local + remote branches + tagsrex repo <domain> prs <dir> [--theme] PR/MR head refs from the remote
rex repo <domain> add <url> [--branch B] [--name N] [--theme] [--install]rex repo <domain> adopt <dir> [--theme] Manage an existing checkout (metadata only)rex repo <domain> link <path> [--name N] [--theme]
rex repo <domain> pull|fetch|push <dir> [--install]rex repo <domain> checkout <dir> <ref> [--install]rex repo <domain> run <dir> <script> Run one package.json script to completionrex repo <domain> check <dir> [--theme] [--install]rex repo <domain> watch list | watch start <dir> <script> [--tail] | watch tail <dir> | watch stop <dir>rex repo <domain> delete <dir> [--theme] [--yes]rex repo tools [--refresh] Detected git/node (login-shell resolution)Behaviours worth knowing:
checkruns nothing. It reports whether composer/npm dependencies are missing or stale and offers the steps;--installis what actually runs them, stopping at the first failure.pullis--ff-only.pushnever forces.--installafter a git op re-installs only when the operation changed lockfiles.linksymlinks an external folder in. Deleting it later only unlinks — your real folder is never touched.addtakes publichttps/owner/repoURLs, and private repos through your own ssh keys. Output appears at completion; the live view is in the app’s panel.- Watchers run inside the app and stop when it quits.
--tailonwatch start, orwatch tail, follows the output in your terminal; Ctrl-C stops following, not the watcher. - Checking out a listed PR ref lands you detached — those refs carry a number and a sha only.
Services, mail, tunnels
Section titled “Services, mail, tunnels”rex service start|stop <mysql|mariadb|postgres|redis|mailpit>rex service restart <nginx|edge|php-8.3> Bounce one web-tier service on a fresh configrex logs [key] [--lines N] [--follow] No key lists all log files
rex mail List caught messages (Mailpit)rex mail list --unread [query] Filter to unread, optionally by queryrex mail open Open the Mailpit web UIrex mail mark-read Mark every message readrex mail clear [--yes] Deletes ALL messages
rex tunnel listrex tunnel start|stop <domain> start prints the public URLrex start/stop cover the web tier; service start|stop is for the optional engines
individually. service restart is restart-only and web-tier-only — nginx, the edge, or one PHP
pool — on a freshly generated config, because “restart the shared nginx” is the thing you want
after a config edit and “stop the shared nginx” is Stop all under another name.
A tunnel start is now written to rexenv.log, and so is its end; rex tunnel start on a
stopped site warns that the link will show the stopped page.
Everything else
Section titled “Everything else”rex config get|set <key> [value] Settings the CLI may touch (refusals say why)rex tld [--set <tld>] [--repair <tld>] [--remove <tld>] Default TLD for new sites; --repair puts back the OS resolver file for a TLD your sites answer on (what doctor names); --remove deletes one that is rexenv's own and no site usesrex completions zsh|bash Print a shell completion scriptrex mcp MCP stdio bridge for an AI agent's clientrex help