---
name: private-tracker-seedbox
description: |-
  Deploy and troubleshoot a Docker seedbox for private trackers — gluetun VPN,
  qBittorrent, PIA port forwarding, and tracker-specific authorization
  (MAM Dynamic Seedbox via Mousehole).
version: 1.0.0
platforms: [linux]
tags: [seedbox, torrent, qbittorrent, gluetun, vpn, private-tracker, mam, myanonamouse]
---

# Private Tracker Seedbox

Deploying a Docker seedbox for private trackers with gluetun (VPN) + qBittorrent,
plus tracker-specific tooling for IP authorization when the torrent client's IP
differs from the browser's IP.

## Gluetun + qBittorrent Base Setup

### Docker Compose Pattern

```yaml
services:
  gluetun:
    image: qmcgaw/gluetun:latest
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - 127.0.0.1:11893:8080    # qBittorrent WebUI
      - 127.0.0.1:6881:6881     # torrent port (optional, qBittorrent uses forwarded port)
      - 127.0.0.1:6881:6881/udp # (optional)
      - 127.0.0.1:5010:5010     # Mousehole WebUI (if used)
    volumes:
      - ./gluetun:/gluetun
    environment:
      VPN_SERVICE_PROVIDER: "private internet access"
      VPN_TYPE: "openvpn"
      OPENVPN_PROTOCOL: "tcp"
      OPENVPN_ENDPOINT_PORT: "443"
      OPENVPN_USER: "your-username"
      OPENVPN_PASSWORD: your-password
      UPDATER_PERIOD: "0"
      SERVER_HOSTNAMES: "nl-amsterdam.privacy.network"  # PIA server
      VPN_PORT_FORWARDING: "on"                          # PIA port forwarding
      PORT_FORWARD_ONLY: "true"                          # only allow port-forwarded ports
      FIREWALL_VPN_INPUT_PORTS: "8080,5010"              # comma-separated allowed ports
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    network_mode: "service:gluetun"
    depends_on:
      gluetun:
        condition: service_healthy
    volumes:
      - ./qbittorrent:/config
      - /path/to/downloads:/downloads
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Chicago
      - WEBUI_PORT=8080
    restart: unless-stopped
```

### Key Config Points

- qBittorrent uses `network_mode: "service:gluetun"` — shares gluetun's network stack
- `depends_on: gluetun: condition: service_healthy` — waits for VPN tunnel before starting
- qBittorrent's listening port (from `Session\Port` in qBittorrent.conf) must match gluetun's forwarded port from the VPN provider
- Port **11893** on host maps to qBittorrent WebUI at port 8080 inside gluetun's stack

### Compose Gotcha: `restart` vs `up -d`

**`docker compose restart <svc>` restarts the existing container with its ORIGINAL config.**
It does NOT pick up changes to port mappings, environment variables, or volumes.
To apply compose changes, use:

```
docker compose up -d
```

This recreates any container whose config has changed. If you need to force even when
the config hasn't changed:

```
docker compose up -d --force-recreate <svc>
```

### Port Binding: localhost-only vs All Interfaces

- `127.0.0.1:5010:5010` — binds ONLY to localhost; accessible from host only
- `5010:5010` — binds to 0.0.0.0 (all interfaces); accessible from LAN and other Docker
  containers (e.g., KasmVNC Chrome)

When running Chrome in a KasmVNC container that's on a different Docker network,
the browser uses its own network namespace. `localhost` inside KasmVNC refers to
KasmVNC itself, not the host. Use the host's LAN IP (e.g., `192.168.50.98:5010`)
and ensure the port is bound to all interfaces.

To verify actual Docker port bindings:
```
docker port <container>
```

## PIA Port Forwarding

PIA port forwarding with gluetun:

1. Set `VPN_PORT_FORWARDING: "on"` and `PORT_FORWARD_ONLY: "true"` on gluetun
2. Gluetun logs show the forwarded port (e.g., `port forwarded is 36590`)
3. The forwarded port is written to `/tmp/gluetun/forwarded_port` inside gluetun
4. qBittorrent must use this port — set `Session\Port=<forwarded port>` in qBittorrent.conf

