XDP · eBPF · Rust

The L4 load balancer forwarding plane that lives in the kernel.

equinox validates and load-balances traffic inside the NIC driver with XDP — before a packet ever reaches your host stack. No userspace proxy in the hot path, no extra hop, no per-connection overhead.

XDP
Runs in the driver, before SKB allocation
0 ms
Downtime on reload — atomic buffer switch
650
Backends per buffer via Maglev hashing
DSR
Direct Server Return — no return path

Core features

Everything you need to put a kernel-speed forwarding plane in front of your services.

In-kernel validation

Truncated packets and illegal TCP flag combinations (NULL, SYN+FIN, SYN+RST, FIN+RST, XMAS) are dropped at the NIC. Unrelated traffic — SSH, everything off your listen ports — passes straight through, untouched.

Maglev load balancing

Consistent hashing with a 65,537-entry table keeps flows pinned to backends even as the set changes. Packets are forwarded via DSR with XDP_TX — only the MAC is rewritten, no userspace round trip.

Zero-downtime hot reload

Routing tables are double-buffered. The control plane fills a standby buffer and flips a single atomic value to switch over. The XDP program is never detached; traffic never stops.

Abuse protection

Per-source-IP sliding windows count requests and malformed packets. Cross the limit and the source is blacklisted in-kernel for five minutes — all of it happening in the data plane, not after the fact.

Active health checking

Backends are TCP-probed every reload. A crashed instance is evicted from the table within a cycle and re-added automatically when it recovers. No traffic black-holed.

Plug-and-play DSR

Direct Server Return means the load balancer is never in the return path — backends reply straight to the client. For Docker, equinox automatically injects the VIP into each container's loopback. For bare metal, a one-line script does it. Zero backend changes.

Opt-in observability

Set one address and get Prometheus /metrics — routed and dropped-by-reason counters, backend gauges — plus a /healthz readiness probe. Off by default, zero setup to skip it.

Native XDP, with fallback

Attaches in native driver mode where the NIC supports it (Intel, Mellanox, virtio-net) and falls back to SKB mode everywhere else. One config line to force a mode.

Why it's fast

A traditional load balancer copies every packet into userspace, makes a decision, and copies it back. equinox decides inside the NIC driver.

  • Before the stack. The XDP hook runs before skb allocation — the kernel hasn't paid for the packet yet.
  • DSR — no return path. Only the MAC is rewritten; the backend replies directly to the client. The load balancer carries zero connection state.
  • No proxy in the path. Routing is a hash lookup and a MAC rewrite, then XDP_TX straight back out the wire.
  • No connection state. Maglev means consistent routing without tracking every flow, so memory and CPU stay flat under load.

Quickstart

Run it in Docker (recommended) or directly on the host — pick a path and drop a config.yaml next to it. Edit the file live to hot-reload.

terminal
# 1. Clone the repo
$ git clone https://github.com/typicallhavok/equinox
$ cd equinox

# 2. Create your config (required — no bundled default)
$ cp config.example.yaml config.yaml   # then edit it

# 3. Recommended — compose runs the forwarding plane + demo backends
$ docker compose up --build

# …or run the published image directly, mounting your config
$ docker pull typicallhavok/equinox:latest
$ docker run --rm --network host --privileged \
    -v "$(pwd)/config.yaml:/app/config.yaml" typicallhavok/equinox:latest

A config at /app/config.yaml is required — the container exits with an error if it's missing. The interface is auto-detected from the default route (override with -e IFACE=eth0), and --cap-add NET_ADMIN SYS_ADMIN BPF works in place of --privileged on most kernels.

Configuration

A single YAML file, read on start and re-read on every edit. It's required — there's no bundled default. Only gateway and discovery are mandatory: point discovery at a Docker network (strategy: docker) or list backend IPs (strategy: static). For every field, see the full configuration reference.

config.yaml
gateway:
  listen_ports: [80, 443]   # ports to load-balance; all else passes through
  vip: "10.0.0.100"          # Virtual IP for DSR
  xdp_mode: "auto"          # auto | skb | drv | hw

discovery:
  strategy: "docker"       # static | docker | dns
  sync_interval_ms: 3000   # re-discover this often
  drop_unmatched: false    # drop vs. pass when no backend
  network: "equinox_backends"
  # static_routes: [{ ip: "172.18.0.10", port: 3000 }]

protection:               # per-source-IP abuse limits
  enabled: true
  rate_limit_per_sec: 5000
  malformed_limit: 20
  window_ms: 1000
  block_duration_secs: 300  # blacklist for 5 min

health_check:             # evict dead backends
  enabled: true
  timeout_ms: 500

# observability:             # opt-in; omit to keep off
#   metrics_addr: "0.0.0.0:9100"
Setting Default What it does
listen_ports 80, 443 Ports intercepted & validated; everything else passes straight to the host.
xdp_mode auto Attach mode. auto tries native and falls back to SKB.
strategy Where backends come from: static, docker socket, or dns.
sync_interval_ms 3000 How often discovery re-runs to pick up scaled or crashed instances.
drop_unmatched false Drop validated packets with no backend instead of passing them.
protection.enabled true In-kernel per-source rate & malformed limiting with timed blacklist.
block_duration_secs 300 How long a tripped source stays blacklisted.
health_check.enabled true TCP-probe backends each reload; only healthy ones get traffic.
metrics_addr off Set it to expose Prometheus /metrics + /healthz.

Put the forwarding plane in front of your services.

Linux + Docker. Apache-2.0 control plane, GPL-2.0 data plane.