Skip to content

Networking & IP versions ​

Hoppla's monitoring workers make outbound connections from inside your cluster. A monitor can only check what your cluster's pod network can actually reach. That one sentence explains most surprises on this page.

Nothing here is required for a normal install. Read it if you want IPv6 monitoring, or if checks behave oddly on an overlay network.

What Hoppla needs ​

RequirementWhy
Outbound TCP 80/443 to the internetHTTP/uptime, SSL, redirect, sitemap and crawl checks
Outbound TCP/UDP 53DNS checks resolve against authoritative nameservers directly
Outbound TCP 43Domain-expiry checks (WHOIS)
Outbound TCP 587Notification email, if you use your own SMTP
Outbound ICMPPing checks

If you enable the bundled network policies (networkPolicy.enabled=true), these are already allowed. Set networkPolicy.backend to cilium if you run Cilium, or standard for any other CNI.

IPv4-only clusters: nothing to do ​

This is the default and it is fully supported. Most clusters are IPv4-only, including a default GKE cluster, an EKS cluster in IPv4 mode, and a default kind cluster.

Hoppla detects that it has no IPv6 egress and handles it honestly:

  • Monitors on IPv4-reachable hosts work normally — the overwhelming majority of hosts.
  • A monitor whose host is IPv6-only is recorded as untestable, not down. That means: visible in results, excluded from uptime %, and no alert is sent.

That last point is deliberate. Reporting a host as "down" when the truth is "we could not test it from here" would be a false alarm, and an IPv6-only monitor would otherwise alert on every single check forever.

Enabling IPv6 monitoring ​

Only worth doing if you monitor IPv6-only hosts, or you want to catch a fault that affects one stack only on a dual-stack host. That second case is the interesting one: a host can be fine over IPv6 and broken over IPv4, which a browser hides (it silently prefers the working path) but real visitors on the broken path do not.

Your cluster's pod network must have working IPv6 egress. Per-monitor behaviour is then controlled by the IP version setting on each uptime check:

SettingBehaviour
Automatic (default)Check every address family DNS advertises. Single-stack hosts get one check; dual-stack hosts get both, so a one-stack outage is reported as a partial outage naming the broken stack.
IPv4 onlyOnly ever check over IPv4.
IPv6 onlyOnly ever check over IPv6.

Automatic costs an extra request only for dual-stack hosts, so the added load scales with how many of your monitored hosts publish AAAA records — not with your total monitor count.

Platform notes ​

On GKE and EKS, the IP family is fixed when the cluster is created and cannot be changed afterwards. If you want IPv6 monitoring, decide before you create the cluster.

GKE — dual-stack is available on new Standard clusters (1.24+) and Autopilot (1.25+), and requires VPC-native networking (Alias IPs) plus GKE Dataplane V2. Create with --stack-type=ipv4-ipv6 and --ipv6-access-type, on a dual-stack subnet. It cannot be enabled on an existing cluster.

Amazon EKS — set with --ip-family at creation and immutable thereafter. Note that EKS does not support dual-stack pods at all: a cluster is either IPv4 or IPv6, never both. In IPv6 mode, pods are IPv6-only and reach IPv4 destinations through NAT64/DNS64 translation — see Known limitation below, which affects Hoppla directly.

kind (local) — add networking: {ipFamily: dual} to your cluster config. Be aware that IPv6 inside the cluster does not imply IPv6 egress to the internet: that depends on the Docker host having working IPv6, which is often not configured by default. Good for testing the setting, not for concluding your IPv6 monitoring works.

Your own infrastructure — you need three things: nodes with a routable IPv6 address and default route, a CNI configured for IPv6, and network policies that permit IPv6 egress. All three, or IPv6 silently fails; see troubleshooting below.

Verifying what your cluster can reach ​

Run this from a worker pod. It is the fastest way to know where you stand:

bash
POD=$(kubectl get pods -n hoppla -l app.kubernetes.io/component=worker-uptime -o name | head -1)

# Does the pod even have an IPv6 address?
kubectl get $POD -n hoppla -o jsonpath='{.status.podIPs}{"\n"}'

# Can it actually reach IPv6 and IPv4?
kubectl exec -n hoppla $POD -c worker -- sh -c '
  curl -6 -sS -o /dev/null -w "IPv6: %{http_code} via %{remote_ip}\n" --max-time 10 https://ipv6.google.com/
  curl -4 -sS -o /dev/null -w "IPv4: %{http_code} via %{remote_ip}\n" --max-time 10 https://www.google.com/'

