Skip to content

Just sharing my complete working stack for ProtonVPN + Transmission #11

@mrjay42

Description

@mrjay42

Here's a complete working ProtonVPN + Transmission docker compose stack

# ======================================================================================
# COMMUNITY STACK: GLUETUN (PROTONVPN WITH NAT-PMP) + TRANSMISSION TORRENT CLIENT
# ARCHITECTURE: Transmission shares Gluetun's network namespace (All traffic via VPN).
# AUTOMATION: Dynamic port changes are caught by Gluetun and pushed to Transmission RPC.
# ======================================================================================

services:
  gluetun-proton:
    image: qmcgaw/gluetun:latest
    container_name: gluetun-proton
    # NET_ADMIN capability is mandatory for Gluetun to manipulate routing tables and firewall rules
    cap_add:
      - NET_ADMIN
    restart: unless-stopped
    networks:
      - private_secured_network

    environment:
      - TZ=UTC # Change to your timezone (e.g., Europe/Paris, America/New_York)
      - VPN_SERVICE_PROVIDER=custom
      - VPN_TYPE=wireguard
      
      # --- PROTONVPN NAT-PMP CONFIGURATION ---
      # Tells Gluetun to request an open incoming peer port via NAT-PMP protocols
      - VPN_PORT_FORWARDING=on
      - VPN_PORT_FORWARDING_PROVIDER=protonvpn
      
      # --- LOCAL FIREWALL BYPASS RULES ---
      # Adjust to your local physical LAN layout to ensure you can access the web UI locally
      - FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24
      # Exposes Transmission's Web UI/RPC port inside Gluetun's firewall layer
      - FIREWALL_INPUT_PORTS=9091

      # --- GLUETUN HEALTH CONTROL SERVER ---
      - HTTP_CONTROL_SERVER=on
      - HTTP_CONTROL_SERVER_PORT=9999

      # --- THE AUTOMATION HOOK (THE BRAINS) ---
      # When the tunnel goes UP and Proton grants a port, this command fires instantly.
      # 1. Installs 'transmission-remote' natively within Gluetun's lightweight Alpine base.
      # 2. Uses template variable {{PORTS}} (Gluetun auto-substitutes this with the real port number).
      # 3. Connects via localhost (since network namespaces are shared) to inject the peer port.
      # 4. Forces an immediate reannounce on all active torrents to broadcast the open port to peers.
      # NOTE: Ensure the '-n user:pass' arguments match the USER and PASS environment values below.
      - VPN_PORT_FORWARDING_UP_COMMAND=/bin/sh -c "apk add --no-cache transmission-remote && transmission-remote localhost:9091 -n YOUR_RPC_USERNAME:YOUR_RPC_PASSWORD -p {{PORTS}} && transmission-remote localhost:9091 -n YOUR_RPC_USERNAME:YOUR_RPC_PASSWORD -t all --reannounce"

    volumes:
      # Mount your custom ProtonVPN WireGuard config file (Ensure 'Port Forwarding' is checked in Proton Dashboard)
      - /path/to/your/apps/vpnProton/wg0.conf:/gluetun/wireguard/wg0.conf:ro
      # Dynamic state storage for Gluetun container
      - gluetun-proton-state:/gluetun

    # --- TRAEFIK REVERSE PROXY LABELS ---
    # Routed directly off the gluetun container because Transmission shares its network interface.
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=private_secured_network"
      # Change your entrypoints, resolvers, and domain hosts to fit your production infrastructure
      - "traefik.http.routers.transmission-proton.rule=Host(`torrents.example.com`)"
      - "traefik.http.routers.transmission-proton.entrypoints=websecure"
      - "traefik.http.routers.transmission-proton.tls.certresolver=yourresolver"
      - "traefik.http.services.transmission-proton.loadbalancer.server.port=9091"

    healthcheck:
      test: ["CMD", "nc", "-z", "127.0.0.1", "9999"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 10s

  transmission-proton:
    image: lscr.io/linuxserver/transmission:latest
    container_name: transmission-proton
    restart: unless-stopped

    # --- THE SECURE BUBBLE CRITICAL SETTING ---
    # Tells Docker to place Transmission completely inside Gluetun's network space.
    # It will have no independent IP, ensuring zero traffic can bypass the VPN tunnel.
    network_mode: "service:gluetun-proton"

    # Keeps the container from executing until Gluetun verifies its tunnel health check is active
    depends_on:
      gluetun-proton:
        condition: service_healthy

    environment:
      - PUID=1000 # Modify to your host user's UID to prevent file permission issues
      - PGID=1000 # Modify to your host user's GID to prevent file permission issues
      - TZ=UTC    # Match with your system timezone
      - USER=YOUR_RPC_USERNAME # Secure login for WebUI/RPC (Must match Hook command above)
      - PASS=YOUR_RPC_PASSWORD # Secure password for WebUI/RPC (Must match Hook command above)

    volumes:
      # Application runtime configuration data
      - /path/to/your/apps/vpnProton/transmission-config:/config
      
      # Persistent Storage Volumes (Map to your host infrastructure media folders)
      - /path/to/your/storage/downloads/completed:/downloads/completed
      - /path/to/your/storage/downloads/incomplete:/downloads/incomplete
      - /path/to/your/storage/downloads/torrents:/downloads/torrents
      - /path/to/your/storage/downloads/watch:/watch
      
      # Example Media sub-category bindings (Uncomment or adapt to add media types)
      # - /path/to/your/storage/media/movies:/downloads/completed/movies
      # - /path/to/your/storage/media/tv:/downloads/completed/tv
      # - /path/to/your/storage/media/audiobooks:/downloads/completed/audiobooks

# --- EXTERNAL NETWORK REQUIREMENT ---
# Uses the pre-existing external network where your Traefik proxy is deployed.
networks:
  private_secured_network:
    external: true

# --- LOCAL NAMED VOLUMES ---
volumes:
  # Isolated named volume keeping Gluetun state information persistent between image updates
  gluetun-proton-state:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions