Skip to content

Quickstart

shipped 1.0.0

From nothing to a working local copy of a production site.

# 1. An empty project directory
mkdir -p ~/Code/example/example.com
cd ~/Code/example/example.com

# 2. Scaffold. Interactive: it asks for the project
#    name, the host and the site-plugin directory.
cwp init

# 3. Start the container and put WordPress core in
#    place. Core is gitignored, never in the repo.
ddev start
ddev wp core download

# 4. Fetch the themes and plugins the site needs
#    in order to render.
cwp fetch

# 5. Pull the production database, proxying the
#    uploads rather than copying them. Runs
#    search-replace, scrubs personal data, installs
#    the mail guard, runs the post-import steps.
cwp pull

# 6. Open it.
cwp open

The first pull on an image-heavy site takes about two minutes, because uploads are proxied rather than copied — a missing file below /wp-content/uploads/ is fetched from the remote on demand. cwp pull --uploads sync copies them instead, and costs what copying them costs.

What just happened

Step 5 is the one worth reading. In order: the local database was snapshotted first, the remote database was exported and brought down, imported, rewritten from the remote URL to the local one, then scrubbed — and the mail guard was installed whether or not scrubbing was switched off.

That last clause is a rule rather than a default. A pulled production database contains real customer addresses and can be tricked into sending mail; the guard intercepts and logs outbound mail instead of sending it.

Check the setup

cwp doctor is read-only and every failure carries the command that fixes it. On a project whose environment does not exist yet, it fails — which is the state a reader meets first, so it is the state recorded here:

$ cwp doctor
✓ Node ≥ 22 — Node 26.5.0
✓ Docker runtime — reachable
✓ DDEV installed — ddev version v1.25.3
✓ mkcert CA — installed
✓ Cloudron CLI installed — 8.2.6
✓ Machine config — valid
✓ Project config — valid
✓ Environment — dev
✓ Cloudron logged in — authenticated
✗ Remote app reachable — "example.com" not in cloudron list — fix: check the cloudron_app value in .cwp.yml
! Remote wp responds — skipped — provider did not pass
! PHP parity — skipped — provider did not pass
✗ WordPress core — not installed in "public" — fix: ddev wp core download
! DDEV project running — not started — fix: ddev start
✓ Uploads fallback — nginx snippet
! Uploads fallback serves a missing file — skipped — found no attachment whose file is absent locally — fix: nothing to fix: with every file present there is nothing for the fallback to serve
✓ Bricks child theme — none configured, none installed — Bricks runs as the active theme
! Bricks readiness (local) — skipped — WP-CLI did not answer on local
! Bricks abilities (remote) — skipped — the environment is not reachable
! Custom post type drift — skipped — the local post types could not be listed — fix: ddev start
✗ 2 checks failed
  → apply the fixes shown against the ✗ checks above

Captured from cwp 1.0.0, exit 1. Not written by hand.

Then take a baseline

cwp db snapshot baseline

Restoring costs five seconds; re-pulling costs minutes. Make it a reflex before any experiment that touches the database — that is what cwp db is for.

Where to go next