# What Hoppla itself concluded
kubectl exec -n hoppla $POD -c worker -- php artisan tinker --execute='
  echo app(\App\Services\IpFamilyEgress::class)->supports("v6") ? "IPv6 detected" : "IPv6 absent";'

The dangerous state is a mismatch: Hoppla reporting "IPv6 detected" while curl -6 fails. Hoppla decides from the presence of a global IPv6 address on the pod, so a cluster that assigns addresses without working egress will make it attempt IPv6 and record genuine failures — turning a safe "untestable" into false alerts. If you see that, set PROBE_IPV6_EGRESS=false on the uptime workers to force IPv6 off until the cluster is fixed.

Troubleshooting ​

IPv6 checks fail even though pods have IPv6 addresses ​

Almost always one of two things, and they look identical from the application. If you run Cilium, its drop counters tell you which — read the reason, not the total:

bash
CILIUM=$(kubectl get pods -n kube-system -l k8s-app=cilium -o name | head -1)
kubectl exec -n kube-system $CILIUM -c cilium-agent -- \
  sh -c 'cilium-dbg metrics list | grep drop_count'
  • Unsupported L3 protocol — the CNI has no IPv6 datapath (for Cilium, enable-ipv6=false). It discards IPv6 in both directions, which also kills IPv6 neighbour discovery, so the node cannot even resolve its gateway. Check with ip -6 neigh show dev eth0: a gateway stuck at INCOMPLETE or FAILED is this.
  • Policy denied — the datapath works, but a network policy only permits IPv4. This is the sneaky one: addresses, routing and neighbour discovery all look healthy right up to the first request. If you wrote your own policies, egress rules restricted to 0.0.0.0/0 need ::/0 alongside them.

Take the same two-step approach on any CNI: first confirm the datapath carries IPv6 at all, then confirm policy permits it.

Slow or stalling transfers on an overlay network ​

Symptom: things work but feel slow, large responses occasionally stall, and the nodes are fragmenting packets. Check the counters on a node:

bash
kubectl exec -n kube-system $CILIUM -c cilium-agent -- awk '
  /^Ip: Forwarding/ { for (i=1;i<=NF;i++) x[$i]=i; getline;
    print "FragCreates", $x["FragCreates"], "ReasmFails", $x["ReasmFails"] }' /proc/net/snmp

Sample twice, a minute apart. FragCreates climbing means packets are being split up; ReasmFails climbing means some are being lost and re-sent. On a healthy overlay both stay flat.

The cause is an MTU that does not account for encapsulation. If your CNI uses an overlay (VXLAN, Geneve, IP-in-IP), every pod packet gets wrapped, and the wrapper needs room:

pod MTU  =  MTU of the interface the overlay actually traverses  −  encapsulation overhead

VXLAN and Geneve each add 50 bytes; IP-in-IP adds 20; WireGuard more.

The trap is the first term. The overlay may not use your primary interface. If nodes tunnel over a separate or private network, that interface's MTU is what counts — and it is frequently lower than 1500. Check every candidate rather than assuming:

bash
kubectl exec -n kube-system $CILIUM -c cilium-agent -- \
  sh -c 'for d in /sys/class/net/*/mtu; do echo "$(dirname $d | xargs basename): $(cat $d)"; done'

Reference values worth knowing: Google Cloud VPC defaults to 1460 (so a VXLAN pod MTU of 1410), AWS VPC supports 9001 inside the VPC but drops to 1500 over VPN and 8500 over inter-region peering, and several private-network offerings run 1450. After changing the MTU, restart pods — the value is applied when a pod's interface is created, so running pods keep the old one.

Known limitation: NAT64/DNS64 clusters ​

Hoppla's per-family checking assumes native dual-stack. It does not currently understand NAT64/DNS64 translation, which is how an IPv6-only cluster reaches IPv4 destinations — notably EKS in IPv6 mode.

On such a cluster, an IPv4-only target resolves to both a real A record and a synthesised NAT64 AAAA. Hoppla plans both legs: the IPv4 leg fails because the pod has no IPv4 stack, while the IPv6 leg succeeds through the translator. The result is a partial outage reported for a host that is actually fine.

Workaround: on an IPv6-only cluster, set the IP version of your uptime checks to IPv6 only. Everything then goes through the translator as intended and results are accurate.

Native dual-stack clusters and IPv4-only clusters are unaffected.