Back to journal
wasm

5 Reasons Your Next Deploy Should Be 145 KB of Wasm, Not Docker

Yesterday I did an experiment: I took this blog — a plain Jekyll site that has been deployed over SFTP to shared hosting for years — and made it serve through a WebAssembly binary running server-side on Cloudflare Workers.

The entire “server” is about 80 lines of Rust, compiled to wasm. Gzipped, the binary is 145 KB. It handles everything my Apache .htaccess used to do: the canonical-host 301 redirect, six security headers on every response, DirectoryIndex resolution, trailing-slash redirects, and the 404 page. The images and CSS never touch the wasm at all — Cloudflare’s static assets binding serves them straight from the CDN, free and unlimited.

I went in expecting a fun toy. I came out convinced this is how I want to deploy a whole class of services. Five reasons.

1. The artifact is absurdly small

The deployable unit is 145 KB gzipped — smaller than most header images on this blog. A Docker image shipping the same routing logic drags along a base image, a distro, and a web server: tens to hundreds of megabytes that aren’t your code. A wasm module is just compiled logic against a standard runtime interface; the runtime is the platform’s problem.

2. You test the exact artifact you ship

wrangler dev runs the same workerd runtime locally that Cloudflare runs at the edge — same wasm binary, same bindings, up in milliseconds. My acceptance script (twelve curl checks: header parity, byte-identical HTML, redirect status codes) runs unchanged against localhost and the deployed URL. No image build, no registry round-trip, no environment drift.

3. The CDN does the heavy lifting — for free

The 10 MB worker limit forces the right architecture anyway: logic in the binary, bytes on the CDN. This blog’s ~74 MB of images, CSS, and fonts are served by Cloudflare’s static assets binding — edge-cached, zero egress fees, not counted against the worker’s request quota. The wasm only wakes up for redirects, headers, and 404s.

4. The economics are embarrassing

$0 per month: 100,000 worker requests/day free, static assets unlimited, no bandwidth charges; $5/month past that. And because wasm instantiates in milliseconds, scale-to-zero has no cold-start penalty — the problem you engineer around with containers doesn’t exist here.

5. It’s real server logic, not a static-hosting hack

The worker does what Apache did via mod_rewrite and mod_headers — canonical 301s, security headers, 404s — in 80 lines of typed, testable Rust instead of .htaccess regex. The same shape carries a real backend: routing, auth, database and upstream calls in one small binary. And with WASI it isn’t vendor-locked — the same module runs on wasmtime, Fastly, or Spin.

The honest caveats

It wasn’t friction-free, and I’d rather you hit these in this paragraph than at midnight:

  • Toolchain sharp edges. worker-build silently requires a recent worker crate version; enabling LTO in the release profile broke wasm-bindgen with a cryptic “externref table” error. Both fixable in minutes once diagnosed, both annoying to diagnose.
  • Platform defaults fight parity. Cloudflare’s default URL handling 307-redirects .html URLs to extensionless ones — nice for a new site, wrong when you need byte-parity with an existing Apache setup. One config line (html_handling = "none") and a few lines of Rust fixed it.
  • The 10 MB limit is real. Wasm-on-Workers is for logic, not payloads. If your service is mostly moving large files, this isn’t your architecture.

Takeaway

A container ships a machine; a wasm module ships a function. For services whose job is logic rather than bulk data — redirects today, APIs tomorrow — the wasm version is orders of magnitude smaller, tests as the identical artifact you deploy, starts in milliseconds, and rides a free CDN for everything heavy.

The blog was the toy problem. Over the next weeks I want to push actual backend logic through this pipeline — a real service with state and upstream calls — and see where the model bends. If it holds, I struggle to justify a Dockerfile for small services again.

Follow Brian Porter on LinkedIn →
← Previous
Brian Porter

Written by

poornerd

CTO at an automotive data company in Munich. Co-founder of SiteForce AG. Four decades writing software and shipping production systems.

Keep reading

Related essays