fix: enable IPv6 on Docker proxy_network for native IPv6 UDP support#4
Merged
Conversation
Docker's default ip6tables: false causes its chain rewrites to wipe ufw's live ip6tables rules after every container restart. This silently dropped all IPv6 UDP port 6969 traffic, causing the UDP tracker to show as down on newTrackon after the nightly backup restart. Fix: add /etc/docker/daemon.json with ip6tables: true. Docker now manages ip6tables rules for all published ports automatically, mirroring its IPv4 behaviour. The fix is systemic — no per-port or per-container configuration needed, and it covers future UDP trackers on any port. Verified on 2026-03-09: - ufw6-user-input rule for udp/6969 survives docker compose restart - udp://udp1.torrust-tracker-demo.com:6969/announce back up on newTrackon Refs: #2
- Add docs/docker-ipv6.md: operations reference explaining why ip6tables: true is required, its scope, what it does not cover (floating IP routing), and server application + verification steps - Add docs/post-mortems/2026-03-09-udp-ipv6-docker.md: full investigation log with commands, outputs, root cause analysis and fix decision rationale - Update docs/issues/ISSUE-2-udp-tracker-down-on-newtrackon.md: add Fix Applied section, tick off completed acceptance criteria - Update server/README.md: add Key Configuration Notes section cross-referencing daemon.json and netplan floating IP config Refs: #2
The deployer handles basic provisioning but does not cover floating IP routing or Docker IPv6 ip6tables configuration. Add docs/post-deployment.md as the canonical checklist of manual steps required after running the deployer, with a section for each concern: 1. Floating IP routing (netplan policy routing — required per floating IP) 2. Docker IPv6 for UDP trackers (daemon.json ip6tables: true — required only for IPv6 UDP announces) - Add deployer callout to docs/docker-ipv6.md pointing to the checklist - Add post-deployment link to README.md Refs: #2
Root Cause B: Docker bridge networks were IPv4-only, so no container IPv6 address existed. Docker created no ip6tables DNAT rules, leaving docker-proxy as sole handler. docker-proxy attempted cross-AF UDP relay (IPv6 host socket to IPv4 container backend 172.21.0.3), which silently drops all native IPv6 UDP packets. Changes: - docker-compose.yml: add enable_ipv6: true + subnet fd01:db8:1::/64 to proxy_network so the container receives a ULA IPv6 address and Docker generates ip6tables DNAT rules, bypassing docker-proxy entirely - docs/post-mortems/2026-03-09-udp-ipv6-docker.md: add Steps 9-12 (docker logs grep, ps aux smoking gun), Root Cause B section, Fix B decision rationale - docs/docker-ipv6.md: rewrite to cover both problems (chain wipe and docker-proxy cross-AF) and both fixes (daemon.json and enable_ipv6 + SNAT) - docs/post-deployment.md: add Step 3 with exact SNAT rule for before6.rules to override Docker MASQUERADE and pin UDP 6969 replies to the floating IPv6 2a01:4f8:1c0c:828e::1 - docs/issues/ISSUE-2-udp-tracker-down-on-newtrackon.md: tick off newTrackon acceptance criterion (confirmed 2026-03-09), update survival criterion - server/README.md: expand Key Configuration Notes with daemon.json, enable_ipv6, and SNAT subsections - project-words.txt: add SNAT, SNATs, POSTROUTING, PREROUTING, userland, ulnp, UNCONN, misrouted, ADDRTYPE, pkts Fix verified: newTrackon reports tracker working for both IPv6 and IPv4 (2a01:4f8:1c0c:828e::1 and 116.202.177.184) as of 2026-03-09. A manual server-side step is still required on fresh deploys: prepend a *nat POSTROUTING SNAT rule to /etc/ufw/before6.rules and run sudo ufw reload. See docs/post-deployment.md Step 3 for the exact lines.
Add the full /etc/ufw/before6.rules file to server/etc/ufw/ so the repo captures the complete server UFW configuration, including the custom *nat POSTROUTING SNAT rule added as part of Fix B. Also exclude the three UFW rules files from cspell (verbatim system files with non-prose content like icmpv6 type names) and update docs/infrastructure.md and docs/post-deployment.md to reference the new file.
Member
Author
Member
Author
|
ACK 3b331cd |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Fixes #2 —
udp://udp1.torrust-tracker-demo.com:6969/announceshowing as down on newTrackon after nightly Docker restart.Two root causes were identified and fixed.
Root Cause A (Fix A — already committed)
Docker's default
ip6tables: falsecauses Docker chain rewrites to wipe ufw's live ip6tables rules after every container restart. IPv6 UDP packets arriving after a restart were silently dropped.Fix:
server/etc/docker/daemon.jsonwith{"ip6tables": true}.Root Cause B (Fix B — this PR)
Docker bridge networks were IPv4-only, so the container had no IPv6 address. Docker created no ip6tables DNAT rules, leaving docker-proxy as the sole IPv6 handler. docker-proxy attempted cross-AF UDP relay (IPv6 host socket → IPv4 container backend
172.21.0.3), which silently drops all native IPv6 UDP packets.Additionally, even with DNAT working, Docker's MASQUERADE rewrote reply source addresses to the server's primary IPv6 (
2a01:4f8:1c19:620b::1) instead of the floating IPv6 (2a01:4f8:1c0c:828e::1). Clients that probed the floating IP received replies from the wrong address and timed out.Packet flow (after fix)
Changes
docker-compose.yml: addenable_ipv6: true+subnet: fd01:db8:1::/64toproxy_networkserver/etc/ufw/before6.rules: add full file snapshot including SNAT ruledocs/docker-ipv6.md: rewrite to cover both problems with packet-flow diagramdocs/post-deployment.md: add Step 3 — SNAT rule forbefore6.rulesdocs/post-mortems/2026-03-09-udp-ipv6-docker.md: full diagnostic chain (Steps 1–12), Root Cause B, Fix B decisiondocs/issues/ISSUE-2-udp-tracker-down-on-newtrackon.md: acceptance criteria updatedserver/README.md: Key Configuration Notes expandedproject-words.txt: new networking terms addedcspell.json: exclude verbatim UFW rules files from spell checkManual server-side step required on fresh deploys
Prepend to
/etc/ufw/before6.rules:Then
sudo ufw reload. Seedocs/post-deployment.mdStep 3.Verification
Confirmed working on newTrackon 2026-03-09: both IPv6 (
2a01:4f8:1c0c:828e::1) and IPv4 (116.202.177.184) reporting as up.Remaining open criterion: fix survives a nightly Docker Compose restart (to verify after tonight's backup cron run).
Refs: #2