Oh My LoadBalancer

Your whole production edge.
One binary. One YAML file.

Reverse proxy, automatic HTTPS, health-checked failover, per-IP abuse protection, and VRRP self-HA — the stack that's usually nginx + certbot + keepalived + fail2ban, as a single memory-safe Rust binary built on Cloudflare's Pingora.

Rust 1.80+ built on Pingora 0.8 single static binary Apache-2.0
The pitch, in one breath

Nobody's production edge is just a proxy

Run one seriously and count the daemons: a proxy, a cert renewer on a cron, a failover manager, a ban list scraping your logs — plus the glue keeping their configs in agreement at 3 a.m.

nginxcertbot + cronkeepalivedfail2banglue scripts
omlb config.yaml

omlb collapses that whole column into one Rust process and one file — no GC pauses, no C footguns, and a --check that proves the config is sound before it ships.

What's in the box

Everything the edge needs, nothing bolted on

L7 proxy on Pingora

HTTP/1.1 and HTTP/2 with TLS termination, SNI routing, and pooled upstream connections. Restarts hand off sockets gracefully, so deploys drop nothing.

Automatic HTTPS

Certificates issue themselves, renew themselves, and slide into the live cert store mid-flight. You will not miss certbot.

Real load balancing

Weighted round-robin, random, two-choices least-conn, or Ketama consistent hashing — chosen per pool, weighted per backend.

Failover that fails over

Active probes catch dead backends. Passive outlier detection catches dying ones mid-traffic. Retries make sure clients never have to care.

Edge security gate

Rate limits per IP, instant bans for anyone touching /.env or wp-login.php, optional 404-strike bans — all judged before routing even runs.

VRRP self-HA

Two nodes, UDP heartbeats, one floating IP. The master dies, the backup takes the address in seconds — keepalived's job, retired.

Live configuration

One schema-checked YAML with env overrides on top. Edit it and the change swaps in atomically mid-traffic — or gets refused whole.

Response caching

Per-route opt-in: cacheable GETs answer from a bounded in-memory LRU and never wake the upstream — or the filter chain.

L4 (TCP) proxying

Raw TCP when you need it — IRC, Postgres, anything — with TLS termination or TLS-to-backend as one-flag options.

Managed pools

Hand a pool a Docker label and it keeps N replicas healthy, replacing containers that hang while alive — the failure a restart: policy never sees.

Pluggable filters

Six native Rust filters for the always-on work, plus sandboxed WASM plugins you can load at runtime when you need something custom (experimental).

Observability

Prometheus metrics, structured JSON or journald logs, an admin API, and a built-in dashboard — all reading counters off the hot path.

Every feature, with its config →

Configuration

Readable in one sitting, validated before it ships

  • Automatic HTTPS with hot cert swap
  • p2c load balancing + health-checked failover
  • Scanner bans, inline, before routing
  • IPv4 and IPv6, one flag
  • omlb --check in CI — a typo'd key is a hard error
  • Hot reload with a connection-preserving atomic swap

That's the entire config for an HTTPS edge with failover and abuse protection. See every mode →

omlb.yaml
tls:
  acme:
    enabled: true
    contacts: ["mailto:you@example.com"]
    terms_of_service_agreed: true
    domains: [example.com]

listeners:
  - { name: https, address: "0.0.0.0:443", tls: { managed: true }, http2: true, ipv6: true }

upstreams:
  app:
    algorithm: power_of_two
    backends: [{ addr: "10.0.0.1:8080" }, { addr: "10.0.0.2:8080" }]
    health_check: { kind: http, path: /healthz }

routes:
  - { name: app, match: { host: example.com }, upstream: app }

security:
  enabled: true
  rate_limit_rps: 100
  ban_paths: [/.env, /.git/, /wp-login.php]
Honest numbers

Where it's fast — and where nginx still wins

Loopback micro-benchmarks, CPU-pinned, median of 3 runs. They measure proxy overhead, not your network — ratios are the point. Full methodology →

L7 throughput & latency

targetreq/sp50p99
direct to backend~97,0000.51 ms2.5 ms
omlb~62,0000.94 ms3.0 ms
nginx~78,0000.60 ms3.7 ms

nginx leads raw RPS by ~25% — a decade-tuned C proxy should. omlb lands in the same order of magnitude with sub-millisecond added median latency and a slightly tighter p99, while carrying features nginx needs three extra daemons for.

Failover & cache

scenarioresult
VRRP: kill master → backup owns VIP~1.5 s
cache HIT vs proxied~137k vs ~92k req/s
native filter overhead~14% of baseline
WASM plugin, pooled isolation~61% of baseline

Failover detection is bounded by dead_multiplier × advert_interval — the same knob keepalived exposes. Cache hits are decoupled from the backend entirely; with a real origin the gap is far larger.

Realistic expectations

What it can't do (yet)

Young project, clearly marked edges — better you read them here than discover them mid-incident.

  • ACME is HTTP-01 only — wildcards need DNS-01, which isn't there yet — bring your own wildcard cert
  • No HTTP/3 (QUIC) — HTTP/1.1 and HTTP/2 are the menu
  • No mTLS client auth — TLS authenticates the server, not the client, for now
  • No live service discovery — backends come from your config, not from Consul, DNS-SRV, or Kubernetes
  • Literal matching — exact hosts and path prefixes — the regex engine you might want isn't here
  • Per-instance ban state — each HA node keeps its own ban table; they don't gossip
  • Topology changes need a restart — a graceful one — but refused on hot reload rather than half-applied
  • No distributed tracing yet — metrics and logs today; OpenTelemetry spans aren't wired up

If the goal is retiring the classic four-daemon HTTPS edge, none of these will stop you. If one of them would, pick something else honestly →

Proven in production

Running the edge at ananaslabs.eu — today

12 domains with Let's Encrypt certificates that issue and renew themselves — the cron count is zero
4 stacks a Matrix homeserver, a mail stack, IRC over L4 TLS, and a pile of web apps behind one YAML file
0 fail2ban processes left running — the gate eats the .env/.git scanner flood inline

It replaced a Caddy deployment outright — and the page you're reading came through it.

One binary. One YAML. --check. Ship.