Skip to content

Commit cb1fd0b

Browse files
committed
docs: [#407] document IPv6 UDP tracker issue and link from post-provision README
1 parent 01d218a commit cb1fd0b

3 files changed

Lines changed: 164 additions & 0 deletions

File tree

docs/deployments/hetzner-demo-tracker/post-provision/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Steps performed after the tracker is running and during ongoing operations:
2020
| Step | Guide | Status |
2121
| --------------------------- | ---------------------------------------------------------- | -------------- |
2222
| 4. newTrackon Prerequisites | [newtrackon-prerequisites.md](newtrackon-prerequisites.md) | 🔄 In Progress |
23+
| 5. IPv6 UDP Tracker Issue | [ipv6-udp-tracker-issue.md](ipv6-udp-tracker-issue.md) | 🔴 Unresolved |
2324

2425
## Why Before `configure`?
2526

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# IPv6 UDP Tracker — Known Issue
2+
3+
> **Status**: 🔴 Unresolved — newTrackon submission rejected with "UDP timeout" on IPv6 probe
4+
5+
## Context
6+
7+
During issue #407 (submitting the UDP1 tracker to newTrackon), the tracker was rejected with a
8+
"UDP timeout" error. The newTrackon probe used the AAAA record
9+
(`2a01:4f8:1c0c:828e::1`) to reach the tracker via IPv6. IPv4 probes (tested locally) work fine.
10+
11+
This document records the investigation, likely root cause, and the fix required.
12+
13+
## Symptom
14+
15+
- `udp://udp1.torrust-tracker-demo.com:6969/announce` submitted to newTrackon
16+
- newTrackon probed via IPv6: `2a01:4f8:1c0c:828e::1`
17+
- Result: ❌ **Rejected — UDP timeout**
18+
- Local test via IPv4 (`116.202.177.184`): ✅ Works
19+
20+
## What Was Ruled Out
21+
22+
| Hypothesis | Evidence | Verdict |
23+
| ---------------------------- | --------------------------------------------------------------- | ------------ |
24+
| Asymmetric routing | Must rule out — see below | 🔍 Possible |
25+
| Wrong IP in DNS | `dig AAAA` returns `2a01:4f8:1c0c:828e::1`| ❌ Ruled out |
26+
| Floating IP not on interface | `ip addr show eth0` shows all four IPs with `valid_lft forever` | ❌ Ruled out |
27+
| BEP 34 TXT record missing | `dig TXT udp1.torrust-tracker-demo.com` returns correct value | ❌ Ruled out |
28+
| Caddy proxy intercepting UDP | UDP tracker bypasses reverse proxy entirely | ❌ Ruled out |
29+
30+
## Most Likely Root Cause — Docker IPv6 Disabled
31+
32+
By default, Docker does **not** enable IPv6. When Docker is started without IPv6 support, the
33+
tracker container binds to `0.0.0.0:6969` (IPv4 only). UDP packets arriving on the IPv6 floating
34+
IP are received by the kernel but never forwarded to the container — they are silently dropped.
35+
36+
This would also explain why:
37+
38+
- IPv4 tests (local machine, clients) work fine — the container is reachable on `0.0.0.0:6969`
39+
- IPv6 probes (newTrackon) fail — the kernel has no listener to forward them to
40+
41+
### Verification Steps
42+
43+
SSH into the server and run:
44+
45+
```bash
46+
# 1. Check what the tracker is actually listening on
47+
sudo ss -ulnp | grep 6969
48+
# Expected if broken: 0.0.0.0:6969 (IPv4 only, no :::6969)
49+
# Expected if working: 0.0.0.0:6969 AND :::6969
50+
51+
# 2. Check if Docker IPv6 is enabled in the daemon
52+
cat /etc/docker/daemon.json
53+
54+
# 3. Check if the Docker network has IPv6
55+
docker network inspect bridge | grep -A5 EnableIPv6
56+
57+
# 4. Check running container port bindings
58+
docker compose ps
59+
```
60+
61+
## Alternative Root Cause — Asymmetric Routing
62+
63+
Even if Docker is IPv6-enabled, the kernel may route reply packets via the wrong interface.
64+
When a UDP probe arrives on `2a01:4f8:1c0c:828e::1`, the reply could leave via the primary
65+
interface IP (`2a01:4f8:1c19:620b::1`) rather than the floating IP. The remote host (newTrackon)
66+
discards packets with an unexpected source IP.
67+
68+
This requires **policy-based routing**: a separate routing table per floating IP that forces
69+
replies to use the correct source.
70+
71+
## Historical Context
72+
73+
### Old Demo Tracker (torrust-demo.com, Digital Ocean)
74+
75+
The previous Torrust demo tracker was deployed on Digital Ocean with a reserved IPv4
76+
(`144.126.245.19`). That deployment only served **IPv4** — no IPv6 floating IPs were configured.
77+
This means the asymmetric routing / IPv6 Docker issue was never encountered.
78+
79+
### This Deployment (torrust-tracker-demo.com, Hetzner)
80+
81+
This is the **first Torrust deployment routing UDP tracker traffic over IPv6 floating IPs**.
82+
The combination of:
83+
84+
1. Multiple floating IPs (both IPv4 and IPv6)
85+
2. Docker with default network settings
86+
3. UDP tracker on port 6969
87+
88+
…is new territory. Either or both root causes above may apply.
89+
90+
### Proxy Difference (Nginx vs Caddy)
91+
92+
The old demo used Nginx as a reverse proxy; this deployment uses Caddy. This is **irrelevant
93+
for UDP tracker traffic** — UDP does not go through the reverse proxy (HTTP only). Both
94+
setups are equivalent from the UDP tracker's perspective.
95+
96+
## Required Fix
97+
98+
### Fix 1 — Enable Docker IPv6 (likely required)
99+
100+
On the server:
101+
102+
```bash
103+
# Check current daemon.json
104+
cat /etc/docker/daemon.json
105+
```
106+
107+
If IPv6 is not enabled, add it:
108+
109+
```json
110+
{
111+
"ipv6": true,
112+
"fixed-cidr-v6": "fd00::/80"
113+
}
114+
```
115+
116+
Then restart Docker and re-check:
117+
118+
```bash
119+
sudo systemctl restart docker
120+
sudo ss -ulnp | grep 6969
121+
```
122+
123+
After this, the tracker should show `:::6969` in the `ss` output.
124+
125+
### Fix 2 — Policy-Based Routing (may also be required)
126+
127+
If replies still go via the wrong source IP:
128+
129+
```bash
130+
# Get the default IPv6 gateway
131+
ip -6 route show default
132+
133+
# Add a routing table for the UDP1 floating IPv6
134+
ip -6 route add default via <ipv6_gateway> dev eth0 table 200
135+
ip -6 rule add from 2a01:4f8:1c0c:828e::1 table 200
136+
137+
# Make persistent via netplan or /etc/networkd-dispatcher
138+
```
139+
140+
## Impact
141+
142+
This issue blocks the UDP1 tracker from being accepted by newTrackon. It does **not** affect:
143+
144+
- HTTP tracker functionality (goes through Caddy → Docker on IPv4)
145+
- IPv4 UDP tracker functionality
146+
- Existing HTTP1 tracker newTrackon listing
147+
148+
## Cross-Repository Note
149+
150+
This issue should also be documented in the
151+
[torrust-tracker](https://github.com/torrust/torrust-tracker) repository, as it involves
152+
the tracker's network configuration requirements when running behind Docker with IPv6 floating
153+
IPs. Any future deployment guide covering IPv6 should mention:
154+
155+
1. Docker daemon needs `"ipv6": true` in `daemon.json`
156+
2. Policy-based routing may be needed for multiple IPv6 floating IPs
157+
158+
## Related
159+
160+
- [Issue #407 — Submit UDP1 Tracker to newTrackon](../../../issues/407-submit-udp1-tracker-to-newtrackon.md)
161+
- [newTrackon Prerequisites](newtrackon-prerequisites.md)
162+
- [Netplan Configuration](newtrackon-prerequisites.md#step-3--configure-all-floating-ips-permanently-via-netplan-)

project-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ unrepresentable
508508
unsubscription
509509
userexample
510510
usermod
511+
ulnp
511512
useroutput
512513
userpass
513514
userspace

0 commit comments

Comments
 (0)