Skip to content

Cloudflare Workers: Not Lambda, Not Containers — It's V8 Isolates

Mar 27, 2026 1 min
TL;DR Cloudflare Workers uses V8 Isolates instead of containers — no cold starts, global edge deployment, and direct access to D1, R2, KV, and AI via Bindings. Great for APIs, SSR, and lightweight backends; not suited for CPU-heavy work.
Table of Contents
  1. V8 Isolates vs. Containers
  2. Limitations Up Front
  3. The Simplest Possible Worker
  4. Building an API with Hono
  5. The wrangler CLI
  6. The Bindings System
  7. The Billing Model
  8. How NobodyClimb Uses Workers
  9. When to Use Workers — and When Not To
  10. Changelog
  11. References

🌏 中文版

Cloudflare Workers is often compared to AWS Lambda, but the two have fundamentally different architectures. Lambda runs in containers; Workers runs in V8 Isolates. That single difference shapes everything.

V8 Isolates vs. Containers

Lambda's cold start problem is essentially the cost of container startup: pulling an image, allocating resources, booting the runtime — best case a few hundred milliseconds, worst case several seconds.

Workers uses V8 Isolates — the same engine that runs JavaScript in Chrome. Isolates are memory-isolated from each other but share the same V8 engine, so there's no new process or container to spin up. When Cloudflare claims "no cold starts," it is not marketing but a consequence of the architecture — the official phrasing is that this model "eliminates the cold starts of the virtual machine model" and is "around a hundred times faster than a Node process". (The widely repeated "0–5ms startup" figure has no traceable source on the current docs, so it is not cited here. Note too that the limits page imposes a 1 second startup-time ceiling for global scope.)

The other key difference is where code runs. Lambda executes in the AWS region you choose; Workers is automatically deployed across Cloudflare's global network — described officially as thousands of machines spread across hundreds of locations. A request from Taiwan gets handled at a nearby edge node — no round-tripping across the globe.

Limitations Up Front

Workers isn't a silver bullet — but the constraint that actually stops you is not the one most people expect. The limit is CPU time, not wall-clock time.

  • CPU time: counts only time the CPU spends executing your code. Waiting on fetch() or a D1 query does not count. 10ms on the free plan; on paid, the default is 30 seconds and can be raised to a 5-minute ceiling in configuration
  • Wall time: an incoming HTTP request has no hard limit while the client stays connected; waitUntil() extends execution for a while after the response is sent. Cron Triggers, Queue consumers, and Durable Object alarms are the invocation types with minute-scale wall-time caps
  • Memory: 128 MB per Worker, identical on both plans
  • Worker size: free and paid have different bundle-size ceilings; an over-fat bundle fails to deploy
  • Subrequests: there is a cap on outbound requests per invocation, much tighter on free; paid can raise it in configuration
  • No full native Node.js API surface: fs, child_process and friends are out, though most pure-JS packages work fine

The exact numbers move. Workers Limits is the only source worth trusting — it now carries separate tables for CPU time and wall time, and is worth reading end to end.

The Workers runtime is not full Node.js. Built-ins like node:crypto and node:buffer come through the Node.js compatibility layer and require the nodejs_compat compatibility flag.

The Simplest Possible Worker

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

    if (url.pathname === "/") {
      return new Response("Hello from the edge", { status: 200 });
    }

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

env is where Bindings come in — more on that below.

Building an API with Hono

Writing a full API with raw fetch handlers gets tedious fast — you end up rolling your own routing. Hono is a Web framework designed specifically for edge runtimes: tiny bundle size, native support for the Workers API.

import { Hono } from "hono";
import { cors } from "hono/cors";

type Bindings = {
  DB: D1Database;
  KV: KVNamespace;
  AI: Ai;
};

const app = new Hono<{ Bindings: Bindings }>();

app.use("*", cors());

app.get("/api/posts", async (c) => {
  const posts = await c.env.DB.prepare(
    "SELECT id, title, created_at FROM posts ORDER BY created_at DESC LIMIT 20"
  ).all();

  return c.json(posts.results);
});

app.post("/api/posts", async (c) => {
  const body = await c.req.json<{ title: string; content: string }>();

  const result = await c.env.DB.prepare(
    "INSERT INTO posts (title, content) VALUES (?, ?)"
  )
    .bind(body.title, body.content)
    .run();

  return c.json({ id: result.meta.last_row_id }, 201);
});

export default app;

c.env gives you type-safe access to your Workers Bindings, with full IDE autocomplete.

The wrangler CLI

