Deploy to Cloudflare Workers
Deploy a Federation project to Workers, D1, Queue, and R2 with fed deploy.
This page is the operational path for deploying a Federation project to Cloudflare Workers.
If you only want to understand how Federation is wired to Workers, read Cloudflare Workers first. This page focuses on production steps: federation.json, local project creation, local deploys, D1 / Queue / R2, interactive administrator setup, env setup, OAuth callbacks, and health checks.
Prerequisites
- A Cloudflare account
- Dependencies installed in the project
Downcity does not deploy Workers automatically after git push. Deployment is explicit:
fed deploy1. Create a Federation project
Developers normally start from an empty directory:
mkdir my-city
cd my-city
fed create . --template cloudflare-workersfed create . --template cloudflare-workers asks for the Federation name and generates the Worker project skeleton:
federation.jsonsrc/index.tspackage.jsontsconfig.json
The generated federation.json only stores basic project information:
{
"schema": 1,
"type": "federation",
"id": "fed_example",
"name": "my-city",
"entry": "src/index.ts",
"deployment": {
"target": "cloudflare-workers",
"resources": {
"d1": {
"type": "d1",
"binding": "DB",
"name": "my-city-db"
},
"queue": {
"type": "queue",
"binding": "DOWNCITY_QUEUE",
"name": "my-city-queue"
},
"storage": {
"type": "r2",
"binding": "DOWNCITY_STORAGE",
"name": "my-city-storage",
"public_url_prefix": "https://images.example.com"
}
}
}
}id is the path-independent Fed identity, and deployment.target declares where it deploys. entry and deployment.resources are commit-safe deployment intent; D1 ids, account state, Worker URLs, and Wrangler config are handled by fed deploy.
If the City template already lives in a Git repository, use fed create to bring it local first:
fed create my-city --template https://github.com/example/my-city.git
cd my-cityfed deploy does not deploy remote URLs directly. It only deploys local Federation projects, so .env and deploy results have a clear project owner.
2. Deploy the current directory
Inside the Federation project directory:
fed deployWhen federation.json uses deployment.target: "cloudflare-workers", fed deploy checks Cloudflare auth automatically. Account state is stored in the system-level Federation registry and reused by later deploys.
You can also pass a local directory:
fed deploy ./my-city3. Resource Config
Cloudflare Workers currently supports three resource declarations:
{
"deployment": {
"target": "cloudflare-workers",
"resources": {
"d1": {
"type": "d1",
"binding": "DB",
"name": "my-city-db"
},
"queue": {
"type": "queue",
"binding": "DOWNCITY_QUEUE",
"name": "my-city-queue"
},
"storage": {
"type": "r2",
"binding": "DOWNCITY_STORAGE",
"name": "my-city-storage",
"public_url_prefix": "https://images.example.com"
}
}
}
}If D1 / Queue are omitted, fed deploy uses ${name}-db and ${name}-queue. Storage is enabled only when deployment.resources.storage exists.
During deployment, fed deploy reuses same-name D1 databases, Queues, and R2 buckets. If a declared resource is missing and this is not --dry-run, it creates the resource automatically. The D1 database id is Cloudflare deploy state and is not written back to project config.
Cloudflare account is not part of the Federation project config. fed deploy reuses Wrangler login first; only when Cloudflare cannot resolve the account automatically does the CLI prompt once and save it into local fed CLI state.
You can also pass the account id for one deploy:
fed deploy --account-id <your-cloudflare-account-id>4. Dry-run
Before publishing, check the bundle and Wrangler config:
fed deploy --dry-runFor another directory:
fed deploy ./my-city --dry-run5. Deploy flow
fed deploy runs these steps:
- reads
federation.jsonfrom the target directory - checks the target platform identity from
federation.json.deployment.target; Cloudflare Workers opens Wrangler login or guides account selection when needed - if this is not
--dry-run, automatically bumpspackage.json.versionby one patch version before deployment - runs
pnpm buildif package.json has abuildscript - runs
pnpm typecheckif package.json has atypecheckscript - prepares D1 by looking up
deployment.resources.d1.name, and creates it automatically when missing - prepares Queue by looking up
deployment.resources.queue.name, and creates it automatically when missing - if
deployment.resources.storageis declared, prepares the corresponding R2 bucket and creates it automatically when missing - generates Wrangler config temporarily instead of writing it into the project directory
- runs
wrangler deploy --config <generated-wrangler.toml> - requests Worker
/healthto initialize Federation system tables and default keys - interactively asks for administrator credentials on first initialization or when
--admin-resetis used - writes only the password digest through deployment-authorized D1 access, then verifies
/v1/admin/login - registers the Worker URL, deploy state, administrator ID, and time-limited Session Token in the local Federation registry without changing the active Federation
- requests Worker
/healthwhen--verifyis passed
Deploy output is grouped by stage: Project, Version, Build, Typecheck, D1 Database, Queue, Storage, Wrangler, Deployment, and Federation Registry.
downfed has no default Federation. A deployed instance appears in the downfed list, but active_server_url is written only when the user explicitly opens that Federation.
federation.json stays as the commit-safe project declaration. Business secrets, provider keys, and service env should be written into Federation's own env table instead of federation.json.
fed deploy --dry-run and fed deploy --verify-only do not change the project version.
6. Developer edge starter
The Downcity repository keeps templates/edgefed as a developer-friendly starter project:
fed deploy templates/edgefed --verifyThis starter is useful for local experiments and custom deployments. Downcity's private official production Worker implementation lives outside the public repository.
7. Initialize Federation
Call /health once after deployment. This lets the Worker boot Federation and create the built-in env / cities tables.
curl https://my-city.<your-subdomain>.workers.dev/healthExpected shape:
{
"ok": true,
"services": ["env", "cities", "bureaus"]
}8. Administrator login
On the first fed deploy, Downcity asks for an administrator ID, password, and confirmation. Passwords must be at least 12 characters; only a PBKDF2 digest is stored in D1. To replace lost credentials, run fed deploy --admin-reset; this requires deployment authority, revokes all existing administrator sessions, and asks for new credentials. --yes confirms the destructive reset in non-interactive environments but does not skip credential prompts.
After deployment, fed web, the admin workspace, and fed query authenticate through /v1/admin/login and use a time-limited administrator Session Token. The browser never receives the remote Session Token; it remains in the local CLI BFF or registry.
9. Write provider and service env
Provider keys and service secrets should be written into the Federation env table instead of public clients.
Example:
curl -X POST https://my-city.<your-subdomain>.workers.dev/v1/env/upsert \
-H "Authorization: Bearer <admin_session_token>" \
-H "Content-Type: application/json" \
-d '{"key":"DEEPSEEK_API_KEY","value":"sk-..."}'Common keys:
| Key | Purpose |
|---|---|
DEEPSEEK_API_KEY | Provider key used by the edge starter project |
STRIPE_SECRET_KEY | Stripe API key for Checkout creation |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret |
DOWNCITY_CITY_BASE_URL | Public City base URL used to derive payment result pages |
CREEM_API_KEY | Creem API key for Checkout creation |
CREEM_PRODUCT_ID | One-time, tax-inclusive Creem product used for hosted Checkout |
CREEM_CURRENCY | Creem product currency; it must match the product's actual currency |
CREEM_WEBHOOK_SECRET | Creem webhook signing secret; the provider is disabled when missing |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Optional Google OAuth login |
WECHAT_CLIENT_ID / WECHAT_CLIENT_SECRET | Optional WeChat website login |
10. Configure OAuth callbacks
If you enable OAuth login, configure the provider callback URL to the deployed Worker:
https://my-city.<your-subdomain>.workers.dev/v1/accounts/oauth/callbackThe callback domain must match the public domain users actually visit. If you later move to a custom domain, update the OAuth provider configuration as well.
11. Verify
If a Federation is already connected, run only the health check:
fed deploy --verify-onlyYou can also check manually:
curl https://my-city.<your-subdomain>.workers.dev/
curl https://my-city.<your-subdomain>.workers.dev/healthUpdating the deployment
After code changes:
fed deploy --verifyRuntime env lives in D1, so redeploying the Worker does not erase provider keys, OAuth secrets, cities, balances, usage records, or Stripe payment records.