Skip to content

Deploy

Provision D1 and R2, apply migrations, set secrets and deploy the engine and web Workers, plus the docs site.

OrderLoop deploys as two Workers in one Cloudflare account:

Worker Config Role
orderloop-engine apps/engine/wrangler.jsonc OrderAgent Durable Object, orderloop-order and orderloop-replenish Workflows, Workers AI. No public URL (workers_dev and preview URLs are off); reached only through the web app’s service binding
orderloop-web apps/web/wrangler.jsonc Dashboard, editor, REST API. Binds the engine as ENGINE (entrypoint EngineApi)

Both bind the same D1 database (orderloop) and R2 bucket (orderloop-docs).

pnpm exec wrangler login
./scripts/provision.sh # creates D1 `orderloop` and R2 `orderloop-docs`; safe to re-run
pnpm --filter @orderloop/web db:migrate:remote
pnpm --filter @orderloop/web exec wrangler secret put APP_PASSWORD
pnpm --filter @orderloop/web exec wrangler secret put WEBHOOK_SECRET
pnpm run deploy # engine first (web binds to it), then web

If provision.sh prints a database_id, put it in the d1_databases entry of both wrangler.jsonc files. Change the web app’s routes to your own hostname.

Use pnpm run deploy, not pnpm deploy: the bare form is pnpm’s built-in deploy command and does something else entirely.

  1. Migrations before code. Apply remote D1 migrations before deploying code that reads a new table or column.
  2. Engine before web. The web Worker’s service binding needs the engine to exist.

Workflow names (orderloop-order, orderloop-replenish) must be unique in the account.

To start orders from email, enable Email Routing on the zone and route the intake addresses to orderloop-engine after the engine is deployed. Enabling it replaces the zone’s MX records. The steps are in Email intake.

  • Set TIME_SCALE to "1" in apps/engine/wrangler.jsonc, or waits and timeouts stay compressed (the default "1440" turns a day into a minute).
  • Set AI_GATEWAY_ID to route model calls through AI Gateway for caching, rate limits and logs.
  • After the first deploy, sign in with no email and APP_PASSWORD, open Team and add people with their roles, and create API tokens for other systems. APP_PASSWORD stays as a break-glass operator sign-in. See Team and roles.

All settings are listed in Configuration.

Production deploys from CI: merging to main is the release. .github/workflows/ci.yml runs lint, type-check, build and tests on every push and pull request. On main it then migrates D1, deploys the engine, deploys the web app, smoke-tests /health and deploys these docs, in that order, so a change never reaches production before its migration. The deploy job needs the repository secrets CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID; without them it does nothing.

Each deploy step runs through a small retry helper (three attempts, 15 then 30 seconds apart), because the Cloudflare API occasionally fails a request outright (codes 7403, 10013) and every step is safe to repeat. If the job still fails, re-run its failed jobs in GitHub Actions rather than deploying from a laptop. The manual commands under First deploy are for setting up a new account.

These docs are apps/docs (@orderloop/docs), a static Starlight site served by a static-assets Worker, orderloop-docs, with no runtime code.

pnpm docs:dev # local preview
pnpm docs:build # static build into apps/docs/dist
pnpm docs:deploy # build, then wrangler deploy

The reference pages (node catalog, built-in flows, event and status lists) and the OpenAPI explorer render from the source at build time, so rebuild and redeploy the docs whenever those change.