Operations

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 deploy

1. 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.json
  • src/index.ts
  • package.json
  • tsconfig.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-city

fed 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 deploy

When 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-city

3. 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-run

For another directory:

fed deploy ./my-city --dry-run

5. Deploy flow

fed deploy runs these steps:

  • reads federation.json from 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 bumps package.json.version by one patch version before deployment
  • runs pnpm build if package.json has a build script
  • runs pnpm typecheck if package.json has a typecheck script
  • prepares D1 for the cloudflare-workers target by looking up resources.d1.name, and creates it automatically when missing
  • prepares Queue by looking up resources.queue.name, and creates it automatically when missing
  • if resources.storage is declared, prepares an R2 bucket by looking up resources.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 /health when --verify is 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 --verify

This 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/health

Expected 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:

KeyPurpose
DEEPSEEK_API_KEYProvider key used by the edge starter project
STRIPE_SECRET_KEYStripe API key for Checkout creation
STRIPE_WEBHOOK_SECRETStripe webhook signing secret
DOWNCITY_CITY_BASE_URLPublic City base URL used to derive payment result pages
CREEM_API_KEYCreem API key for Checkout creation
CREEM_PRODUCT_IDCreem product used for hosted Checkout
CREEM_WEBHOOK_SECRETOptional Creem webhook signing secret
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETOptional Google OAuth login
WECHAT_CLIENT_ID / WECHAT_CLIENT_SECRETOptional 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/callback

The 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-only

You can also check manually:

curl https://my-city.<your-subdomain>.workers.dev/
curl https://my-city.<your-subdomain>.workers.dev/health

Updating the deployment

After code changes:

fed deploy --verify

Runtime env lives in D1, so redeploying the Worker does not erase provider keys, OAuth secrets, cities, balances, usage records, or Stripe payment records.