Edge workers run your code within 50ms of every user on the planet. That power comes with risk: a bad deploy replicates instantly to 300+ PoPs. This guide shows a production-grade pipeline to build, test, and ship CDN edge functions safely — with sub-second rollbacks and zero global downtime.
1. Why Edge Deployment Is Different
Unlike origin deployments, edge workers are distributed, stateless, and constrained. You get 1-128 MB memory, 5-50ms CPU limits, and no filesystem. Deploys are not rolling restarts — they are atomic version swaps across the entire Anycast network. A memory leak that would slowly degrade an origin server will instantly degrade every edge node.
Three properties define a safe edge pipeline:
- Deterministic builds: Identical source must produce identical bundles. Lock dependencies, pin your runtime version, and bundle offline.
- Environment parity: Your local runtime must match production's V8/workerd isolate. Don't test with Node.js.
- Progressive delivery: Never go 0 → 100%. Use traffic splitting and automatic rollback on error budget burn.
If you are new to edge performance tradeoffs, start with CDN benefits for performance and security to understand where workers fit versus caching strategies and origin logic.
2. The Deployment Pipeline
A mature edge worker pipeline has five stages. Each stage gates the next, and artifacts flow forward without rebuilds.
| Metric | Edge Processing | Origin Processing | Improvement |
|---|---|---|---|
| Latency (p50) | 8–15 ms | 120–300 ms | 15–20x faster |
| Cache Hit Ratio | 85–95% | N/A | Reduces origin load |
| TLS Handshake | 1–5 ms (nearby PoP) | 50–150 ms | 10–30x faster |
| Bandwidth Cost | $0.01–0.04/GB | $0.05–0.12/GB | 3–5x cheaper |
| Availability | 99.99% (multi-PoP) | 99.9% (single DC) | 10x fewer outages |
3. Build: Bundle for the Edge
Never deploy raw TypeScript or unbundled node_modules. Edge runtimes execute a single script. Bundling reduces cold starts, enforces size limits, and eliminates dynamic require surprises.
Recommended toolchain
- esbuild for speed and deterministic output. Set
target: es2022andformat: esm. - Wrangler / workerd or Fastly Compute toolchain as your deploy CLI. Pin the version in package.json.
- Size budget: Fail CI if gzipped bundle exceeds 500 KB for Cloudflare Workers or 1 MB for Fastly/CloudFront Functions.
name = "edge-router"
main = "dist/worker.js"
compatibility_date = "2026-09-01"
compatibility_flags = ["nodejs_compat"]
[build]
command = "npm run build"
[limits]
cpu_ms = 10
[observability]
enabled = true
head_sampling_rate = 0.1
[placement]
mode = "smart"
src/worker.ts — edge entry with caching
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
let res = await cache.match(request);
if (res) return res;
// Stale-while-revalidate at the edge
res = await fetch(request, { cf: { cacheTtl: 60, cacheEverything: true } });
if (res.ok) {
ctx.waitUntil(cache.put(request, res.clone()));
}
return res;
}
} satisfies ExportedHandler<Env>;
Build in CI with npm ci --ignore-scripts and cache the dist/ artifact. That artifact is what you test and promote — never rebuild between stages. For image-heavy workloads, pair workers with image optimization at the edge to keep CPU under budget.
4. Test: Three Layers of Confidence
Edge workers fail in ways unit tests miss: KV eventual consistency, cache eviction, and subrequest limits. You need three layers.
Layer 1 — Unit tests (fast, isolated)
Mock fetch, caches, and KV. Use Vitest with @cloudflare/vitest-pool-workers so tests run inside workerd, not Node.
Layer 2 — Integration tests against local runtime
Run Miniflare or workerd locally. Test real routing, header mutations, and cache key strategies without network calls. Example assertion: request to /api/* bypasses cache, /static/* hits cache.
Layer 3 — Preview deployment + synthetic traffic
Deploy every pull request to pr-1234.edge-preview.workers.dev with isolated KV namespaces. Run Playwright or k6 against the preview URL. Check performance metrics like Time to First Byte and cache hit ratio before merging.
name: edge-deploy
on: [push]
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: 'npm' }
- run: npm ci
- run: npm run build
- run: npm test -- --pool=workers
- name: Deploy preview
if: github.ref != 'refs/heads/main'
run: npx wrangler deploy --env preview --branch ${{ github.head_ref }}
- name: Canary to production
if: github.ref == 'refs/heads/main'
run: npx wrangler deploy --env production --strategy canary
Gate merges on bundle size, CPU time (p95 < 10ms in tests), and Lighthouse Core Web Vitals delta. A 100ms regression in LCP should block promotion.
5. Ship: Progressive Delivery and Rollback
Atomic global deploys are dangerous without traffic shaping. Use version aliases and gradual rollouts.
| Strategy | How It Works | Rollback Time | Best For |
|---|---|---|---|
| Instant Swap | New version replaces old globally at once | 30–60s (redeploy) | Low-risk config changes |
| Canary 5/50/100 | Route 5% traffic to new version, auto-promote on health | <5s (alias switch) | All production code changes |
| Blue/Green Alias | Two live versions, switch alias via API | <2s | Critical paths, checkout flows |
| Request Header Split | Internal users get new version via header | Instant (header removal) | Dogfooding, QA validation |
For e-commerce and high-traffic sites, canary is mandatory. See CDN for e-commerce for patterns on session affinity during canaries. Implementation varies by provider:
- Cloudflare: Use
wrangler versions upload+wrangler versions deploy --percent 5. Keep two previous versions. - Fastly Compute: Create new service version, activate with condition
randombool(5,100), then promote. - AWS CloudFront Functions / Lambda@Edge: Publish new function version and update distribution with traffic weight via CodeDeploy.
Automate health checks: if error rate >0.5% or p95 latency +20% for 2 minutes, automatically shift alias back. Do not require human approval for rollback — require it for promotion.
6. Observability After Ship
You cannot debug what you cannot see. Edge logs are sampled and delayed, so instrument before you need it.
- Structured logs: Emit JSON with
requestId,colo,cpuTime, andcacheStatus. Useconsole.logwith Tail Workers or logpush to R2/S3. - Metrics: Track
worker_cpu_ms,subrequest_count,kv_read_latency, anderror_ratioper version. Alert on version-level anomalies, not global averages. - Tracing: Propagate
traceparentfrom edge to origin. Sample 1% in production, 100% in preview. - Real User Monitoring: Correlate edge version with Core Web Vitals. A canary that improves TTFB by 40ms but hurts INP will not show in synthetic checks.
Set dashboards to compare canary vs. stable side-by-side. If canary shows higher 5xx or KV timeouts in a single colo, halt promotion — it may be a regional capacity issue, not code.
7. Security and Performance Guardrails
Edge workers have privileged access to every request. Enforce guardrails in CI:
- Secrets: Never bundle secrets. Use
wrangler secret putor environment bindings. Scan bundles for leaked keys withtrufflehog. - CPU and memory: Fail builds that exceed 80% of limit in load tests. A worker that passes at 5 RPS may OOM at 500 RPS per isolate.
- Subrequest limits: Cloudflare caps at 50 subrequests; Fastly at similar thresholds. Mock and count them in tests.
- WAF integration: Run workers after WAF, not before, unless you intentionally handle bot filtering at the edge.
Performance tip: move image resizing and A/B logic to the edge, but keep heavy computation at the origin. Edge CPU is 5-10x more expensive per millisecond than origin CPU.
Done right, edge worker deployment feels like shipping to localhost — fast, safe, and reversible. Build once, test in workerd, canary with metrics, and keep rollbacks instant. Your users will never notice a deploy, which is exactly the point.