Introducing Oh My LoadBalancer
Ask anyone who runs their own servers what their “edge” actually is, and you’ll get a list: nginx doing the proxying, certbot and a cron entry keeping certificates alive, keepalived standing by for failover, fail2ban chewing through logs looking for scanners — and usually some glue script that exists only because the other four don’t know about each other. Five things to install. Five configs. Five things that can page you.
omlb is our attempt to make that list one item long. A single memory-safe binary reads a single YAML file and gives you L7 routing, Let’s Encrypt certificates that look after themselves, health-checked failover, per-IP rate limiting with automatic scanner bans, and VRRP self-HA with a floating IP — built on Pingora, the proxy engine Cloudflare wrote to replace nginx at their own edge. No GC pauses, no C footguns, no daemon zoo. And --check proves the config is sound before anything ships.
What’s actually in it
This isn’t a routing-only proxy with a marketing page full of “coming soon.” What ships today:
- L7 HTTP/1.1 + HTTP/2 proxying on Pingora — TLS termination, SNI routing, pooled upstream connections, and graceful restarts that hand off sockets instead of dropping them.
- Automatic HTTPS — certificates issue on boot, renew on schedule, and swap into the live SNI store mid-flight. Nothing to cron, nothing to reload.
- Real load balancing — weighted round-robin, random, two-choices least-conn, or Ketama consistent hashing, chosen per pool.
- Failover that actually fails over — active TCP/HTTP probes plus passive outlier detection that ejects a backend answering 5xx before the next probe, with retries onto healthy backends so clients never see the failure.
- A security gate ahead of routing — per-IP token buckets, instant bans for anyone touching
/.envor/wp-login.php, optional strike bans on repeated 404s, and a CIDR allowlist so it never bites your own infra. - VRRP self-HA — two nodes, UDP heartbeats, one floating IP. The master dies; the backup takes the address in seconds.
- Live configuration — one schema-validated YAML with
OMLB_*env overrides, hot-reloaded atomically on save. Structural changes are refused whole rather than half-applied. - And the rest — per-route response caching, raw L4 TCP proxying, self-healing managed Docker pools, native and WASM filters, Prometheus metrics and structured logs. Same binary, same file.
Several of those lines are exactly where the classic stack sprouts extra daemons, paid tiers, or separate modules — certificates, active health checks, failover, bans. The features page has the exact YAML that turns each one on.
Honest numbers
We benchmark against nginx because it’s the natural peer, and we publish the loopback results as-is — including the parts where nginx wins. On an AMD Ryzen 7 PRO 8840U, 4 proxy workers pinned to 4 cores, concurrency 64:
nginx pushes ~78,000 req/s against a direct-to-backend ceiling of ~97,000; omlb lands at ~62,000 req/s — roughly 0.8× nginx, about what you’d expect from a young Rust proxy against a decade-tuned C one. Where it’s competitive is latency: omlb adds well under a millisecond of median latency over the direct backend, and its p99 (3.0 ms) is actually slightly tighter than nginx’s (3.7 ms) in the stable runs. VRRP failover — kill the master, backup takes the VIP — lands around 1.5 s, bounded by the same dead_multiplier × advert_interval knob keepalived exposes.
That’s the whole honest read: nginx wins raw throughput; omlb is in the same order of magnitude with a comparable-to-tighter tail, while carrying failover, ACME, and abuse protection that nginx needs three extra daemons for. If the only number you care about is single-box RPS, decade-tuned C proxies still win that drag race — see the comparison page and full methodology in bench/ for the rest.
What it can’t do yet
We’d rather you find out here than after a bad deploy. As of this release, omlb:
- Speaks ACME only over HTTP-01 — wildcards need DNS-01, so a wildcard cert is bring-your-own (it will serve, but not renew itself).
- Has no HTTP/3 — HTTP/1.1 and HTTP/2 are the menu.
- Offers no mTLS client auth — TLS authenticates the server, not the client, for now.
- Does no live service discovery — backends come from the config and resolve at boot or reload, not from a DNS-SRV or Kubernetes watch.
- Matches literally — exact hosts, path prefixes, exact headers and methods. The regex engine you might be reaching for isn’t there.
- Keeps ban state per node — HA peers don’t share the security gate’s ban table.
- Requires a graceful restart for topology changes — new listeners, backends, or pools are refused on hot reload rather than half-applied, deliberately.
- Has no distributed tracing — metrics and structured logs today; OpenTelemetry spans aren’t wired up.
- Ships plugins as experimental — the always-on logic you’d actually want (auth, rate limiting, header rewrites) is built in natively instead.
For the job this was built for — one HTTPS edge that used to take four daemons — none of those gaps matter. If one of them does matter to you, the comparison page will point you at Traefik or Envoy without any hard feelings.
Running it for real
omlb isn’t a demo. It runs the edge at ananaslabs.eu today, where it replaced a Caddy deployment outright: twelve domains on real Let’s Encrypt certificates, fronting a Matrix homeserver, a mail stack, IRC over L4 TLS, and a pile of web apps — host and path routing, static responses, gzip, well-known delegation, one config file. The security gate has been quietly eating the .env/.git/wp-login scanner flood the whole time, with zero fail2ban processes anywhere. This site — the page you’re reading — comes through it too.
Want to try it? The quick start in the docs gets a minimal HTTPS edge running in about five minutes: build the binary, write one YAML file, --check it, run it.