zudo-cloudflare-wisdom

Type to search...

to open search from anywhere

Standalone Workers

CreatedApr 4, 2026UpdatedApr 5, 2026Takeshi Takatsudo

Deploying independent Workers separate from Pages

When to Use

Standalone Workers are best for:

  • Search workers — Dedicated search APIs with their own data and dependencies
  • AI chat workers — LLM-powered endpoints with API key bindings
  • Webhook handlers — Receiving callbacks from external services
  • Proxy workers — Routing or transforming requests

Project Structure

A typical standalone Worker project:

packages/my-worker/
├── src/
│   └── index.ts
├── wrangler.toml
├── package.json
└── tsconfig.json

Worker Entry Point

// src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/api/search") {
      return handleSearch(request, env);
    }

    return new Response("Not Found", { status: 404 });
  },
};

ExecutionContext and Background Processing

The third argument to fetch is ExecutionContext, which provides waitUntil() for running async work after the response is sent:

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext,
  ): Promise<Response> {
    // Return response immediately
    const response = buildResponse(request, env);

    // Run non-critical work in the background
    ctx.waitUntil(logAnalytics(request, env));
    ctx.waitUntil(updateCache(request, env));

    return response;
  },
};

This is critical for:

  • Webhook handlers — Slack, GitHub, etc. require responses within 3 seconds. Process the webhook payload after responding.
  • Logging and analytics — Don’t block the user’s response for non-critical writes.
  • Cache warming — Update caches after serving a response.

⚠️ waitUntil does not extend CPU time

ctx.waitUntil() keeps the Worker alive after the response is sent, but does not increase the CPU time limit. Long-running operations may still be terminated.


## Deployment

Deploy with `wrangler deploy` (not `wrangler pages deploy`):

```bash
npx wrangler@4 deploy

In CI, deploy from the worker’s directory:

- name: Deploy Worker
  working-directory: packages/my-worker
  run: pnpm run deploy
  env:
    CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

Path-Triggered Deploys

For monorepos with multiple workers, trigger deploys only when the worker’s files change:

on:
  push:
    branches:
      - main
    paths:
      - 'packages/my-worker/**'
  workflow_dispatch:  # Allow manual triggers

This avoids unnecessary deploys when other parts of the monorepo change.