Skip to content

Optional: keepalived VIP for seamless AdGuard DNS failover (server ↔ Pi) #62

Description

@ulises-c

Summary

Add an optional keepalived + Virtual IP (VIP) layer so LAN DNS fails over
seamlessly between the primary AdGuard (linux-server/adguard) and the secondary
AdGuard on the Pi (linux-pi/adguard).

This is a potential follow-up, not urgent. The current setup already survives a
full main-server outage — the Pi resolver is self-sufficient (public DoH upstreams,
host-networked :53, filtering active, decoupled from Tailscale/the server). The
only rough edge is client steering: the router hands out DNS as
[<server-ip> primary, <pi-ip> secondary], and secondary-DNS failover is not
deterministic — some clients keep hitting the dead primary until a timeout before
falling back. Verdict today: works-but-degraded.

Proposal

Introduce a floating VIP (a spare LAN address outside the DHCP pool, e.g.
<vip>) managed by keepalived/VRRP across both resolver hosts:

  • Primary host = MASTER (higher priority), normally holds the VIP.
  • Pi = BACKUP; claims the VIP within ~1–3s if the master's VRRP heartbeats stop.
  • A vrrp_script health check probes AdGuard on :53 so the VIP also moves when
    AdGuard itself dies while the host stays up (not just on a full crash).
  • Router DHCP then hands out only the VIP as DNS — no primary/secondary racing,
    so failover is invisible to clients.

Keeps the same AdGuard software on both ends, so filtering + split-horizon behavior
is preserved regardless of which node holds the VIP. LAN-only; tailnet HTTPS admin
access via the sidecars is unaffected.

Suggested shape (mirror existing per-service layout)

  • New linux-server/keepalived/ and a Pi counterpart under linux-pi/, each with a
    tracked keepalived.conf template + .env.example (VIP, interface, router id,
    auth pass, priority) and a :53 health-check script; real values in gitignored
    .env, placeholders committed.
  • Document the router change (hand out the VIP as the sole DNS) in the UPS/DNS
    runbooks and docs/.

Acceptance criteria

  • keepalived deployed on both resolver hosts; VIP held by the primary at rest.
  • Stopping AdGuard (or the whole host) on the primary relocates the VIP to the
    Pi within a few seconds; DNS keeps resolving + filtering via the VIP throughout.
  • Recovery: primary reclaims the VIP when it returns; no split-brain (matching
    virtual_router_id, distinct priorities, shared auth).
  • Router DHCP issues the VIP as the sole DNS; no real IPs/hostnames/tailnet
    names in any tracked file.

Notes / caveats

  • Requires both nodes on the same L2 subnet (they are) and VRRP allowed on the LAN
    (multicast 224.0.0.18, IP proto 112 — fine on typical home switches).
  • Overkill for most home LANs; the degraded secondary-DNS path usually self-recovers
    within seconds. This buys zero-hiccup failover, nothing more.
  • Alternative considered: make the Pi the sole DHCP DNS (moves the SPOF to the Pi) —
    rejected in favor of an active/backup VIP.

Context: verified DNS-resilience discussion during the PR #61 live-verification work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions