async python load balancer / HTTP1,2,3 / Python 3.15.0t / debian trixie slim
1.4K
An async HTTP(S) load balancer and reverse proxy built on Quart. It fronts a pool of backends with round-robin or sticky routing, health checks with a circuit breaker, per-IP rate limiting, a libmodsecurity-powered WAF, URL rewriting, Prometheus metrics, and a live management web UI.
Source & full docs: https://github.com/rackitio/apython_lb
| Tag | Description |
|---|---|
latest, X.Y.Z, X.Y | Built from the corresponding vX.Y.Z release tag in the source repo |
Images are multi-arch (linux/amd64, linux/arm64) and rebuilt only on
tagged releases — latest always tracks the newest release, never an
unreleased commit.
The container serves TLS directly and expects /app/cert.pem and
/app/key.pem. Generate a self-signed pair for local testing (bring a
CA-issued cert/key for anything real):
mkdir -p certs
openssl req -x509 -newkey rsa:2048 -keyout certs/key.pem -out certs/cert.pem \
-days 365 -nodes -subj "/CN=localhost"
docker run --rm -p 8443:443/tcp -p 8443:443/udp \
-v "$PWD/certs/cert.pem":/app/cert.pem:ro \
-v "$PWD/certs/key.pem":/app/key.pem:ro \
<dockerhub-namespace>/apython_lb:latest
curl -sk https://localhost:8443/health
Then register a backend through the management API:
curl -sk -X POST https://localhost:8443/v1/manage/configs \
-H "Content-Type: application/json" \
-d '{
"name": "my-backend",
"proto": "https",
"host": "example.com",
"ips": "1.2.3.4,5.6.7.8",
"hc_path": "/healthz"
}'
Route a request to it by adding @apython_lb(backend_name="my-backend", ...)
to a Quart route — see the source repo for route decorator examples
(rate limiting, sticky sessions, URL rewriting).
| Port | 443/tcp (HTTP/1.1 and HTTP/2 over TLS) and 443/udp (HTTP/3 over QUIC) — there is no plain-HTTP listener |
/app/cert.pem, /app/key.pem | TLS certificate and key — required, container will not start without them |
/app/data | SQLite database (apython_lb.db) holding backend configs; mount a volume here to persist configs across restarts |
Set these as container environment variables. Defaults shown are the ones baked into the image.
| Variable | Default | Description |
|---|---|---|
MODSECURITY_ENABLED | true | WAF on by default; set false to disable |
APP_SECRET_KEY | (auto-generated) | Sticky-session signing key — set explicitly in production, or sessions won't survive restarts/replicas |
MANAGE_BASIC_AUTH | (unset) | user:password for HTTP Basic auth on /v1/manage, /ws/configs, /metrics — on top of the built-in internal-IP restriction |
LOG_LEVEL | INFO | INFO = access logs only; DEBUG = all app logs |
RATE_LIMIT_REQUESTS / RATE_LIMIT_WINDOW_SECONDS | 100 / 60 | App-wide rate limit |
HEALTH_CHECK_INTERVAL | 60 | Seconds between backend health checks |
DNS_NAMESERVERS | 1.1.1.1 | Comma-separated resolvers for backend hostname lookups |
LB_MAX_ATTEMPTS | 3 | Retries per request before giving up |
HYPERCORN_WORKERS | 1 | Worker processes sharing the bound ports (SO_REUSEPORT) — each is a fully independent process with its own state |
HYPERCORN_WORKER_CLASS | asyncio | asyncio or uvloop. Not trio — the app's SQLite layer requires asyncio's event loop, so trio isn't installed |
The full variable reference (20+ options covering IP tracking, sticky pinning, TLS verification, and WAF tuning) is in the project README linked above.
/v1/manage, /ws/configs, and /metrics are restricted to internal
(RFC1918/loopback) source IPs by default. If you front this container with
another proxy, every client collapses into that proxy's IP — set
MANAGE_BASIC_AUTH in that case.X-Forwarded-For is deliberately
not trusted. Run this container as the first hop from clients.pytest as part of its own build (a dedicated Docker
test stage in the source repo) and only publishes if that passes.Base image: rackitio/pythont, a
free-threaded CPython 3.15.0 (--disable-gil) build on debian:trixie-slim
— no official Python image ships a free-threaded variant yet — plus
libmodsecurity-dev for the WAF
engine (loaded via ctypes, no compiler needed at runtime). Served over
HTTP/1.1, HTTP/2, and HTTP/3 via Hypercorn, optionally scaled with
HYPERCORN_WORKERS/HYPERCORN_WORKER_CLASS (uvloop available alongside
the asyncio default). PYTHON_GIL=0 keeps free-threading on even though
Hypercorn's HTTP/3 dependency (aioquic) hasn't declared itself GIL-safe —
see the source repo's Dockerfile and README for why that's safe here,
for both worker-class options. The test build stage and its
dependencies are stripped from the published image — this is a lean
production image.
See the source repository for license terms and to file issues or contribute.
Content type
Image
Digest
sha256:8224ec63f…
Size
77.2 MB
Last updated
2 days ago
docker pull rackitio/apython_lb