Skip to content
Tech News
← Back to articles

Zeroserve: A zero-config web server you can script with eBPF

read original more articles

Disclaimer: This article is co-authored with GPT-5.5 and Claude Opus 4.8.

zeroserve is a small, fast, zero-config HTTPS server. You hand it a tarball of a website and it serves it - over HTTP/2 and TLS 1.3, with hot reload and a tiny resident footprint. The twist is that you can drop eBPF programs into the tarball and they run on every request, in userspace, as sandboxed middleware - rewriting, authenticating, and rate-limiting requests, or reverse-proxying them to a backend when you want it to act as a gateway in front of your app.

In short:

Fast : on one core it beats nginx across most workloads - small and large static files, scripted middleware, and small-response proxying, all over HTTPS.

: on one core it beats nginx across most workloads - small and large static files, scripted middleware, and small-response proxying, all over HTTPS. Efficient eBPF scripting : scripts are JIT-compiled to native code and sandboxed in userspace, cheap enough to run on every request.

: scripts are JIT-compiled to native code and sandboxed in userspace, cheap enough to run on every request. Program-as-configuration : your eBPF program is the whole configuration, deciding what happens to each request.

: your eBPF program is the whole configuration, deciding what happens to each request. io_uring throughout : every network and disk operation is submitted through io_uring .

: every network and disk operation is submitted through . Modern TLS in the box : TLS 1.3, HTTP/2, Encrypted Client Hello, SNI certificate selection, and JA4 fingerprinting.

: TLS 1.3, HTTP/2, Encrypted Client Hello, SNI certificate selection, and JA4 fingerprinting. Simple to operate: serve a whole site from one tarball and hot-reload it (and the TLS material) with a SIGHUP .

It's meant to be an alternative to nginx and Caddy, and the design bet is about configuration. Those servers give you a declarative config language - location blocks, rewrite rules, map directives, try_files - and then, once the declarative language hits its limits, an optional scripting runtime bolted on the side (Lua, or Caddy's plugins). Behavior ends up split across two layers: directives that quietly grow their own control flow, plus scripts that run somewhere in the request lifecycle you have to keep in your head.

... continue reading