Check forwarded port:
```
docker exec gluetun cat /tmp/gluetun/forwarded_port
```

## MAM (MyAnonaMouse) — Dynamic Seedbox / Mousehole

### The Problem

MAM compares your **browser's IP** against your **torrent client's IP**. If they don't match
(e.g., browser on home IP, torrent client on VPN IP), you get:
> **"Unrecognized host/PassKey. (client IP here)"**

### Step 1: Get Staff Approval

Open a ticket with MAM staff explaining you use a VPN for your torrent client.
Per MAM rule 1.2, VPN use must be authorized.

### Step 2: Deploy Mousehole as a Sidecar

[Mousehole](https://github.com/t-mart/mousehole) is a background service that periodically
calls MAM's Dynamic Seedbox API to register your current VPN IP.

Add to your docker-compose.yml:

```yaml
services:
  # ... gluetun and qbittorrent as above ...

  mousehole:
    image: tmmrtn/mousehole:latest
    container_name: mousehole
    network_mode: "service:gluetun"
    depends_on:
      gluetun:
        condition: service_healthy
    environment:
      TZ: America/Chicago
      MOUSEHOLE_AUTH_PASSWORD: your-strong-password
    volumes:
      - mousehole:/var/lib/mousehole
    restart: unless-stopped

volumes:
  mousehole:
```

Also add `- 127.0.0.1:5010:5010` to gluetun's `ports:` and add `,5010` to
`FIREWALL_VPN_INPUT_PORTS`.

### Step 3: Configure Mousehole

1. Browse to **http://localhost:5010**
2. Log in with the password set in `MOUSEHOLE_AUTH_PASSWORD`
3. Get your MAM session cookie:
   - Log in to MAM in your browser
   - DevTools → Application/Storage → Cookies → `myanonamouse.net`
   - Copy the **session** cookie value
4. Paste it into Mousehole's web UI
5. Click **Update Now** to immediately register your current VPN IP

Mousehole will now automatically keep MAM updated with your VPN IP on a schedule.

### MAM Tracker Info

- Allowed qBittorrent: from 5.0.1 to latest 5.2.x line
- qBittorrent Anonymous Mode will break tracker communication
- The tracker returns bencoded responses; HTTP 200 + "failure reason" means the tracker
  accepted the request but rejected the client/IP/credentials
- Passkey is embedded in the announce URL, not sent as a GET parameter

## Port Blacklist Notes

MAM (and many private trackers) block common P2P ports (6881-6889, etc.).
Configure qBittorrent to use the VPN's forwarded port instead of defaults.

## Troubleshooting

### Tracker returns "Non-Whitelisted client or version"
- Check the MAM Allowed Clients page: the client version may not be whitelisted yet
- Even if the web page says it's allowed, the tracker filter may lag behind
- Verify qBittorrent is not in Anonymous Mode

### Tracker returns "Unrecognized host/PassKey"
- Browser IP ≠ torrent client IP
- Use Mousehole (Dynamic Seedbox API) to register the torrent client's IP

### qBittorrent logging shows no tracker announcements
- Check that the torrent isn't paused
- Verify qBittorrent's listening port matches the VPN forwarded port
- Confirm `disable_dht = 0` is set correctly in the fastresume for private torrents
  (private torrents should have DHT disabled)

### Torrent stalled with no peers
- Confirm port forwarding is active via `docker exec gluetun cat /tmp/gluetun/forwarded_port`
- Test tracker reachability: `docker exec qbittorrent curl -s -o /dev/null -w "%{http_code}" <tracker_url>`
- Verify the passkey in the announce URL is correct (check fastresume via `strings`)

## References

- [Mousehole GitHub](https://github.com/t-mart/mousehole)
- [Gluetun Wiki](https://github.com/qdm12/gluetun-wiki)
- [MAM FAQ — Unrecognized host/PassKey](https://s.mrd.ninja/upmm)
- [MAM Allowed Clients](https://s.mrd.ninja/CLNTe)
