One binary · written in C · Caddy-style config

The reverse proxy that
routes on what’s inside
the request.

limer.sh is an ultra-fast HTTP server and load balancer that distributes traffic by JSON fields, form fields, body regexes, headers, cookies, the path, or the client address — not just the URL. One binary, a readable Limerfile, automatic HTTPS, HTTP/2 and gRPC.

Get started → ★ Star on GitHub $ git clone && make
Limerfile
example.com {
    # Same customer → same upstream, chosen by a field in the JSON body
    route orders {
        match method POST
        match json type == order refund
        reverse_proxy 10.0.1.10:8080 10.0.1.11:8080 {
            lb_policy hash json customer.id
        }
    }

    # Everything else: least-connections with active health checks
    reverse_proxy 10.0.0.10:8080 10.0.0.11:8080 10.0.0.12:8080 {
        lb_policy least_conn
        health_uri /healthz
    }
}
Payload-based distribution

Balance on the request, not just the URL.

Most proxies can only see the method and path. limer.sh looks inside the request and picks a handler or an upstream from its contents. lb_policy hash json customer.id sends every request for a customer to the same backend with weighted rendezvous hashing — so when an upstream leaves, only its keys get remapped.

Match with == != ~ !~ > < ^= *= exists missing, AND matchers together, OR their values, and negate any of them.

  • json
  • form
  • body regex
  • header
  • cookie
  • query
  • path
  • host
  • method
  • remote_ip
  • tls_client
  • split %
shard by tenant
:8080 {
    route events {
        match path /events/*
        match json event == signup purchase
        to :9001 :9002 :9003
        lb_policy hash json tenant
    }

    # 5% canary — sticky per user
    route {
        match split 5 cookie session
        reverse_proxy canary.internal:8080
    }

    reverse_proxy :9001 :9002 :9003
}
Everything in one binary

A full edge, nothing to assemble.

limer.sh works like Caddy: automatic certificates, zero-downtime reloads, an admin API — plus the payload routing and balancing that Caddy lacks.

Payload routing & hashing

Route and balance on JSON, form, body, header, cookie, query, path or client IP. Rendezvous hashing keeps keys stable across upstream changes.

Automatic HTTPS

Let’s Encrypt over http-01, tls-alpn-01 or dns-01 with wildcards. Renews itself. tls internal for local dev.

HTTP/2 & gRPC

HTTP/2 both sides via h2c:///h2://, trailers intact. Unary and bidirectional streaming gRPC pass straight through.

Smart load balancing

Round robin, least-conn, random, IP/URI hash, sticky cookies — with weights, active health checks and passive ejection.

Caching & rate limits

In-memory LRU cache honouring Cache-Control, ETag and stale-while-revalidate. Token-bucket rate limits keyed by any placeholder.

Static files & compression

file_server with ranges, try_files and directory browsing. gzip, zstd and brotli, streamed and negotiated.

Zero-downtime reloads

Hot reload on SIGHUP or the admin API. A bad config is rejected and the old one keeps running. In-flight requests finish cleanly.

Dynamic upstreams

Pools from DNS A/AAAA or SRV records, re-resolved on an interval. Mutual TLS, traffic mirroring and canary splitting built in.

Ops you can see

Prometheus metrics, JSON upstream status, runtime drain and weight overrides that survive reloads, multi-process workers with SO_REUSEPORT.

Performance

An epoll data path in C — about 3–5× Caddy.

Same 4-vCPU VM, same two backends, same round_robin policy and an enlarged keep-alive pool, driven by wrk. HTTP/1.1 reverse-proxy throughput:

ScenarioCaddy v2.10limer.shRatio
1 core · GET round robin · 100 conns9,200 req/s32,000 req/s3.5×
1 core · POST 120 B JSON8,600 req/s31,000 req/s3.6×
1 core · GET · 1000 conns7,400 req/s26,000 req/s3.5×
4 cores · GET round robin · 100 conns23,500 req/s103,000 req/s4.4×
4 cores · POST 120 B JSON20,500 req/s90,000 req/s4.4×
4 cores · GET · 1000 conns22,500 req/s115,000 req/s5.1×

~5 KB per active connection · one worker can hold ~500,000 connections in ~2.5 GB · reproduce it all with bench/run.sh

Quickstart

Running in under a minute.

No runtime, no modules to install — just libc and whatever optional libraries you have. Linux (epoll).

  1. Build it

    Clone and make. Optional libs (OpenSSL, nghttp2, zstd, brotli) are auto-detected; make install drops it in /usr/local/bin.

  2. Write a Limerfile

    One directive per line, blocks in braces — the Caddyfile syntax you already know, with import, snippets and {$ENV}.

  3. Run & reload

    limer run in the foreground (perfect under systemd). limer reload swaps config with zero downtime; limer validate checks it first.

try it locally
# build
git clone https://github.com/limer-sh/limer
cd limer && make

# spin up three test backends
python3 tests/backend.py 9001 shard-a &
python3 tests/backend.py 9002 shard-b &
python3 tests/backend.py 9003 shard-c &

# route by payload
./limer run -c examples/payload-routing.Limerfile

curl -X POST localhost:8080/echo \
     -d '{"tenant":"acme","event":"signup"}'