---
title: "5 Reasons Your Next Deploy Should Be 145 KB of Wasm, Not Docker"
date: 2026-07-15T08:00:00+00:00
author: "poornerd"
tags: ["wasm", "howto"]
canonical: https://www.poornerd.com/2026/07/15/wasm-not-docker.html
source: Raw Markdown twin of the HTML article; content is the original source.
---
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.

