diff --git a/src/routes/blog/setup-udp-tracker-behind-floating-ip/+page.server.ts b/src/routes/blog/setup-udp-tracker-behind-floating-ip/+page.server.ts new file mode 100644 index 0000000..5cf0731 --- /dev/null +++ b/src/routes/blog/setup-udp-tracker-behind-floating-ip/+page.server.ts @@ -0,0 +1,14 @@ +import { getMetadata } from '$lib/data/metadata'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ url }) => { + const slug = url.pathname.split('/').filter(Boolean).pop(); + if (!slug) throw new Error('Slug could not be determined.'); + + const metadata = await getMetadata(); + const currentPost = metadata.find((post) => post.slug === slug); + + if (!currentPost) throw new Error(`Post not found: ${slug}`); + + return { currentPost, allPosts: metadata }; +}; diff --git a/src/routes/blog/setup-udp-tracker-behind-floating-ip/+page.svelte b/src/routes/blog/setup-udp-tracker-behind-floating-ip/+page.svelte new file mode 100644 index 0000000..4d9e4a3 --- /dev/null +++ b/src/routes/blog/setup-udp-tracker-behind-floating-ip/+page.svelte @@ -0,0 +1,549 @@ + + + + +
+ +
+

Introduction

+

+ 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. +

+ +

Why This Matters

+

+ 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. +

+ + + Core problem: with default routing, packets that arrive via floating IP A can + leave through primary IP B. This asymmetric path is enough to break UDP tracker probes. + + +

Tested Environment

+
    +
  • Cloud provider: Hetzner Cloud
  • +
  • OS: Ubuntu 24.04 LTS
  • +
  • + Tracker endpoint: + udp://udp1.torrust-tracker-demo.com:6969/announce +
  • +
  • Floating IPv4: 116.202.177.184
  • +
  • Floating IPv6: 2a01:4f8:1c0c:828e::1
  • +
  • Container stack: Docker + Docker Compose
  • +
+ +

Architecture Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LayerWhat must happen
DNSUDP tracker domain resolves to dedicated floating IPs
FirewallUDP port 6969 allowed on IPv4 and IPv6
Kernel routingSource policy routing for each floating IP
Docker IPv6ip6tables enabled and bridge network has IPv6 subnet
NAT (IPv6)SNAT replies to the floating IPv6 for UDP/6969
+ +

Step 1: Configure Policy Routing for Floating IPs

+

+ 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. +

+ + + + + + + If you use cloud-init, keep your custom floating-IP and routing rules in a separate + netplan file with a higher numeric prefix (for example, 60-floating-ip.yaml) + rather than editing 50-cloud-init.yaml. + + +

Step 2: Open UDP Port 6969 in the Firewall

+

+ 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. +

+ +

Step 3: Enable Docker ip6tables Management

+

+ 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:

+ + + +

+ Step 4: Enable IPv6 on the Docker Bridge Network +

+

+ 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. +

+ + + + + +

Step 5: Add SNAT for IPv6 UDP Replies

+

+ 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. +

+ + + + + +

Verification Checklist

+
    +
  1. + Domain points to the correct floating IPs: dig A and dig AAAA. +
  2. +
  3. Firewall allows UDP 6969 on both families.
  4. +
  5. Policy rules and custom tables are active after reboot.
  6. +
  7. Container has a non-empty IPv6 on the bridge network.
  8. +
  9. DNAT and SNAT counters increase when probes run.
  10. +
  11. Tracker accepts announces and replies from the expected source IP.
  12. +
+ + + +

+ For external validation, we used + newTrackon + and the raw status page at newtrackon.com/raw. +

+ +

Cloud Provider Naming Equivalents

+

The same server-side setup is useful across providers, even if naming differs.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ProviderTypical name
HetznerFloating IP
DigitalOceanReserved IP
AWSElastic IP
Linode/AkamaiStatic IP
+ +

Conclusion

+

+ 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. +

