+ In this article, we document how we configured the server for the + Torrust Tracker Demo + to run a UDP tracker behind a floating IP on Ubuntu. +
++ The same approach applies to other cloud providers where floating IPs are called + static IPs, reserved IPs, or + elastic IPs. The naming changes, but the network behavior is the same. +
+ ++ Using floating IPs is a common strategy to isolate infrastructure from public endpoint + addresses. It lets you replace, resize, or rebuild the internal server while keeping the + same public DNS records and tracker announce URLs. +
++ For HTTP services this is usually straightforward. For UDP trackers, there is an extra + requirement: the response must come back from the same public IP that received the + request. If replies leave via another source IP, many clients treat it as a timeout. +
+ +udp://udp1.torrust-tracker-demo.com:6969/announce
+ 116.202.177.1842a01:4f8:1c0c:828e::1| Layer | +What must happen | +
|---|---|
| DNS | +UDP tracker domain resolves to dedicated floating IPs | +
| Firewall | +UDP port 6969 allowed on IPv4 and IPv6 | +
| Kernel routing | +Source policy routing for each floating IP | +
| Docker IPv6 | +ip6tables enabled and bridge network has IPv6 subnet | +
| NAT (IPv6) | +SNAT replies to the floating IPv6 for UDP/6969 | +
+ For every floating IP, add a source-based routing policy so replies use the matching
+ public address. On our server we persist this in /etc/netplan/60-floating-ip.yaml.
+
60-floating-ip.yaml)
+ rather than editing 50-cloud-init.yaml.
+
+ In our investigation, one blocker was firewall path behavior on IPv6. The server had
+ ufw in default deny mode, and UDP 6969 was not explicitly allowed.
+
+ Important nuance: with Docker, published ports on IPv4 are often reachable even when
+ ufw looks restrictive, because Docker installs its own NAT and forwarding
+ rules. That does not guarantee equivalent behavior for IPv6 in every setup.
+ For this reason, verify IPv4 and IPv6 paths separately instead of assuming both families behave
+ the same way.
+
+ Expected result includes both 6969/udp and 6969/udp (v6) as
+ ALLOW IN. Treat this as one control in a layered setup, not as the only
+ explanation for reachability.
+
+ Docker frequently handles IPv4 iptables automatically, but IPv6 behavior depends on daemon + settings and network topology. To keep IPv6 UDP handling predictable across restarts, + enable ip6tables in Docker. +
+ +Add it to /etc/docker/daemon.json, then restart Docker:
+ If the bridge network has no IPv6 subnet, containers only get IPv4 addresses. In that + case, native IPv6 UDP forwarding can fail. We solved this by enabling IPv6 in the Docker + network. +
+ ++ After enabling IPv6 inside Docker, replies can still leave with the primary IPv6 because + of MASQUERADE behavior. For floating IPv6 UDP endpoints, add an explicit SNAT rule. +
+ +dig A and dig AAAA.
+ + For external validation, we used + newTrackon + and the raw status page at newtrackon.com/raw. +
+ +The same server-side setup is useful across providers, even if naming differs.
+| Provider | +Typical name | +
|---|---|
| Hetzner | +Floating IP | +
| DigitalOcean | +Reserved IP | +
| AWS | +Elastic IP | +
| Linode/Akamai | +Static IP | +
+ To run a UDP tracker reliably behind floating IPs, you need more than DNS and a port + mapping. You need symmetric routing, correct IPv6 firewall behavior, container IPv6 + networking, and explicit SNAT when floating IPv6 is involved. +
++ This is exactly how we fixed the Torrust Tracker Demo deployment on Hetzner Ubuntu. In a + follow-up update, we can extend this article with packet-flow diagrams and + provider-specific adaptations for DigitalOcean, AWS, and Linode. +
+ ++ If you want broader context around this setup, these articles cover the full deployment + story, newTrackon requirements, and the demo infrastructure decisions. +
++ The following links were used during investigation and documentation. Local files are + listed with absolute paths exactly as provided. +
+