Lifecycle
The Tamer workflow mirrors Terraform: bootstrap → sync → plan → apply → deploy → drift → destroy.
Bootstrap
tamer bootstrapCreates account-scoped Tamer metadata (shared across all envs):
tamer-state(D1) — deployment state, keyedtamer_state:{env}:{stackName}tamer-artifacts(R2) — Tamer-managed bundlestamer-secrets(D1) — encrypted secrets vault
Run once per account. Idempotent. No --env flag — these resources are account-scoped (no -<env> suffix); per-env data is isolated by row key.
Sync
tamer sync --env devReads Cloudflare API listings and merges them into state. No writes to Cloudflare. Use after out-of-band changes or when adopting existing resources.
Plan
tamer plan --env dev
tamer plan --env dev --json # machine-readable
tamer plan --env dev --out plan.json # save plan with attestation
tamer plan --env dev --destroy # preview destructionRead-only preview of what apply + deploy would create/update/replace. Never mutates state or Cloudflare. See CLI Reference for all flags.
Apply
tamer apply --env dev
tamer apply --env dev --plan plan.json # saved plan
tamer apply --env dev --rollback-on-failure # CI safety
tamer apply --env dev --target d1:settings # scopedProvisions missing resources (D1, R2, KV, Queues, Hyperdrive, Vectorize, AI Gateway, Pipelines, Workflows, Secrets Stores, DNS records). Generates wrangler.json for every worker. Does not deploy workers.
Workflows: deferred registration on greenfield envs
Workflows are a chicken-and-egg case — Cloudflare can't register a Workflow until its host script exists. On a fresh env, apply runs before deploy, so the script isn't live yet. Tamer handles this by deferring Workflow registration during apply when the host script returns a 404, and re-registering it during deploy after wrangler deploy succeeds. You'll see a deferred (script "..." not deployed yet; will register during deploy) notice — that's expected on first apply.
Deploy
tamer deploy --env dev
tamer deploy --env dev --worker api # single workerFor each worker (in topological order by service bindings, including durable_objects.bindings[].script_name):
- Push required secrets from vault
- Generate
wrangler.json - Run
buildstep (if declared) — spawns with resolved vars as env - Run
wrangler types - Run
wrangler deploy - Register Workflows declared by this worker (after the script is live)
- Apply zone-name
tamerRoutesvia Workers Routes API
Drift
tamer drift --env dev
tamer drift --env dev --jsonRead-only diff between Tamer state, Cloudflare reality, and current config. Reports missingFromCloudflare, unrecordedInState, and undeployed.
Destroy
tamer destroy --env dev --confirm-env dev
tamer destroy --env dev --confirm-env dev --plan destroy.jsonRemoves workers, resources, routes, and namespaces for the env. Protected envs (default: prod, production) require --confirm-env or --force.
Destroying a single resource
tamer plan --env dev --destroy --target d1:app-db # preview
tamer destroy --env dev --target d1:app-db # delete just that DB
tamer apply --env dev --target d1:app-db # recreate fresh--target <kind>:<logical> scopes destroy to one declared resource: only its state entries are deleted (every shard, for a sharded D1) — workers, routes, and all other resources are untouched. The protected-env gate still applies. Destroy also accepts shard_group:<groupName>; --wipe-metadata, --skip-workers, and --plan are rejected alongside --target.
Two things to know:
- Recreating a D1 changes its UUID. Deployed workers bind the old id, so after the targeted
apply, runtamer deploy --env <env>(andtamer migrate --env <env>for a fresh database). - It's also the stale-state fix. If a resource was deleted out-of-band (drift reports
missingFromCloudflare),applywon't recreate it while the state entry exists. A targeted destroy tolerates the Cloudflare 404 and prunes the entry; thenapplyrecreates cleanly.
Orphan reconciliation (resources removed from config)
Removing a resource from tamer.config.ts does not delete it from Cloudflare — destruction stays explicit. Instead, tamer sync flags every state entry that's no longer declared (an "orphan"), recording it in the orphanedAt map with the timestamp it was first detected. Two commands surface orphans so you can find them:
tamer statuslists an Orphaned section (stack + tenant).tamer driftreports a removed from config direction per kind.
Acting commands then skip orphans so you don't waste work on the condemned:
wfp tenant resetskips utility D1s and shard groups removed from the worker template (logging a line per skip), resetting only declared siblings.
To actually remove orphans:
tamer destroy --env dev --orphans # stack: every orphaned resource
tamer destroy --env dev --orphans --dry-run # list without deleting
tamer wfp tenant destroy --env dev --workspace acme --orphans # one tenantdestroy --orphans honors preserveOnDestroy and the protected-env gate. A resource re-added to config has its flag cleared on the next sync.
Sync runs automatically before drift / status / destroy / wfp tenant destroy / wfp tenant reset (on non-local envs) so orphan flags are fresh; --allow-stale skips that sync.
Reset (D1 greenfield loop)
tamer reset --env dev --target d1:app-db
tamer reset --env dev --kind d1 # every declared stack D1
tamer reset --env dev --target d1:app-db --skip-deploy
tamer reset --env local --target d1:app-db # local miniflare sqlite only
# WFP tenant D1s (remote greenfield):
tamer wfp tenant reset --env dev --workspace acme --kind d1
# WFP tenant D1s (local miniflare under the template worker dir):
tamer wfp tenant reset --env local --workspace localdev --kind d1One command for the stack dev-loop: targeted destroy → targeted apply → migrate → seed → redeploy workers that bind the reset database(s). Tenant reset is the parallel path for WFP template D1s (remote: recreate CF D1 + reupload; local: wipe sqlite + migrate + seed). See Squashing migrations and Seed for the full workflow (including manual migration-file squashing). D1 only in v1; protected envs require --confirm-env (stack) or --confirm-tenant (remote tenant).
State management
State lives in tamer-state (account-scoped D1, shared across all envs). Each env's state is isolated by row key: tamer_state:{env}:{stackName}. Each stack writes to its own row within the env's namespace. The state schema auto-migrates older versions in place on read.
Key operations:
beginOperation/finishOperation/failOperation— tracks in-progress commands with timestampsoperationHistory— last 50 operations (capped)tamer events --env dev— prints operation timeline