docs / Deploy
Deploy
deploy runs build → (D1) → (deploy) and asks only what it cannot decide. Every prompt has a default; every cloud action is preceded by a check.
Preview first — --plan
--plan uses the same resolver and the same safety decisions as a real run, but it is zero-mutation and offline by default: no build, no dist/ write, nowrangler.jsonc or lw-state.json edit, no D1 create or apply, no deploy, and no prompts. It prints the worker name, the account a real run would target, the D1 action and identity, the schema action, the files a real run would change, and the cloud operations it would perform.
npx laravel-workers deploy --plan --mode 3 --d1-auto
npx laravel-workers deploy --plan --mode 3 --d1-auto --check # + read-only account/D1 checks
# exit 0 the plan is actionable
# exit 2 a real run would stop (bad flags, state, missing D1 id, schema drift under --yes)
# exit 1 --check could not complete its read-only lookupThe schema line describes the existing dist/schema.sql; because--plan does not build, a missing file is called out and an existing one is markednot checked against the current source. Run a real build first when the preview must reflect a source change.
Three modes
| mode | what it does | touches Cloudflare? |
|---|---|---|
| 1 | compile only — identical to build | no |
| 2 | build, then create or reuse a D1 and apply the schema when the database is empty | D1 only |
| 3 | mode 2, then wrangler deploy; the URL and ids are recorded in lw-state.json | D1 + Worker |
The prompts
$ npx laravel-workers deploy What should I do? [1] build only [2] build + database [3] build + database + deploy Choice [1] ▸ 3 Worker name [my-laravel-app] ▸ Database: [A] auto [B] list existing [C] paste database_id Choice [A] ▸ A → built dist/ · READINESS: RUNS ON THE EDGE → D1 "my-laravel-app-db" ready → deployed · https://my-laravel-app.example.workers.dev
The worker name comes from wrangler.jsonc when present, then fromlw-state.json, then from the app-directory slug. The build runs with--strict, and only after it exits 0 does anything on Cloudflare happen — a failed build cannot leave an orphan D1 or a half-deploy.
The schema guard
dist/schema.sql is generated CREATE TABLE DDL, not an idempotent migration. Applying it to a database that already has those tables can fail withtable already exists — so deploy refuses to guess.
| D1 state | what happens |
|---|---|
| 0 tables | schema applied automatically |
| tables, no recorded hash | interactive: asks before applying; with --yes: stops (exit 2) |
| tables, hash matches | skipped as already applied — no prompt |
| tables, hash changed | stops on drift; say --skip-schema or --schema=apply |
| table count unreadable | stops instead of assuming the database is empty |
The account guard
lw-state.json records the Cloudflare account id a deployment used. Before any D1 or deploy call, modes 2 and 3 resolve the account Wrangler will actually use — theaccount_id pin first, then CLOUDFLARE_ACCOUNT_ID (orCF_ACCOUNT_ID), then the sole account wrangler whoami reports — and compare it with the recorded id. A mismatch refuses the run before anything is touched.
- The id is the identity: a renamed account, or the same account through another email alias, does not switch; a different id always stops.
- Switching is explicit:
--allow-account-change, oryat an interactive prompt.--yesand non-interactive runs never confirm. - A switch clears the stale URL and deploy time, and refuses to reuse a recorded D1 the new account does not own.
- Several accounts logged in with nothing pinning one stops as well — accounts are never guessed.
What is recorded
{
"schemaVersion": 1,
"worker": "my-laravel-app",
"workerUrl": "https://my-laravel-app.example.workers.dev",
"databaseId": "…",
"accountId": "…",
"schemaHash": "…",
"deployedAt": "2026-10-18T09:12:04.331Z"
}Add lw-state.json to .gitignore unless you want the worker URL and D1 id in source control. A damaged, unreadable or future-version file stopsdeploy before any prompt; status and doctor report it and exit 2 rather than pretending the app has no deployment.
Non-interactive (CI)
npx laravel-workers deploy --name my-worker --mode 3 --d1-auto # full deploy, no prompts
npx laravel-workers deploy --mode 3 --d1-id <uuid> --yes # same D1, automatedSee CI & verdict for the workflow that wraps this, andCLI reference for every flag and exit code.
Source: README.md §deploy — repository ba1e9a5