wrangler is Cloudflare's official developer tool, covering everything from project initialization to production deployment.

Start new projects with npm create cloudflare@latest — it installs wrangler as a project devDependency and scaffolds the config. Invoke it day to day as npx wrangler <command>; a global install is discouraged because the version then drifts from the project. The commands you will actually use are wrangler login, wrangler dev (run locally), wrangler deploy, and wrangler tail (live logs). Full list: Wrangler commands.

wrangler dev runs a real Workers runtime locally. Bindings (D1, KV, R2) default to local emulation and can be pointed at real Cloudflare resources instead.

The configuration file now comes in JSON and TOML flavours, and Cloudflare recommends wrangler.jsonc for new projects — some newer Wrangler features are JSON-only:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-api",
  "main": "src/index.ts",
  // Set this to the date you started the Worker
  "compatibility_date": "2026-08-18",
  "compatibility_flags": ["nodejs_compat"],
  "d1_databases": [
    { "binding": "DB", "database_name": "my-db", "database_id": "<DATABASE_ID>" }
  ],
  "kv_namespaces": [{ "binding": "KV", "id": "<NAMESPACE_ID>" }],
  "ai": { "binding": "AI" },
  "observability": { "enabled": true }
}

wrangler.toml still works — the schema is identical, only the syntax differs. Field reference: Wrangler configuration.

The Bindings System

Bindings are how Workers connects to Cloudflare services. They're injected via the env object — no API keys required, no network overhead (inside a Worker, a Binding is a direct runtime connection).

Key Bindings:

BindingPurpose
D1DatabaseSQLite relational database
KVNamespaceKey-value store, ideal for caching
R2BucketObject storage, S3-compatible
AiWorkers AI — embeddings and LLMs
QueueMessage queue for async job processing
DurableObjectNamespaceStateful single-instance objects
FetcherService binding to call another Worker

Cloudflare KV is eventually consistent — global reads are fast but writes have propagation delay. Cloudflare R2 is the right choice for static assets and user-uploaded media, with no egress fees.

The Billing Model

Rate cards rot, so here is only the shape: Workers bills on two axes — request count and CPU milliseconds. Time spent waiting on network I/O is not billed, and that is the biggest structural difference from Lambda (billed in GB-seconds, waiting included). A Worker that mostly calls external APIs and computes little is disproportionately cheap here.

The free plan has a daily request cap; the paid plan has monthly included allowances with usage-based overage. D1, KV, R2, and Workers AI each carry their own free allowance and their own billing axes, counted separately. Current numbers: Workers Pricing.

The free plan covers most side projects and small-to-medium applications.

How NobodyClimb Uses Workers

NobodyClimb runs its entire backend on Workers with no traditional server:

  • Web SSR: Next.js 15 deployed to Workers via @opennextjs/cloudflare — dynamic routes handled by Workers, static assets served via Cloudflare Assets
  • API: A Hono Worker connected to D1 (user data, climbing records) and KV (caching, rate limiting)
  • AI: The RAG Pipeline also runs in Workers — embeddings and LLM calls go through env.AI via Workers AI

The core reasoning: a climbing community platform has unpredictable traffic. Running a persistent server wastes money when traffic is low. Workers bills per request — idle time costs nothing, and when traffic spikes, it responds fast from the nearest edge node worldwide.

When to Use Workers — and When Not To

Good fits for Workers:

  • REST APIs, GraphQL endpoints
  • SSR with Next.js, Nuxt, or similar adapters
  • Edge middleware (auth, redirects, A/B testing)
  • Webhook handlers
  • Lightweight scheduled tasks (via Cron Triggers)

Poor fits for Workers:

  • Heavy CPU-bound workloads (ML training, image or video transcoding) — the hardest constraint; the CPU ceiling stops you outright
  • Anything whose working set exceeds 128 MB of memory
  • Packages that depend on native Node.js addons (some database drivers, .node binaries)
  • Anything requiring a local filesystem (fs is unavailable)

Two things are no longer reasons to avoid Workers: long-lived WebSocket connections have Durable Objects (no wall-time cap while the caller stays connected), and long-running processes have Workflows. They are a different mental model, not a capability Workers lacks.

Compared to traditional VPS or container setups: what Workers gives up is how much CPU and memory a single request may burn. What you get back is zero infrastructure management, global deployment, and billing that charges only for actual computation. Small units of work belong on Workers; large ones belong in containers.

Changelog

  • 2026-08-19: Fact-checked against primary sources and refreshed; perishable details handed back to official docs. Added to the "Cloudflare Edge Stack" series.

References