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, admin key retrieval, 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 .fed create . interactively asks for the Federation name and deploy target, then generates the project skeleton:
federation.jsonsrc/index.tspackage.jsontsconfig.json
The generated federation.json only stores basic project information:
{
"schema": 1,
"type": "city",
"name": "my-city",
"target": "cloudflare-workers",
"entry": "src/index.ts",
"resources": {
"d1": {
"binding": "DB",
"name": "my-city-db"
},
"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"
}
}
}type says this is a Federation project, and target declares where it deploys. The current target is cloudflare-workers, which means Cloudflare Edge Workers. entry and resources are commit-safe deployment intent; the D1 database id, Cloudflare account, Worker URL, and Wrangler config are handled by fed deploy at deploy time instead of being written into federation.json.
If the City template already lives in a Git repository, use fed create to bring it local first:
fed create 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 the cloudflare-workers target, fed deploy checks Cloudflare auth automatically. If Wrangler is not logged in, it opens Wrangler login. If Wrangler is logged in but Cloudflare cannot return an account automatically, the CLI prompts you to refresh login or enter the Cloudflare account id once. The account id is saved to local CLI state 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:
{
"resources": {
"d1": {
"binding": "DB",
"name": "my-city-db"
},
"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 the default names ${name}-db and ${name}-queue from federation.json.name. Storage is enabled only when 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.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 for the
cloudflare-workerstarget by looking upresources.d1.name, and creates it automatically when missing - prepares Queue by looking up
resources.queue.name, and creates it automatically when missing - if
resources.storageis declared, prepares an R2 bucket by looking upresources.storage.name, 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> - automatically registers the deployed Worker URL as the current Federation
- requests Worker
/healthwhen--verifyis passed
Deploy output is grouped by stage: Project, Version, Build, Typecheck, D1 Database, Queue, Storage, Wrangler, Deployment, and Active Server.
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/edge as a developer-friendly starter project:
fed deploy templates/edge --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", "accounts", "balance", "payment", "usage", "ai"]
}8. Retrieve the admin key
On first boot, Federation stores DOWNCITY_FEDERATION_ADMIN_SECRET_KEY in its own env table. For Workers + D1, retrieve it from the remote D1 database:
npx wrangler d1 execute my-city-db --remote \
--command "SELECT value FROM env WHERE key='DOWNCITY_FEDERATION_ADMIN_SECRET_KEY'"Keep this key in a trusted place. It is the credential used by Admin City, fed CLI, or direct admin HTTP calls.
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_secret_key>" \
-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 | Creem product used for hosted Checkout |
CREEM_WEBHOOK_SECRET | Optional Creem webhook signing secret |
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.