Compose HA: three hosts, one VIP
The ha-static shape of docs/architecture.md §4.4 and §6.5: every host runs
kudurru, dnsdist and keepalived from deploy/compose/ha/node-{a,b,c}.
clients ──▶ VIP 10.0.0.53 (keepalived, VRRPv3 unicast, auth_hmac)
│
┌───────────────────┼────────────────────┐
node-a .11 node-b .12 node-c .13
dnsdist :53 ──PROXYv2──▶ kudurru :5353 on every node (each dnsdist lists all three)
- keepalived decides only who holds the VIP:
state BACKUP,nopreempt, unicast peers,auth_hmac. Its track script probes the local dnsdist on :53 over UDP and TCP and requires at least one healthy backend through the dnsdist API, so a node whose kudurru is down but whose dnsdist is healthy keeps serving through the other nodes' kudurru. - dnsdist forwards with PROXYv2 and the shared TLV secret, so every kudurru logs the real client address and applies client groups correctly.
- kudurru runs the same file-managed configuration on every node.
kudurru.yamlandobjects.d/are distributed by your own tooling (git, ansible, rsync); the UI is read-only forkudurru.ioobjects in this shape.
The kudurru-dnsdist and kudurru-keepalived images (supervisors
kudurru edge run and kudurru vip run) are milestone M4 deliverables.
Prepare every host
Replace the example addresses (10.0.0.11-13, VIP 10.0.0.53/24,
eth0) in the threedocker-compose.ymlfiles:--backends,--peers,--vipandKUDURRU_LISTENERS__PROXYPROTOCOLFROM(exact/32s of the three hosts, never the LAN prefix).Create the secrets once and copy the same files to every host:
mkdir -p secrets openssl rand -base64 32 > secrets/encryption_key # shared: sessions, internal TLS and token openssl rand -base64 18 > secrets/admin_password openssl rand -hex 32 > secrets/proxy_secret # PROXYv2 TLV 0xE0, at least 32 bytes openssl rand -hex 16 > secrets/vrrp_hmac # keepalived auth_hmac chmod 0600 secrets/*Put the TLS certificate for DoT/DoH (the same pair on every node) into
certs/tls.crtandcerts/tls.key.Copy
node-ato the first host,node-bto the second,node-cto the third, and start them:docker compose up -d.
Check
dig @10.0.0.53 example.com # through the VIP
dig @10.0.0.12 -p 5353 example.com # kudurru on node-b directly (no PROXYv2 header: attributed to the peer)
docker compose exec keepalived kudurru health --vrrp
curl -s http://10.0.0.11:8081/healthz | jq
(kudurru lameduck [5s] drains the local node: run it with
docker compose exec kudurru kudurru lameduck 5s before stopping the
container; kudurru lameduck --cancel undoes it.) Stop kudurru on the VIP holder: the VIP stays, its dnsdist sends the
queries to the other two nodes. Stop dnsdist (or the host): the VIP moves
within fall 3 seconds. kudurru lameduck drains a node from every
dnsdist's pool without moving the VIP; kudurru vip drain moves it (D51).
Changing configuration
Edit kudurru.yaml or objects.d/ in your source of truth and distribute it
to all three hosts. Objects re-apply on file change; keys flagged R in
kudurru config schema need docker compose restart kudurru (one host at a
time). Zone serials are max(prev+1, unix_now) on each twin, close but not
identical: external secondaries should transfer from one named node.
Upgrading
One host at a time: docker compose pull && docker compose up -d, wait for
kudurru health --ready, then the next host. See
runbooks.md.