+ +
+
+
+ + +
+ + diff --git a/src/routes/blog/setup-udp-tracker-behind-floating-ip/metadata.ts b/src/routes/blog/setup-udp-tracker-behind-floating-ip/metadata.ts new file mode 100644 index 0000000..635e991 --- /dev/null +++ b/src/routes/blog/setup-udp-tracker-behind-floating-ip/metadata.ts @@ -0,0 +1,12 @@ +export const metadata = { + title: 'How to Run a UDP Tracker Behind a Floating IP on Ubuntu', + slug: 'setup-udp-tracker-behind-floating-ip', + contributor: 'Jose Celano', + contributorSlug: 'jose-celano', + date: '2026-04-14T00:00:00.000Z', + coverImage: + '/images/posts/setup-udp-tracker-behind-floating-ip/udp-tracker-floating-ip-ipv6-docker-configuration.webp', + excerpt: + 'A practical guide to running a UDP BitTorrent tracker behind floating IPs (also known as static, reserved, or elastic IPs) on Ubuntu, including policy routing, Docker IPv6 networking, and SNAT for correct reply paths.', + tags: ['BitTorrent', 'Tracker', 'Networking', 'IPv6', 'Deployment'] +}; diff --git a/static/blogMetadata.json b/static/blogMetadata.json index 8c85939..abc08b8 100644 --- a/static/blogMetadata.json +++ b/static/blogMetadata.json @@ -12,6 +12,21 @@ "Benchmarking" ] }, + { + "title": "Bencode to JSON Converter in Rust", + "slug": "bencode-to-json-converter-in-rust", + "contributor": "Jose Celano", + "contributorSlug": "jose-celano", + "date": "2024-11-01T11:57:37.926Z", + "coverImage": "/images/posts/bencode-to-json-converter-in-rust/bencode-to-json-converter-in-rust.webp", + "excerpt": "We're excited to introduce bencode2json, a crate that simplifies converting Bencode data to JSON, benefiting the Rust BitTorrent community.", + "tags": [ + "Bencode", + "JSON", + "Converter", + "Rust" + ] + }, { "title": "Containerizing Rust Applications", "slug": "containerizing-rust-applications-best-practices", @@ -40,6 +55,20 @@ "Production" ] }, + { + "title": "Deploying the Torrust Tracker Demo with the Torrust Tracker Deployer", + "slug": "deploying-torrust-tracker-with-the-deployer", + "contributor": "Jose Celano", + "contributorSlug": "jose-celano", + "date": "2026-04-08T00:00:00.000Z", + "coverImage": "/images/posts/deploying-torrust-tracker-with-the-deployer/torrust-tracker-deployer-hetzner-deployment.webp", + "excerpt": "Learn how we used the Torrust Tracker Deployer to deploy the Torrust Tracker Demo to Hetzner Cloud — a production-ready setup with HTTPS, MySQL, floating IPs, and Grafana monitoring — and what we discovered along the way.", + "tags": [ + "Tutorial", + "Deployment", + "Automation" + ] + }, { "title": "Contributor Path", "slug": "contributor-path", @@ -54,21 +83,6 @@ "Contributors" ] }, - { - "title": "Bencode to JSON Converter in Rust", - "slug": "bencode-to-json-converter-in-rust", - "contributor": "Jose Celano", - "contributorSlug": "jose-celano", - "date": "2024-11-01T11:57:37.926Z", - "coverImage": "/images/posts/bencode-to-json-converter-in-rust/bencode-to-json-converter-in-rust.webp", - "excerpt": "We're excited to introduce bencode2json, a crate that simplifies converting Bencode data to JSON, benefiting the Rust BitTorrent community.", - "tags": [ - "Bencode", - "JSON", - "Converter", - "Rust" - ] - }, { "title": "Building with AI Agents, Building for AI Agents", "slug": "building-with-ai-agents-building-for-ai-agents", @@ -85,20 +99,6 @@ "Tutorial" ] }, - { - "title": "Deploying the Torrust Tracker Demo with the Torrust Tracker Deployer", - "slug": "deploying-torrust-tracker-with-the-deployer", - "contributor": "Jose Celano", - "contributorSlug": "jose-celano", - "date": "2026-04-08T00:00:00.000Z", - "coverImage": "/images/posts/deploying-torrust-tracker-with-the-deployer/torrust-tracker-deployer-hetzner-deployment.webp", - "excerpt": "Learn how we used the Torrust Tracker Deployer to deploy the Torrust Tracker Demo to Hetzner Cloud — a production-ready setup with HTTPS, MySQL, floating IPs, and Grafana monitoring — and what we discovered along the way.", - "tags": [ - "Tutorial", - "Deployment", - "Automation" - ] - }, { "title": "Hash2Torrent - Retrieve Torrent Files Effortlessly!", "slug": "hash2torrent-retrieve-torrent-files-effortlessly", @@ -129,15 +129,17 @@ ] }, { - "title": "How To Contribute To This Site", - "slug": "how-to-contribute-to-this-site", - "contributor": "", - "contributorSlug": "", - "date": "2023-04-22T21:55:15.361Z", - "coverImage": "/images/posts/sample-post.jpg", - "excerpt": "How to manage existing blog posts and create new ones on this site.", + "title": "How To Setup The Dev Env", + "slug": "how-to-setup-the-development-environment", + "contributor": "Jose Celano", + "contributorSlug": "jose-celano", + "date": "2023-07-11T12:29:04.295Z", + "coverImage": "/images/posts/development-environment.png", + "excerpt": "If you want to contribute to the Torrust Index, this article explains how to setup a development environment with the latest versions for all services.", "tags": [ - "Documentation" + "Torrent", + "Tracker", + "BitTorrent" ] }, { @@ -155,17 +157,15 @@ ] }, { - "title": "How To Setup The Dev Env", - "slug": "how-to-setup-the-development-environment", - "contributor": "Jose Celano", - "contributorSlug": "jose-celano", - "date": "2023-07-11T12:29:04.295Z", - "coverImage": "/images/posts/development-environment.png", - "excerpt": "If you want to contribute to the Torrust Index, this article explains how to setup a development environment with the latest versions for all services.", + "title": "How To Contribute To This Site", + "slug": "how-to-contribute-to-this-site", + "contributor": "", + "contributorSlug": "", + "date": "2023-04-22T21:55:15.361Z", + "coverImage": "/images/posts/sample-post.jpg", + "excerpt": "How to manage existing blog posts and create new ones on this site.", "tags": [ - "Torrent", - "Tracker", - "BitTorrent" + "Documentation" ] }, { @@ -243,13 +243,13 @@ ] }, { - "title": "Released version v3.0.0-beta", - "slug": "released-v3-0-0", + "title": "Released version v3.0.0", + "slug": "released-v3-0-0-beta", "contributor": "Jose Celano", "contributorSlug": "jose-celano", - "date": "2024-09-03T14:30:38.554Z", - "coverImage": "/images/posts/released-v3-0-0-beta/team.png", - "excerpt": "We're excited to announce the release of v3.0.0-beta, marking a significant step towards our upcoming major release, v3.0.0. This release solidifies the features and prepares us for the beta phase.", + "date": "2024-10-03T11:05:14.597Z", + "coverImage": "/images/posts/released-v3-0-0/team.png", + "excerpt": "We’re thrilled to announce the official release of version 3.0.0 of the Torrust software.", "tags": [ "Announcement", "Release" @@ -271,18 +271,34 @@ ] }, { - "title": "Released version v3.0.0", - "slug": "released-v3-0-0-beta", + "title": "Released version v3.0.0-beta", + "slug": "released-v3-0-0", "contributor": "Jose Celano", "contributorSlug": "jose-celano", - "date": "2024-10-03T11:05:14.597Z", - "coverImage": "/images/posts/released-v3-0-0/team.png", - "excerpt": "We’re thrilled to announce the official release of version 3.0.0 of the Torrust software.", + "date": "2024-09-03T14:30:38.554Z", + "coverImage": "/images/posts/released-v3-0-0-beta/team.png", + "excerpt": "We're excited to announce the release of v3.0.0-beta, marking a significant step towards our upcoming major release, v3.0.0. This release solidifies the features and prepares us for the beta phase.", "tags": [ "Announcement", "Release" ] }, + { + "title": "How to Run a UDP Tracker Behind a Floating IP on Ubuntu", + "slug": "setup-udp-tracker-behind-floating-ip", + "contributor": "Jose Celano", + "contributorSlug": "jose-celano", + "date": "2026-04-14T00:00:00.000Z", + "coverImage": "/images/posts/setup-udp-tracker-behind-floating-ip/udp-tracker-floating-ip-ipv6-docker-configuration.webp", + "excerpt": "A practical guide to running a UDP BitTorrent tracker behind floating IPs (also known as static, reserved, or elastic IPs) on Ubuntu, including policy routing, Docker IPv6 networking, and SNAT for correct reply paths.", + "tags": [ + "BitTorrent", + "Tracker", + "Networking", + "IPv6", + "Deployment" + ] + }, { "title": "Setting Up Torrust with Claude Code", "slug": "setting-up-torrust-with-claude-code", @@ -298,19 +314,16 @@ ] }, { - "title": "Submitting Trackers to newTrackon", - "slug": "submitting-trackers-to-newtrackon", + "title": "Torrust - Enhancing the BitTorrent Ecosystem", + "slug": "torrust-enhancing-the-bittorrent-ecosystem", "contributor": "Jose Celano", "contributorSlug": "jose-celano", - "date": "2026-04-01T12:00:00.000Z", - "coverImage": "/images/posts/submitting-trackers-to-newtrackon/submitting-bittorrent-tracker-to-newtrackon.webp", - "excerpt": "A practical guide to submitting BitTorrent trackers to newTrackon, the popular third-party uptime monitoring service. Learn the prerequisites, how to work around the one-tracker-per-IP constraint using floating IPs, and how to configure DNS correctly with BEP34.", + "date": "2024-05-31T09:33:14.163Z", + "coverImage": "/images/posts/deploying-torrust-to-production/deploy-torrust-to-a-digital-ocean-droplet.png", + "excerpt": "Torrust, an open-source organization, is making significant contributions to the BitTorrent ecosystem by developing robust tools, improving documentation, and fostering community collaboration.", "tags": [ - "BitTorrent", - "Tracker", - "DevOps", - "DNS", - "Demo" + "Introduction", + "Torrust" ] }, { @@ -327,19 +340,6 @@ "BitTorrent" ] }, - { - "title": "Torrust - Enhancing the BitTorrent Ecosystem", - "slug": "torrust-enhancing-the-bittorrent-ecosystem", - "contributor": "Jose Celano", - "contributorSlug": "jose-celano", - "date": "2024-05-31T09:33:14.163Z", - "coverImage": "/images/posts/deploying-torrust-to-production/deploy-torrust-to-a-digital-ocean-droplet.png", - "excerpt": "Torrust, an open-source organization, is making significant contributions to the BitTorrent ecosystem by developing robust tools, improving documentation, and fostering community collaboration.", - "tags": [ - "Introduction", - "Torrust" - ] - }, { "title": "BitTorrent Trackers Implemented in Rust", "slug": "trackers-implemented-in-rust", @@ -355,6 +355,22 @@ "Open Source" ] }, + { + "title": "Submitting Trackers to newTrackon", + "slug": "submitting-trackers-to-newtrackon", + "contributor": "Jose Celano", + "contributorSlug": "jose-celano", + "date": "2026-04-01T12:00:00.000Z", + "coverImage": "/images/posts/submitting-trackers-to-newtrackon/submitting-bittorrent-tracker-to-newtrackon.webp", + "excerpt": "A practical guide to submitting BitTorrent trackers to newTrackon, the popular third-party uptime monitoring service. Learn the prerequisites, how to work around the one-tracker-per-IP constraint using floating IPs, and how to configure DNS correctly with BEP34.", + "tags": [ + "BitTorrent", + "Tracker", + "DevOps", + "DNS", + "Demo" + ] + }, { "title": "Visualize Tracker Metrics with Prometheus and Grafana", "slug": "visualize-tracker-metrics-prometheus-grafana", diff --git a/static/images/posts/setup-udp-tracker-behind-floating-ip/udp-tracker-floating-ip-ipv6-docker-configuration.webp b/static/images/posts/setup-udp-tracker-behind-floating-ip/udp-tracker-floating-ip-ipv6-docker-configuration.webp new file mode 100644 index 0000000..0c8fb48 Binary files /dev/null and b/static/images/posts/setup-udp-tracker-behind-floating-ip/udp-tracker-floating-ip-ipv6-docker-configuration.webp differ