A modern, high-performance private BitTorrent tracker
Three containers β Nuxt 4 web Β· Nitro API Β· Go tracker β backed by PostgreSQL and Redis.
Features β’ Architecture β’ Quick Start β’ Static deployment β’ Documentation β’ Live Demo
- Zero-Knowledge auth β PBKDF2-600k + SHA-256 verifier; the password never leaves the browser. Same flow drives sign-in, passkey rotation, and change-password.
- Proof of Work on registration stops drive-by signup spam.
- Hashed IPs β SHA-256 with daily-rotating salt; no raw IP persisted. Banning a user atomically banlists their last-known IP.
- Privacy toggles β hide last-seen on public profile (mods/admins always see the truth).
- Rich media metadata β TMDb (films + TV), IGDB (games), Open Library + Google Books (books); user locale drives the lookup language.
- Smart media-id paste β drop an IMDb / TMDb / TVDB / IGDB slug or ISBN into search to filter the listing.
- Dedicated upload page β auto title + tags from filename, multi-source search picker, duplicate preflight, conditional ID block per category, Tiptap WYSIWYG description, NFO drag-drop (CP437 β UTF-8).
- Operator console β
/admincovers users, categories, roles, invites, branding, panic, tags, Torznab, reports, HnR. - Notification fan-out β every event-emitting route hits Postgres + Redis pub/sub + the user's chosen external transport (SMTP, Telegram, Discord, ntfy, Gotify, Pushover, Slack, Mattermost, webhook, Apprise, Web Push).
- HTTP announce (BEP 3) on
8080/tcp; sub-ms p99; alloc-friendly bencode path. - UDP announce (BEP 15) on
6969/udp; ~6Γβ8Γ cheaper on the wire than HTTP; statelessconnection_id= HMAC-SHA256(secret, ip β minute), so no per-id memory. - BEP 41 URL_DATA passkey β
udp://host:6969/announce/PASSKEYworks as-is in every modern client. - Multi-tier
.torrentfiles β generator advertises HTTP + UDP independently.TRACKER_UDP_ENABLED=falsedisables UDP and drops it from new.torrentfiles in one go.
- Seed-bonus points β customisable per-minute rules (time, torrent age, rarity); tiered curves with live preview; ledger-backed.
- Bonus shop β operator-curated catalogue with built-in
upload_creditandinviteeffects. - Bonus events β time-bounded Freeleech / Silverleech / custom multipliers, applied on the announce hot path.
- Panic Mode β instant AES-256-GCM encryption of torrent data + user fields; recovery requires the original Panic Password. See Panic Mode.
- Distributed rate limiting β Redis-backed sliding windows; progressive penalties; auto IP bans.
- Optional static deployment β distroless nginx serves a CSR bundle in ~28 MB (see below).
- Owner-curated network β connect independent Trackarr instances through a double opt-in, Ed25519-signed handshake; off by default, enabled from
/admin/federation. - Granular, asymmetric scopes β share/accept
catalog,social,accounts,swarmper peer and per direction; suspend / block / revoke / re-scope any peer from the owner console. - Federated catalogue β browse & search partner content (cron-synced cache + live fan-out) with cross-instance dedupe; a remote download links back to its origin instance (never your local passkey).
- Federated social & identity β follow remote uploaders, read-only sanitized comments/forum, and prove you own an account on a partner (verified-identity badge + read-only reputation; no SSO, economies stay isolated).
- Optional swarm cross-announce β opt-in per torrent and per peer, behind
TRACKER_FEDERATION_SWARM(off); onlyip/port/isSeedercross the wire and ratio/HnR stay local. See Federation.
Three independent containers behind Caddy, plus the usual Postgres + Redis. They share zero process state β Redis is the only cross-cutting bus.
βββββββββββββββββββββββββββββββββββββββββββββββ
Browser ββHTTPSβββΊ β Caddy :80 / :443 / :443/udp (HTTP/3) β
β /announce* β tracker :8080 (BEP 3) β
β /api/* β api :4000 β
β /uploads/* β api :4000 β
β /* β web :3000 (Nuxt SSR)β
βββββββββββββββββββββββββββββββββββββββββββββββ
BT client ββUDPβββΊ tracker :6969 (BEP 15, bypasses Caddy β UDP can't be reverse-proxied)
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β apps/web β β apps/api β β apps/tracker β
β Nuxt 4 SSR β β Nitro 4 β β Go 1.25 β
β Vue 3 / TS β β Drizzle ORM β β sqlc β
β (stateless) β β Zod, h3 β β HTTP + UDP β
ββββββββββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββββββββββββ
β PgBouncer :6432 β
ββββββββββββββββββ¬ββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββ
β PostgreSQL :5432 β
ββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββ
api βββΊβ Redis :6379 ββββ tracker
β peers, sessions, rate-limit, β
β seed-bonus, notification bus β
ββββββββββββββββββββββββββββββββββ
Why three containers
- The tracker is its own thing. It's the hot path β every BitTorrent client in the swarm hits
/announceevery few minutes. A static Go binary onscratch(~10 MB image, sub-ms p99) means a single broken Nuxt deploy can't take down announces. Two transports β BEP 3 over HTTP/8080 and BEP 15 over UDP/6969 β share one wire-agnostic processor. - API and web are split.
apps/api(Nitro standalone) owns every/api/*route, upload endpoints, metadata lookups, admin tools.apps/webis Nuxt SSR β rendered shell + page chunks. They scale and redeploy independently. - Distroless everywhere.
apps/web+apps/apirun ongcr.io/distroless/nodejs24-debian13:nonroot;apps/trackerruns onscratch; the static frontend runs oncgr.dev/chainguard/nginx. No shells, no package managers, non-root by default.
More details: Architecture guide.
- Docker 20+ and Docker Compose v2
- For production: a domain name, ports
80/443open, anACME_EMAILfor Let's Encrypt.
Trackarr publishes signed multi-arch images to GHCR β no need to build anything locally:
| Image | Role |
|---|---|
ghcr.io/dim145/opentracker/api:latest |
Nitro API |
ghcr.io/dim145/opentracker/front-ssr:latest |
Nuxt SSR web |
ghcr.io/dim145/opentracker/front:latest |
Static SPA (overlay) |
ghcr.io/dim145/opentracker/tracker:latest |
Go BitTorrent tracker |
Full list & tags: https://github.com/Dim145?tab=packages&repo_name=opentracker.
docker-compose.prod.yml pulls these by default; pin a specific tag with IMAGE_TAG=v0.17.0.
git clone https://github.com/Dim145/opentracker.git /opt/trackarr
cd /opt/trackarr
cp .env.example .env
cat >> .env <<EOF
NODE_ENV=production
DOMAIN=your-domain.com
TRACKER_DOMAIN=tracker.your-domain.com
ACME_EMAIL=admin@your-domain.com
NUXT_SESSION_SECRET=$(openssl rand -hex 32)
ADMIN_API_KEY=$(openssl rand -hex 32)
IP_HASH_SECRET=$(openssl rand -hex 32)
CHANNEL_ENCRYPTION_KEY=$(openssl rand -hex 32)
DB_PASSWORD=$(openssl rand -base64 24)
REDIS_PASSWORD=$(openssl rand -base64 24)
NUXT_PUBLIC_TRACKER_HTTP_URL=https://tracker.your-domain.com/announce
NUXT_PUBLIC_TRACKER_UDP_URL=udp://tracker.your-domain.com:6969/announce
EOF
docker compose -f docker-compose.prod.yml up -dPoint your-domain.com + tracker.your-domain.com at the VPS IP, then open
https://your-domain.com β the first user to register becomes the admin
and is prompted to set a panic password.
Updates are a git pull && docker compose -f docker-compose.prod.yml pull && up -d
(or pin IMAGE_TAG to a release). Volumes (postgres_data, redis_data,
uploads_data, caddy_data) survive rebuilds.
Full walk-through, env reference, and operations: doc/guide/getting-started.md.
A second image β ghcr.io/dim145/opentracker/front β serves a fully static SPA
from distroless Chainguard nginx:
| SSR (default) | Static | |
|---|---|---|
| Image size | 254 MB | 28 MB |
| Idle RSS | ~120 MB | ~7 MB |
| Cold start | ~2 s | <100 ms |
| Base image | distroless/nodejs24 |
chainguard/nginx |
| First paint | server-rendered HTML | SPA shell, hydrates client-side |
docker compose \
-f docker-compose.prod.yml \
-f docker-compose.static.yml \
--env-file .env \
up -dThe static bundle fetches GET /api/runtime-config on boot and patches
useRuntimeConfig().public, so the same image is portable across domains β
only the API container needs the NUXT_PUBLIC_TRACKER_*_URL vars.
| Layer | Technology | Notes |
|---|---|---|
| Frontend | Nuxt 4, Vue 3, Tailwind CSS, Tiptap | SSR by default, opt-in static SPA build |
| API | Nitro 4 (Node 24), Drizzle ORM, Zod | Standalone container, distroless runtime |
| Tracker | Go 1.25, sqlc | scratch-based image, sub-ms announce p99 |
| Database | PostgreSQL 16 | gin_trgm_ops full-text, drizzle-kit push |
| Connection pool | PgBouncer | Transaction-mode pooling |
| Cache / queue | Redis 7 | Peer hashes, sessions, rate-limit windows, pub/sub |
| Reverse proxy | Caddy 2 | Auto-HTTPS, HTTP/3 |
| Crypto | Web Crypto API, scrypt, AES-256-GCM | ZKE auth, Panic encryption |
| Observability | Prometheus /metrics |
Dedicated port on the API container |
| Monorepo | pnpm workspaces | packages/{shared,db} + apps/{web,api,tracker} |
Security deep-dive: doc/guide/security.md, doc/guide/zero-knowledge-auth.md, doc/guide/panic-mode.md.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing) - Open a Pull Request
The repo is a pnpm monorepo. For local hacking, see Getting Started β Local development; for running the full container stack on your laptop, see Local Production.
Trackarr is built on the shoulders of giants. Thanks to the following open-source projects:
| Project | Role |
|---|---|
| Nuxt | Fullstack Vue framework |
| Vue.js | Reactive frontend framework |
| Nitro | Universal JS server engine |
| Drizzle ORM | TypeScript ORM |
| sqlc | Go DB codegen for the tracker |
| PostgreSQL | Database |
| Redis | In-memory cache |
| ioredis | Redis client for Node.js |
| Caddy | Reverse proxy + HTTPS |
| Chainguard | Distroless container images |
| Tailwind CSS | Utility-first CSS |
| Tiptap | WYSIWYG editor |
| Chart.js | Charts & visualizations |
| Iconify | Icon framework (Phosphor set) |
| VitePress | Documentation framework |
| Pinia | State management |
| Zod | Schema validation |
| TMDb | Films + TV metadata |
| IGDB | Video-game metadata |
| Open Library | Books + ebook metadata |
| Google Books API | Books metadata (fallback) |
| web-push | RFC 8291 / 8292 push delivery |
Thanks to all our contributors! Sorted by number of commits.
| Avatar | Contributor | Commits |
|---|---|---|
| Dim145 | 4 | |
| IkiaeM | 4 |
MIT License β see LICENSE for details.
Built with β€οΈ for the P2P community