67 docs indexed

HAProxy

HAProxy configuration for TLS termination, round-robin load balancing across obleth pods, and streaming support.

HAProxy is the reference TLS terminator and load balancer for obleth. It ships in the Docker Compose stack under the edge profile. The Helm chart does not deploy HAProxy — on Kubernetes the same role is filled by a Service plus the chart's optional Ingress.

Docker Compose (edge profile)

cd deploy/docker
docker compose --profile edge up -d

The shipped image is haproxy:3.2. It starts on port 80 with the obleth data plane as the backend; TLS is not enabled by default.

Shipped configuration

This is deploy/docker/haproxy.cfg as it ships:

global
    log stdout format raw local0
    maxconn 50000

# Re-resolve backend hostnames at runtime via Docker's embedded DNS, so
# recreating the obleth container (new IP) does not leave HAProxy health-checking
# a dead address. Kubernetes Services track pod IPs natively, so this is a
# Compose-only concern.
resolvers docker
    nameserver dns 127.0.0.11:53
    resolve_retries 3
    timeout resolve 1s
    timeout retry  1s
    hold valid 10s

defaults
    mode http
    log global
    option httplog
    timeout connect 5s
    timeout client  300s    # must be >= OBLETH_UPSTREAM_TIMEOUT_SECS
    timeout server  300s
    # streaming responses: don't buffer
    option http-no-delay

frontend obleth_front
    bind *:80
    default_backend obleth_pods

backend obleth_pods
    balance roundrobin
    # one pod in compose; in prod this is the obleth fleet
    server obleth1 obleth:8080 check resolvers docker init-addr last,libc,none
    # server obleth2 obleth2:8080 check resolvers docker init-addr last,libc,none

TLS termination

The shipped frontend is plaintext :80 only, on the assumption that a managed edge load balancer terminates TLS in front of it. To make HAProxy the TLS edge instead, mount your certificate (PEM format, certificate + private key in a single file) and enable TLS on the frontend:

frontend obleth_front
    bind *:443 ssl crt /etc/ssl/private/obleth.pem
    bind *:80
    http-request redirect scheme https unless { ssl_fc }
    default_backend obleth_pods

For Docker Compose, mount the cert and publish 443:

haproxy:
  ports: ["80:80", "443:443"]
  volumes:
    - ./certs/obleth.pem:/etc/ssl/private/obleth.pem:ro

Streaming requirements

obleth uses HTTP/1.1 streaming for text/event-stream (SSE) responses. HAProxy must not buffer streaming responses. Key settings:

  • timeout client 300s and timeout server 300s (must be >= OBLETH_UPSTREAM_TIMEOUT_SECS)
  • option http-no-delay — forwards each chunk as it arrives instead of coalescing it
  • Do not enable compression — it buffers the response

Health check

HAProxy can health-check obleth pods using the /health endpoint:

backend obleth_pods
    option httpchk GET /health
    http-check expect string "ok"
    server obleth1 obleth:8080 check inter 5s fall 3 rise 2

The data plane's /health route needs no authentication and returns the literal body ok.

Stats page

The Compose edge profile also exposes HAProxy's built-in Prometheus exporter and human stats UI on :8404 (/metrics and /stats). The /stats page is protected with HTTP basic auth so it can't be browsed anonymously if the port is ever published:

frontend stats
    bind *:8404
    mode http
    http-request use-service prometheus-exporter if { path /metrics }
    stats enable
    stats uri /stats
    stats refresh 10s
    stats realm obleth\ stats
    stats auth ${HAPROXY_STATS_USER}:${HAPROXY_STATS_PASSWORD}

Set the credentials in deploy/docker/.env:

HAPROXY_STATS_USER=admin
HAPROXY_STATS_PASSWORD=...   # change from the dev example

The /metrics endpoint is left unauthenticated so Prometheus can scrape it; keep :8404 on the internal network and do not publish it publicly.

Admin API protection

The admin API (:9180) should not be exposed via HAProxy. Keep it on the internal network only and use Kubernetes NetworkPolicy or firewall rules to restrict access.