A Cloudflare Workers Alternative: When You Need Containers, Not Isolates

Cloudflare Workers runs V8 isolates at the edge; Inquir runs full Node, Python, and Go containers. Where the isolate ceiling bites, what containers change, and how to choose.

A Cloudflare Workers Alternative: When You Need Containers, Not Isolates

Isolates and containers: two honest bets on the word “serverless”

“Serverless” hides an architectural fork. Cloudflare Workers runs your code as a V8 isolate — the same lightweight sandbox mechanism that keeps browser tabs apart — replicated across Cloudflare’s global edge. Inquir Compute runs your code as a container: a full Node.js 22, Python 3.12, or Go 1.22 process with a real runtime underneath it. Both bill per use, both hide servers, both let you deploy a function without provisioning a box. Underneath, they are almost nothing alike.

That difference is the whole story of this comparison. If you are shopping for a cloudflare workers alternative, the question is rarely “which serverless is better.” It is “does my workload fit inside an isolate, or does it need a container?” Get that right and the rest — latency, cold starts, native modules, pricing shape — follows.

This is a fair comparison, not a takedown. Workers is genuinely excellent at what it was designed for, and for a large class of edge work it is the correct choice. But the isolate model has a ceiling, and when you hit it you feel it as a wall, not a slope. The goal here is to name that ceiling precisely, show what container-backed serverless changes, and give you an honest decision guide — including the cases where you should close this tab and stay on Workers.

What the V8 isolate model buys you — and where the ceiling sits

Start with why Workers is good, because the strengths and the limits are the same design decision viewed from two sides.

A V8 isolate is not a container and not a virtual machine. It is a sandbox inside a shared runtime process. Thousands of isolates share one process, and Cloudflare spins one up in front of a request in a fraction of a millisecond. Two consequences fall out of that:

  • There is effectively no cold start. An isolate has almost nothing to boot — no OS, no container image to pull, no language runtime to initialize per request. Startup is close to instant.
  • It runs everywhere. Because isolates are cheap, Cloudflare can run your code in hundreds of locations close to users. A request is handled at a point of presence near the client, not at a single origin region.

Those two properties are hard to beat, and no container platform matches them. That is the honest good news for Workers.

The price of that model is what you are allowed to do inside the isolate. To keep isolates cheap and safe to co-tenant by the thousand, the runtime deliberately withholds most of an operating system:

  • No native modules. An isolate cannot load a native addon — a .node binary, a shared library, anything compiled against libc. Pure JavaScript, TypeScript, and WebAssembly are in; native code is out.
  • No arbitrary binaries or subprocesses. You cannot shell out to ffmpeg, spawn a child process, or invoke a CLI. There is no process table to spawn into.
  • No real filesystem. There is no durable local disk to write to and read back.
  • A restricted API surface. You get Web-standard APIs — fetch, Web Crypto, streams — plus an opt-in Node.js compatibility layer that covers a growing subset of Node’s built-ins. It is genuinely more than it used to be, but it is a subset, and it does not include the ability to run native code.
  • Bounded CPU and memory. Each request gets a capped slice of CPU time and a small, fixed memory budget per isolate. Great for short, sharp handlers; unforgiving for anything heavy.

None of this is a bug. These are the v8 isolates limits by design — the same constraints that make the platform fast and globally cheap. The trouble only starts when your code needs one of the things on that withhold list.

When the ceiling actually bites: native deps, heavy libs, binaries, ML

Here is where teams go looking for a workers alternative. The pattern is almost always the same: a dependency your business already relies on assumes a real operating system, and the isolate refuses to load it.

Concrete offenders, all common in production Node and Python code:

  • Image and media processing. sharp wraps libvips; ffmpeg is a binary you shell out to. Both are native. Neither fits an isolate.
  • Password hashing and crypto with native builds. bcrypt, argon2, and various crypto libraries ship native addons for speed.
  • Headless browsers and rendering. Puppeteer or Playwright driving Chromium is a subprocess controlling a large native binary — nowhere near isolate territory.
  • Database drivers with native bindings. Plenty of clients compile against C libraries.
  • ML inference. Running an ONNX model, a scikit-learn pipeline, or anything through a native runtime is a container job. Edge platforms offer their own managed inference products, but bring-your-own native ML runtime is not what an isolate does.
  • Big dependency trees and language runtimes. Anything that expects Python 3.12 or a full Go toolchain, not a JavaScript sandbox with a compatibility shim.

