Skip to content

RTFM · Healthchecks

lb.saphira.udp: is the UDP listener bound?

UDP has no handshake, so this check posts a datagram and listens: if the network answers "no such port" (ICMP unreachable) the listener is absent, and if the service replies, the reply must match what you expect.

Saphira Linux dragon mascot

What it proves

Layer 4, connectionless. The check opens a connected UDP socket to real:$4, sends one datagram, and reads the result. The kernel turns an ICMP port-unreachable or host-unreachable return into a local error; that is the discrimination: a bound listener accepts the datagram silently (or replies), an unbound port makes the host itself answer "nothing here".

A service that accepts the datagram and stays silent cannot be distinguished from a firewall that dropped it: both end in a timeout, and timeout is unhealthy by design. This check suits UDP services that reply to a probe (DNS, NTP, QUIC, syslog-with-ack); for strictly fire-and-forget flows, use sendexpect-udp or the ipproto check in accept mode and read their honest semantics.

How it works

Probe engine again: saphira-lb-probe udp. When LB_SAPHIRA_EXPECT is set, the first reply must match the regex (matched against raw bytes, latin-1, dot-matches-newline); without it, any reply, even an empty one, is a pass.

Configuration

Knobs
VariableDefaultMeaning
LB_SAPHIRA_SEND-Optional text payload to send (an empty datagram when unset)
LB_SAPHIRA_EXPECT-Regex the reply must match (raw-bytes match; e.g. DNS/NTP response markers)
LB_SAPHIRA_TIMEOUT3Seconds to wait for the verdict

Wiring it up

ldirectord
# /etc/ha.d/ldirectord.cf
virtual = 203.0.113.10:53
    protocol = tcp
    scheduler = wlc
    real = 192.168.20.53:53 gate 1
    checktype = external
    checkcommand = "/var/lib/lb/saphira/lb.saphira.udp"
HAProxy
# haproxy.cfg
global
    external-check

backend app_pool
    mode tcp
    option external-check
    external-check command "/var/lib/lb/saphira/lb.saphira.udp"
    server app1 192.168.20.53:53 check inter 5s fall 3 rise 2

For DNS specifically, prefer the dns check: it verifies transaction ID, response bit and RCODE rather than any random reply.

Run it by hand

Manual run
LB_SAPHIRA_SEND='hello' /var/lib/lb/saphira/lb.saphira.udp 21 514 192.168.20.60 514
echo $?
# 0 = datagram accepted (or reply matched)
# 1 = ICMP unreachable, timeout, or a reply that failed the expect

Failure modes

  • Immediate exit 1 with refusal: ICMP port-unreachable: definitively nothing bound
  • Timeout: silent listener, silent firewall, or a service that does not answer this payload
  • Reply did not match; wrong service on the port, or expect regex too strict

Did we miss something?

If this page left something unanswered, found an error, or there is another subject you would like documented, tell us. Saphira’s documentation grows from real problems people need to solve.

Send feedback or request a new section →

Prefer not to do it yourself?

Everything needed to do the work yourself is documented here and remains free; we charge for human time, not for withholding knowledge. Sometimes the missing resource is simply time. The same people who build Saphira can provide paid professional help with implementation, migration, troubleshooting and administration.

Ask about professional support →