Greenfield Setup
Starting fresh with Tamer — no existing Cloudflare resources, no legacy naming. This is the happy path from empty repo to a live multi-tenant deployment.
1. Install Tamer
npm install -D @dragonmastery/tamer wrangler2. Create your config
Minimum viable config for a single-product multi-tenant app:
// tamer/project.config.ts
import { cf, defineConfig } from "@dragonmastery/tamer";
export default defineConfig({
stack: "myapp",
account_id: "your-account-id",
compatibility_date: "2025-05-19",
// Workers for Platforms: dispatch namespace per product-env (default
// name `{product}-{env}`). Each service declared under `workers` gets
// one script per workspace (`{service}-{workspace}-{env}`).
wfp: {
ephemeralEnvPattern: "^pr-",
namespaces: {
myapp: {
workers: {
tenant: {
main: "apps/tenant/src/index.ts",
shardGroups: [
{ name: "primary", binding: "DB", migrationsDir: "db/primary/migrations" },
],
d1: [
{ logicalName: "system", binding: "DB_SYSTEM", migrationsDir: "db/system/migrations" },
],
},
},
},
},
},
workers: {
api: {
path: "apps/api",
main: "src/index.ts",
scriptName: "myapp-api",
resources: {
d1: [{
logicalName: "app-db",
type: "single",
binding: "APP_DB",
migrationsDir: "apps/api/migrations",
}],
},
vars: { ENVIRONMENT: "local" },
env: {
dev: { vars: { ENVIRONMENT: "${tamer:env}" } },
prod: { vars: { ENVIRONMENT: "prod" } },
},
tamerRoutes: [
{ host: "api.myapp.com", customDomain: true },
],
},
spa: {
path: "apps/spa",
scriptName: "myapp-web",
assets: { directory: "dist", not_found_handling: "single-page-application" },
build: { command: "vite build" },
vars: {
VITE_API_URL: "http://127.0.0.1:8993/v1",
ENVIRONMENT: "local",
},
env: {
dev: { vars: { VITE_API_URL: "https://${tamer:env}.api.myapp.com/v1", ENVIRONMENT: "${tamer:env}" } },
prod: { vars: { VITE_API_URL: "https://api.myapp.com/v1", ENVIRONMENT: "prod" } },
},
tamerRoutes: [
{ host: "myapp.com", customDomain: true },
],
},
},
outputs: {
api_worker_name: cf.worker("api").name,
},
});For the full split-config pattern (per-worker files with env overrides), see Single-Product Multi-Tenant.
3. Set up .env
# .env (gitignored)
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_API_TOKEN=your-api-tokenThat's it — no app config values, no VITE_* URLs. Just Tamer's auth credentials. See Values Lifecycle.
4. Bootstrap (once per account)
bunx tamer bootstrapCreates three shared account-scoped resources:
tamer-state(D1) — deployment state for all envstamer-secrets(D1) — encrypted secrets vaulttamer-artifacts(R2) — build bundles
Idempotent — safe to run multiple times.
5. Initialize secrets
# Generate master key for dev (shown once — store in CI + password manager)
bunx tamer secrets init --env dev
# Set each required secret (pipe the value on stdin — interactive entry
# is not supported yet, so a bare `set` on a TTY errors)
echo -n "sk_live_..." | bunx tamer secrets set STRIPE_API_KEY --env dev
echo -n "..." | bunx tamer secrets set JWT_SECRET --env dev
# Verify all declared secrets are in the vault
bunx tamer secrets verify --env devStore the master key as TAMER_SECRETS_KEY_dev in your CI secrets.
6. Apply resources
bunx tamer apply --env devCreates everything declared in config:
- Dispatch namespace (
myapp-dev, one perwfp.namespacesproduct) - D1 databases (e.g.
db_app-db_dev—db_{logical}_{env}) - R2 buckets
- KV namespaces
- DNS records / routes
- Generates
wrangler.jsonper worker
7. Migrate databases
bunx tamer migrate --env devRuns D1 migrations on each database declared with migrationsDir.
8. Deploy workers
bunx tamer deploy --env devPer worker (topologically sorted by service bindings):
- Generate
wrangler.json(if changed) - Build SPA (
vite build— values baked at compile time) wrangler types→wrangler deploy- Push secrets from vault
- Register workflows
9. Provision your first tenant
# `--main` is optional — defaults to the template's configured `main`.
bunx tamer wfp tenant provision --env dev \
--workspace acmeCreates per-tenant D1 shards, runs migrations, bundles the tenant Worker via wrangler (TS compilation, WASM, polyfills), and deploys it to the dispatch namespace with D1 bindings. See Single-Product Multi-Tenant for the dispatch routing pattern.
10. Set up CI
Copy the workflow templates from CI Workflows into .github/workflows/. Set up GitHub secrets:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID(variable)TAMER_SECRETS_KEY_devTAMER_SECRETS_KEY_prod
Recap
bootstrap → once per account
secrets init → once per env (generates master key)
secrets set → per secret
apply → creates resources
migrate → runs D1 migrations
deploy → builds + deploys workers
wfp tenant provision → creates a tenantNext: Single-Product Multi-Tenant for the full architecture, CI Workflows to automate everything, or Local Development for the daily dev loop.