There are workarounds on the edge — recompile a library to WebAssembly, swap sharp for a WASM image library, call an external service to do the native work. Sometimes those are fine, and it is fair to say Workers’ WASM support is real and useful. But WASM ports lag their native originals, carry their own size and performance costs, and do not exist for every library. When your handler is fundamentally about doing heavy, native, or binary work, you are fighting the platform. That is the signal to move that path off isolates.

What container-backed serverless changes

Container-backed serverless keeps the parts of the serverless deal you actually wanted — deploy a function, no servers to manage, per-use economics — and removes the isolate’s runtime restrictions.

On Inquir, each function is a real container running one of three managed runtimes: Node.js 22, Python 3.12, or Go 1.22. Because it is a genuine OS process, the withhold list from the isolate world simply does not apply:

  • Native modules load normally. sharp, bcrypt, native database drivers — if it installs on a normal server, it runs here. Heavy or shared dependencies can be mounted as layers (shared dependency catalogs for Node, Python, and Go) so you are not rebuilding a fat image on every deploy.
  • A full runtime and a real environment. A filesystem (a writable /tmp), the standard library, subprocess-friendly patterns — the things your libraries assume exist.
  • Mixed languages behind one gateway. A Python ML tool, a Go hot path, and a Node webhook processor can live in one workspace, each in its own container, all reachable through the same API gateway with per-route auth.
  • Origin-style and private-network work. Network egress is off by default as a security posture and enabled per function, so a container can be granted access to reach private services and databases — the kind of origin IO an edge isolate is deliberately far from.

The point of the whole exercise — a sharp thumbnail, a bcrypt hash, an ONNX inference (load the model once on cold start, CPU only, via an attached layer) — runs without a fight.

Two honesty checks, because container-backed is not magic:

  • Cold start is real, just hidden. A container has to start — prepare the image, boot the runtime — which is a container-startup cost, not an isolate’s near-zero. Inquir runs hot/warm pools (by default at least one and up to eight warm containers per function, recycled periodically) to keep a ready instance in front of traffic. That hides cold starts for warm paths; it does not make them zero. The first invoke, or the first after an idle window, pays real startup. Anyone who promises “no cold starts” on containers is selling.
  • It is not edge-distributed. Your container runs in a region, at an origin — not replicated across hundreds of points of presence next to every user. If your defining requirement is “handle the request 20 milliseconds from the user in Sydney and São Paulo simultaneously,” a regional container is the wrong tool and an edge isolate is the right one.

There is also a time budget: a single function or step runs 5 seconds by default and 15 minutes at most. Work that outlasts one request is expressed as a pipeline of chained steps rather than one unbounded function, with per-step retries and backoff and a dead-letter path on exhaustion. There is no exactly-once guarantee and no strict ordering — you keep handlers idempotent, the same discipline any honest queue asks of you.

Before and after: a thumbnail that will not fit in an isolate

The most common isolate wall is also the easiest to show. A thumbnail endpoint using sharp is a few lines of code that a V8 isolate cannot run, because sharp is backed by libvips — a native library.

On Workers, the import itself is the problem:

// Cloudflare Worker (V8 isolate)
import sharp from 'sharp'; // native addon — cannot load in an isolate

export default {
  async fetch(req) {
    const input = await req.arrayBuffer();
    // sharp needs libvips (a native binary); the isolate has no way to load it
    const thumb = await sharp(input).resize(320, 240).jpeg().toBuffer();
    return new Response(thumb, { headers: { 'content-type': 'image/jpeg' } });
  },
};

Your options on the edge are to find a WebAssembly image library with a different API, or to call an external service to do the resize. Both are real; both are detours around the fact that the isolate cannot run the library you already use.

In a container, the same dependency just works. Here is the equivalent Inquir function — the gateway invokes it with an API-Gateway-style event (httpMethod, path, headers, queryStringParameters, body as a string):

// Inquir function (Node 22 container)
import sharp from 'sharp'; // native addon loads normally

