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

1. Create a Federation project

Developers normally start from an empty directory:

mkdir my-city
cd my-city
fed create . --template cloudflare-workers

fed create . --template cloudflare-workers asks for the Federation name and generates the Worker project skeleton:

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

3. 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-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.deployment.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 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.storage is 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 /health to initialize Federation system tables and default keys
  • interactively asks for administrator credentials on first initialization or when --admin-reset is 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 /health when --verify is 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 --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", "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:

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_IDOne-time, tax-inclusive Creem product used for hosted Checkout
CREEM_CURRENCYCreem product currency; it must match the product's actual currency
CREEM_WEBHOOK_SECRETCreem webhook signing secret; the provider is disabled when missing
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.