Skip to content

Lifecycle

The Tamer workflow mirrors Terraform: bootstrap → sync → plan → apply → deploy → drift → destroy.

Bootstrap

bash
tamer bootstrap

Creates account-scoped Tamer metadata (shared across all envs):

  • tamer-state (D1) — deployment state, keyed tamer_state:{env}:{stackName}
  • tamer-artifacts (R2) — Tamer-managed bundles
  • tamer-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

bash
tamer sync --env dev

Reads Cloudflare API listings and merges them into state. No writes to Cloudflare. Use after out-of-band changes or when adopting existing resources.

Plan

bash
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 destruction

Read-only preview of what apply + deploy would create/update/replace. Never mutates state or Cloudflare. See CLI Reference for all flags.

Apply

bash
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            # scoped

Provisions 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

bash
tamer deploy --env dev
tamer deploy --env dev --worker api    # single worker

For each worker (in topological order by service bindings, including durable_objects.bindings[].script_name):

  1. Push required secrets from vault
  2. Generate wrangler.json
  3. Run build step (if declared) — spawns with resolved vars as env
  4. Run wrangler types
  5. Run wrangler deploy
  6. Register Workflows declared by this worker (after the script is live)
  7. Apply zone-name tamerRoutes via Workers Routes API

Drift

bash
tamer drift --env dev
tamer drift --env dev --json

Read-only diff between Tamer state, Cloudflare reality, and current config. Reports missingFromCloudflare, unrecordedInState, and undeployed.

Destroy

bash
tamer destroy --env dev --confirm-env dev
tamer destroy --env dev --confirm-env dev --plan destroy.json

Removes workers, resources, routes, and namespaces for the env. Protected envs (default: prod, production) require --confirm-env or --force.

Destroying a single resource

bash
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, run tamer deploy --env <env> (and tamer 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), apply won't recreate it while the state entry exists. A targeted destroy tolerates the Cloudflare 404 and prunes the entry; then apply recreates 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 status lists an Orphaned section (stack + tenant).
  • tamer drift reports a removed from config direction per kind.

Acting commands then skip orphans so you don't waste work on the condemned:

  • wfp tenant reset skips utility D1s and shard groups removed from the worker template (logging a line per skip), resetting only declared siblings.

To actually remove orphans:

bash
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 tenant

destroy --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)

bash
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 d1

One 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 timestamps
  • operationHistory — last 50 operations (capped)
  • tamer events --env dev — prints operation timeline

Released under the Tamer Evaluation License.