export async function handler(event) {
  const input = Buffer.from(event.body ?? '', 'base64');

  const thumb = await sharp(input)
    .resize(320, 240, { fit: 'cover' })
    .jpeg({ quality: 82 })
    .toBuffer();

  return {
    statusCode: 200,
    body: JSON.stringify({ bytes: thumb.length, format: 'jpeg' }),
  };
}

No WASM port, no external image service, no rewrite of your processing logic. The difference is not the handler shape — both are small functions taking a request and returning a response. The difference is that one runs on a full runtime that can load native code and the other cannot.

The honest trade: edge latency and instant cold start vs full runtime and native modules

Strip away the marketing and the edge functions vs containers decision comes down to a single trade you cannot buy your way out of.

Workers gives you proximity and instant starts, and asks you to live inside the isolate sandbox:

  • Code runs a few milliseconds from the user, in hundreds of locations.
  • Effectively no cold start.
  • Massive, cheap concurrency for short handlers, billed per request.
  • In exchange: JavaScript / TypeScript / WASM only, no native modules, restricted APIs, capped CPU and memory.

Containers give you a full runtime, and ask you to accept an origin address and a real startup cost:

  • Any native module, any heavy dependency, three languages, real subprocesses and filesystem.
  • Reach into private networks and origin databases.
  • In exchange: your code runs in a region, not at the edge; and cold start is container-startup, softened by warm pools but never zero.

Neither column is strictly better. They are answers to different questions. “How do I run trivial logic as close to the user as physically possible?” points at isolates. “How do I run a real, dependency-heavy backend without managing servers?” points at serverless containers. Most confusion about a workers alternative is really a workload that was quietly asking the second question while being run against the first tool.

When Cloudflare Workers is still the right choice

This is the section a fair comparison owes you. There are workloads where Workers is not just adequate but the best tool available, and no container platform — Inquir included — should tempt you off it.

Stay on Workers when:

  • The logic is small and edge latency is the point. Redirects, header rewriting, geo-routing, A/B and feature-flag routing, request normalization, signed-URL checks. Tiny handlers where the win is being close to the user.
  • You are caching, shielding, or fanning out at the edge. Sitting in front of an origin, serving cache hits, and collapsing load before it reaches you is exactly the isolate model’s home turf.
  • Your code is already isolate-native. Pure JavaScript, TypeScript, or WebAssembly, no native addons, no subprocesses, no private-network origin calls. If it fits the sandbox, the sandbox is fast and cheap.
  • Global concurrency of short requests dominates. Per-request pricing and hundreds of POPs are a genuinely great fit for spiky, high-volume, low-CPU traffic.

And crucially, the answer is often both, not either. Workers at the edge for caching, shielding, and lightweight routing; a container platform at the origin for the dependency-heavy APIs, webhook processors, scheduled jobs, and background work behind it. Two tiers that complement each other — the edge does what only the edge can, the origin does what needs a real runtime. Reaching for a workers alternative rarely means ripping Workers out; usually it means giving the heavy paths a home they actually fit.

Decision guide and the takeaway

A short field guide, no ceremony:

  • Native module, binary, subprocess, or heavy library? Container. This is the clearest signal — running native modules on serverless is precisely what isolates cannot do and containers do without thinking.
  • Python or Go, or a mix of languages? Container. Isolates are a JavaScript / WASM world.
  • Private-network or origin-database IO? Container.
  • Long-running or multi-step work past a single request? Container, expressed as chained pipeline steps within the 15-minute-per-step budget — not one unbounded function.
  • Tiny, latency-critical, global, isolate-native logic? Workers. Stay.
  • Caching, shielding, edge routing? Workers. Stay — and put the heavy tier behind it.

The honest framing: Cloudflare Workers is a superb edge-function platform, and the isolate model is why it is fast, global, and cheap for small handlers. That same model is a hard ceiling for native modules, heavy libraries, binaries, and real backend work. Container-backed serverless like Inquir Compute trades edge proximity and near-zero cold start for a full Node.js 22 / Python 3.12 / Go 1.22 runtime that loads native code, reaches private services, and runs mixed-language backends — with cold starts hidden by warm pools rather than eliminated, and long work chained into pipeline steps rather than promised as unbounded.

Pick by the workload, not the label. When you need isolates, Workers is hard to beat. When you need containers, that is exactly the wall an edge isolate was designed not to climb — and the right time to reach for a container-backed alternative.