Monorepo with two Workers and a shared package. Demonstrates the documented limitations of the Deploy to Cloudflare button.
Category C: Documented limitations. Things the deploy button explicitly doesn't support, plus a common footgun the docs warn about. Useful for showing what the failure UX looks like to users who hit these walls.
dtc-broken-limitations/
├── package.json # workspaces root
├── tsconfig.base.json # shared TS config
├── migrations/ # D1 migrations (referenced by worker-api)
│ └── 0001_init.sql
├── packages/
│ └── shared/ # shared lib imported by both workers
│ ├── package.json # name: @dtc/shared
│ └── src/index.ts
└── apps/
├── worker-api/ # Worker #1 — references @dtc/shared + ../../migrations
│ ├── package.json
│ ├── wrangler.jsonc
│ └── src/index.ts
└── worker-frontend/ # Worker #2 — references @dtc/shared
├── package.json
├── wrangler.jsonc
└── src/index.ts
The Deploy to Cloudflare flow has roughly six steps:
- Click button → land on
deploy.workers.cloudflare.com. - Auth → sign in to Cloudflare and connect a Git provider.
- Configure form → name Worker/repo, fill in resource and secret fields.
- Submit form → Cloudflare validates the form.
- Clone + provision → Cloudflare copies the subdirectory (only) into your GitHub, creates resources.
- Build + deploy → Workers Builds runs
npm install+ yourdeployscript.
| # | Bug | Fails at step |
|---|---|---|
| 1 | worker-api imports @dtc/shared, which lives at packages/shared/ outside the subdirectory. When the button clones only apps/worker-api/, the dependency vanishes |
Step 6 — npm install cannot resolve @dtc/shared, then tsc/wrangler deploy fails: Cannot find module '@dtc/shared' |
| 2 | worker-api/wrangler.jsonc sets migrations_dir: "../../migrations", which escapes the cloned subdirectory |
Step 6 — wrangler d1 migrations apply fails: migrations directory not found |
| 3 | Repo contains TWO Workers (worker-api, worker-frontend). Cloudflare only deploys one per button click. User ends up with two disconnected Cloudflare projects and two GitHub repo clones for the same source |
Step 1 / 5 — user has to know to click two different buttons. There is no single-button deploy for the whole app |
| 4 | worker-api's deploy script references the database by NAME (dtc-broken-limitations-prod-db) instead of by BINDING (DB). If the user renames the database in the form, the migrations command targets a database that doesn't exist |
Step 6 — migrations fail with "database not found" if the user changed the default name in Step 3 |
| 5 | Workspace-style package.json at the root with workspaces: ["apps/*", "packages/*"]. When the cloned subdirectory loses the root, npm install behavior differs and Wrangler autoconfig (if it runs) can't find framework deps that live at the workspace root |
Step 6 — varies; usually npm install resolution errors or autoconfig misdetection |
- User clicks the API worker button.
- Signs in. Form looks normal.
- Clicks Deploy.
- Cloudflare clones
apps/worker-api/(only) into a new GitHub repo.packages/shared/andmigrations/do not come with it. - Workers Builds runs
npm run deploy:npm installerrors on@dtc/sharednot being a registered package.- Or the build proceeds, then
wrangler d1 migrations applyfails because../../migrationsdoesn't exist. - Or
wrangler deployfails on the import of@dtc/shared.
- To get the frontend deployed, user has to come back, click the second button, repeat. They now have two unrelated Cloudflare Workers + two unrelated GitHub repos for what was supposed to be one app.
A small two-Worker app sharing one library:
worker-api→GET /items,POST /items, backed by D1.worker-frontend→ returns HTML, in a real app would proxy to the API.- Both use the
@dtc/sharedok()/err()helpers.
- Monorepo / subdirectory must be self-contained: Deploy to Cloudflare docs → Limitations
- Multiple Workers in one repo not deployed together: same docs section.
- Workspace dependencies hide framework: Wrangler automatic configuration → Workspaces
- Migrations referencing database name vs binding: Deploy to Cloudflare docs → Running D1 Migrations
- Push this folder to a new public GitHub repo.
- Update the
REPLACE_MEin both badge URLs above. - Click each badge and observe failures at step 6 (or the awkward "two buttons" problem at step 1).