An all-in-one Docker container that provides an Alpine-based Transmission Bittorrent client with an OpenVPN or WireGuard connection, plus Privoxy, and DNS-over-HTTPS. It includes a health check to monitor the VPN connection and restart the container if it fails.
- π‘οΈ Enhanced Kill Switch: Strict iptables rules with default DROP policies prevent any IP leaks
- π DNS Leak Prevention: Blocks all DNS queries (port 53) on non-VPN interfaces
- ποΈ Active VPN Monitoring: Continuously monitors VPN health and stops Transmission if VPN fails
- β‘ Auto-Recovery: Optional automatic VPN restart on failure (configurable)
- π₯ Smart Health Checks: Container health focuses on Transmission functionality, not VPN status
- β‘ Lightweight: Built on Alpine Linux for a minimal footprint
- π§© Extensible: Supports custom scripts and alternative web UIs
- π Metrics & Monitoring: Built-in Prometheus-compatible metrics endpoint
- π DNS Over-HTTPS: Encrypts DNS queries to prevent snooping
This project follows a versioning scheme aligned with the upstream LinuxServer.io transmission releases:
4.0.6- Matches the upstream Transmission version from LinuxServer.io-rX- Our revision number (increments with each release)
4.0.6-r1- First release based on Transmission 4.0.64.0.6-r2- Second release with bug fixes/features4.0.6-r7- Release with comprehensive monitoring stack, Prometheus fixes, troubleshooting tools4.0.6-r20- Current release (DNS fixes, updated base image and dependencies)
- Major/Minor: When LinuxServer.io updates Transmission (e.g.,
4.0.6β4.1.0) - Revision: For our bug fixes, features, or documentation updates
This ensures compatibility with the upstream base image while tracking our enhancements.
Run the container with this command:
docker run -d \
--cap-add=NET_ADMIN \
--device=/dev/net/tun \
--name=transmissionvpn \
-p 9091:9091 \
-v ./config:/config \
-v ./downloads:/downloads \
-v ./watch:/watch \
-e VPN_CLIENT=openvpn \
-e VPN_CONFIG=/config/openvpn/your_provider.ovpn \
-e VPN_USER=your_vpn_username \
-e VPN_PASS=your_vpn_password \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=America/New_York \
magicalyak/transmissionvpn:latestversion: "3.8"
services:
transmissionvpn:
image: magicalyak/transmissionvpn:latest
container_name: transmissionvpn
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "9091:9091"
volumes:
- ./config:/config
- ./downloads:/downloads
- ./watch:/watch
environment:
- VPN_CLIENT=openvpn
- VPN_CONFIG=/config/openvpn/provider.ovpn
- VPN_USER=your_username
- VPN_PASS=your_password
- PUID=1000
- PGID=1000
- TZ=America/New_York
- LAN_NETWORK=192.168.1.0/24
restart: unless-stopped| Variable | Function | Example |
|---|---|---|
VPN_CLIENT |
VPN type (openvpn/wireguard) | openvpn |
VPN_CONFIG |
Path to VPN config file | /config/openvpn/provider.ovpn |
VPN_USER |
VPN username (OpenVPN only) | your_username |
VPN_PASS |
VPN password (OpenVPN only) | your_password |
| Variable | Function | Default | Example |
|---|---|---|---|
PUID |
User ID for file permissions | 1000 |
1000 |
PGID |
Group ID for file permissions | 1000 |
1000 |
TZ |
Timezone | UTC |
America/New_York |
LAN_NETWORK |
Local network CIDR | (none) | 192.168.1.0/24 |
ENABLE_PRIVOXY |
Enable HTTP proxy | no |
yes |
DEBUG |
Enable debug logging | false |
true |
TRANSMISSION_WEB_UI_AUTO |
Auto-download web UI | (none) | flood |
METRICS_ENABLED |
Enable built-in custom metrics server | false |
true |
METRICS_PORT |
Prometheus metrics port | 9099 |
8080 |
METRICS_INTERVAL |
Metrics update interval (seconds) | 30 |
60 |
TRANSMISSION_PEER_PORT |
BitTorrent peer port | (none) | 51413 |
PRIVOXY_PORT |
Privoxy HTTP proxy port | 8118 |
8119 |
INTERNAL_METRICS_ENABLED |
Enable internal health metrics | false |
true |
CHECK_DNS_LEAK |
Enable DNS leak detection | false |
true |
CHECK_IP_LEAK |
Enable IP leak detection | false |
true |
PIA_PORT_FORWARD |
Enable PIA port forwarding (requires non-US server) | false |
true |
| Volume | Function | Example |
|---|---|---|
/config |
Configuration files and VPN configs | ./config:/config |
/downloads |
Completed downloads | ./downloads:/downloads |
/watch |
Auto-add torrent files | ./watch:/watch |
| Port | Function | Required | Configurable |
|---|---|---|---|
9091 |
Transmission Web UI | Yes | No |
8118 |
Privoxy HTTP proxy | No | Via PRIVOXY_PORT |
9099 |
Prometheus metrics endpoint | No | Via METRICS_PORT |
51413 |
BitTorrent peer port | No | Via TRANSMISSION_PEER_PORT |
Dynamic Port Configuration:
- Metrics Port: Set
METRICS_PORT=8080to use port 8080 instead of 9099 - BitTorrent Port: Set
TRANSMISSION_PEER_PORT=6881to use port 6881 instead of 51413 - Privoxy Port: Set
PRIVOXY_PORT=8119to use port 8119 instead of 8118
The container automatically configures iptables rules for your custom ports. No manual firewall configuration needed!
mkdir -p transmissionvpn/{config/openvpn,downloads,watch}
cd transmissionvpnPlace your VPN provider's .ovpn file in config/openvpn/ directory.
Note: The container will fail to start if VPN_CLIENT is set and no valid config file is present.
docker-compose up -dOpen http://localhost:9091 in your browser.
# Run the verification script
./scripts/verify-fixes.sh
# Or manually check
docker exec transmissionvpn /root/healthcheck-fixed.shThis container provides two complementary monitoring systems:
Built-in custom metrics server that exposes Transmission metrics directly from within the container. No separate services required! This replaces the problematic transmission-exporter with a reliable Python-based solution.
-
Enable the metrics server in your
.envfile:METRICS_ENABLED=true METRICS_PORT=9099 METRICS_INTERVAL=30
-
Expose the port in your
docker-compose.yml:ports: - "9091:9091" # Transmission Web UI - "9099:9099" # Prometheus metrics (if enabled)
-
Start the container:
docker-compose up -d
-
Verify metrics are available:
curl http://localhost:9099/metrics curl http://localhost:9099/health
Add this scrape configuration to your prometheus.yml:
scrape_configs:
- job_name: 'transmissionvpn'
static_configs:
- targets: ['localhost:9099'] # or your Docker host IP
scrape_interval: 30sThe custom metrics server provides these key metrics for dashboards:
transmission_torrent_count- Total number of torrentstransmission_active_torrents- Number of active torrentstransmission_downloading_torrents- Number of downloading torrentstransmission_seeding_torrents- Number of seeding torrentstransmission_download_rate_bytes_per_second- Current download ratetransmission_upload_rate_bytes_per_second- Current upload ratetransmission_session_downloaded_bytes- Session downloaded bytestransmission_session_uploaded_bytes- Session uploaded bytes
β
Reliable: No hanging issues like transmission-exporter
β
Lightweight: Pure Python, minimal dependencies
β
Session-aware: Properly handles Transmission's CSRF protection
β
Environment-driven: Configured via environment variables
β
Health endpoint: /health for monitoring checks
β
Error-resilient: Continues working even if Transmission restarts
Internal metrics collection for system monitoring and debugging. These metrics are stored in /tmp/metrics.txt inside the container.
INTERNAL_METRICS_ENABLED=true # Enable internal metrics collection
CHECK_DNS_LEAK=true # Enable DNS leak detection
CHECK_IP_LEAK=true # Enable IP leak detection- System Health: CPU usage, memory usage, disk usage
- VPN Status: Interface status, connectivity, ping times
- Network: Total RX/TX bytes, DNS resolution times
- Transmission: Response times, active torrents count
- Security: IP leak detection, DNS leak detection
# View current metrics
docker exec transmissionvpn cat /tmp/metrics.txt
# View health logs
docker exec transmissionvpn cat /tmp/healthcheck.logInfluxDB v2 Scraper:
- Target URL:
http://your-host:9099/metrics - Schedule:
30s(or desired interval) - Bucket: Choose your metrics bucket
Docker Networks: If running Prometheus in Docker, ensure both containers are on the same network:
networks:
monitoring:
external: true| Feature | Custom Metrics Server | Internal Health Metrics |
|---|---|---|
| Purpose | External monitoring | Internal debugging |
| Format | Prometheus standard | Custom text format |
| Access | HTTP endpoint | File inside container |
| Metrics | Transmission-focused | System health focused |
| Use Case | Grafana dashboards | Troubleshooting |
| Default | Disabled | Disabled |
| Reliability | High (custom solution) | High |
This container works with any OpenVPN or WireGuard provider:
- NordVPN - Download configs from account dashboard
- ExpressVPN - Get configs from manual setup page
- Surfshark - Download from manual setup section
- ProtonVPN - Get configs from downloads page
- Private Internet Access - Download from client support
- PrivadoVPN - Generate configs from account dashboard
- Mullvad - Generate configs from account page
π Provider-specific guides: VPN Setup Documentation
Private Internet Access (PIA) supports port forwarding on select servers. This allows incoming BitTorrent connections, improving download speeds and peer connectivity.
To enable PIA port forwarding:
environment:
- VPN_CLIENT=openvpn
- VPN_CONFIG=/config/openvpn/ca_toronto.ovpn # Must be non-US server!
- VPN_USER=your_pia_username
- VPN_PASS=your_pia_password
- PIA_PORT_FORWARD=trueImportant notes:
- Port forwarding is disabled on US servers by PIA policy
- Use servers like: CA Toronto, CA Montreal, Netherlands, Switzerland, Germany, UK, Sweden
- The forwarded port is dynamic and assigned by PIA (not configurable)
- The container automatically configures Transmission to use the forwarded port
- A keepalive runs every 15 minutes to maintain the port binding
- Check
/tmp/pia_forwarded_portinside the container for the assigned port
Supported servers for port forwarding:
- Canada (Toronto, Montreal, Vancouver)
- Europe (Netherlands, Switzerland, Germany, France, UK, Sweden, etc.)
- Asia Pacific (Singapore, Japan, Australia)
- South America (Brazil, Mexico)
This image supports several alternative web UIs for Transmission. To use one, set the TRANSMISSION_WEB_UI environment variable to one of the following values:
flood- Modern, feature-rich UI for torrent management.kettu- Clean and responsive web interface.combustion- Sleek, modern, and mobile-friendly.transmission-web-control- A popular alternative with more advanced features.
- β Automatic download during the build process.
- β No additional volumes or configuration is required.
- β Seamless integration with the base image.
- β Always up-to-date with the latest version of the UI.
For custom UIs, mount your files and use TRANSMISSION_WEB_HOME:
volumes:
- ./my-custom-ui:/web-ui:ro
environment:
- TRANSMISSION_WEB_HOME=/web-ui- Download your provider's
.ovpnfile - Place it in
config/openvpn/directory - Set
VPN_CLIENT=openvpn - Provide your credentials via
VPN_USERandVPN_PASS
Tip: You can also use auth-user-pass credentials.txt in your .ovpn file.
For WireGuard, additional requirements apply:
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
environment:
- VPN_CLIENT=wireguard
- VPN_CONFIG=/config/wireguard/wg0.confNote: No username/password needed for WireGuard.
The container implements a multi-layer kill switch to prevent IP leaks:
- Strict iptables rules - Default DROP policies on all chains
- DNS leak prevention - Blocks port 53 on all non-VPN interfaces
- Active monitoring - VPN monitor service checks connectivity every 30 seconds
- Automatic protection - Stops Transmission immediately if VPN fails
# Run the automated test script
./test-killswitch.sh
# Or manually verify
docker exec transmissionvpn /usr/local/bin/vpn-killswitch.sh verifyThe VPN monitor service continuously checks:
- VPN interface status
- Network connectivity through VPN
- DNS resolution (optional)
- External IP verification (optional)
Configure monitoring behavior:
environment:
- VPN_CHECK_INTERVAL=30 # Check every 30 seconds
- VPN_MAX_FAILURES=3 # Stop after 3 failures
- AUTO_RESTART_VPN=true # Auto-restart VPN on failureThe container includes automatic health monitoring:
- VPN Connectivity - Verifies tunnel is active
- IP Leak Protection - Blocks traffic if VPN fails
- DNS Leak Prevention - Routes DNS through VPN
- Kill Switch Status - Monitors firewall rules
Check container health:
docker exec transmissionvpn /root/healthcheck.sh# Check logs
docker logs transmissionvpn
# Common issues:
# - Missing VPN config file
# - Incorrect credentials
# - Missing NET_ADMIN capability# Check external IP
docker exec transmissionvpn curl ifconfig.me
# Verify VPN connection
docker exec transmissionvpn ping -c 3 8.8.8.8π Complete troubleshooting: Troubleshooting Guide
- Configuration Examples - Real-world usage examples
- VPN Provider Setup - Provider-specific guides
- Docker Secrets - Secure credential management
- Troubleshooting - Common issues and solutions
| haugene | transmissionvpn |
|---|---|
OPENVPN_PROVIDER |
Use VPN_CONFIG with .ovpn file |
OPENVPN_USERNAME |
VPN_USER |
OPENVPN_PASSWORD |
VPN_PASS |
LOCAL_NETWORK |
LAN_NETWORK |
haugene/docker-transmission-openvpn uses different paths than the LinuxServer.io base:
| Directory Purpose | haugene Path | transmissionvpn Path |
|---|---|---|
| Main Volume | /data |
/downloads |
| Completed Downloads | /data/completed/ |
/downloads/complete/ |
| Incomplete Downloads | /data/incomplete/ |
/downloads/incomplete/ |
transmissionvpn v4.0.6-2+ automatically creates compatibility symlinks during container startup:
/downloads/completedβ/downloads/complete/dataβ/downloads
This means both directory structures work simultaneously without requiring configuration changes! Your existing Sonarr/Radarr setup should continue working after migration.
This image includes a compatibility layer that automatically maps many of haugene's environment variables to the new ones. For most users, this will be enough.
# NEW: LinuxServer.io compatible structure
volumes:
- /your/downloads:/downloads # Downloads go to /your/downloads/complete/
- /your/incomplete:/downloads/incomplete# ALTERNATIVE: Map to match haugene's structure
volumes:
- /your/downloads:/downloads/completed # Downloads go directly to /your/downloads/
- /your/incomplete:/downloads/incompleteIf you're getting "directory does not appear to exist inside the container" errors:
-
Check Download Client Settings:
- Host:
transmissionvpn(or your container name) - Port:
9091 - Category: Set appropriate category for TV/Movies
- Directory: Leave empty or use
/downloads/complete/
- Host:
-
Configure Remote Path Mappings:
Host: transmissionvpn Remote Path: /downloads/ Local Path: /media/downloads/ (or your host path) -
Ensure Consistent Volume Mapping:
# Both containers must see the same paths transmissionvpn: volumes: - /media/downloads:/downloads sonarr: volumes: - /media/downloads:/media/downloads
- Check logs:
docker logs transmissionvpn - Read docs: Complete guides linked above
- Search issues: GitHub Issues
- Create issue: Use our templates
Click to expand for a full list of environment variables
| Variable | Description | Default |
|---|---|---|
VPN_CLIENT |
openvpn or wireguard |
openvpn |
VPN_CONFIG |
Path to VPN config file | (required) |
VPN_USER |
VPN username | (required for OpenVPN) |
VPN_PASS |
VPN password | (required for OpenVPN) |
VPN_OPTIONS |
Additional VPN options | (none) |
| Variable | Description | Default |
|---|---|---|
VPN_CHECK_INTERVAL |
Seconds between VPN health checks | 30 |
VPN_MAX_FAILURES |
Max failures before stopping Transmission | 3 |
VPN_INITIAL_DELAY |
Seconds to wait after VPN setup before monitoring | 15 |
CHECK_DNS |
Enable DNS resolution testing | true |
CHECK_EXTERNAL_IP |
Verify external IP through VPN | true |
AUTO_RESTART_VPN |
Auto-restart VPN on failure | false |
| Variable | Description | Default |
|---|---|---|
LAN_NETWORK |
Local network CIDR | (none) |
NAME_SERVERS |
DNS servers | (auto) |
ADDITIONAL_PORTS |
Extra ports | (none) |
| Variable | Description | Default |
|---|---|---|
TRANSMISSION_PEER_PORT |
P2P port | (random) |
TRANSMISSION_DOWNLOAD_DIR |
Download directory | /downloads |
TRANSMISSION_WATCH_DIR |
Watch directory | /watch |
TRANSMISSION_WEB_HOME |
Alternative web UI | (none) |
| Variable | Description | Default |
|---|---|---|
PUID |
User ID | 1000 |
PGID |
Group ID | 1000 |
TZ |
Timezone | UTC |
UMASK |
File creation mask | 002 |
| Variable | Description | Default |
|---|---|---|
ENABLE_PRIVOXY |
HTTP proxy | no |
DEBUG |
Debug logging | false |
METRICS_ENABLED |
Built-in custom metrics server | false |
METRICS_PORT |
Metrics endpoint port | 9099 |
METRICS_INTERVAL |
Metrics update interval (seconds) | 30 |
INTERNAL_METRICS_ENABLED |
Internal health metrics | false |
CHECK_DNS_LEAK |
DNS leak detection | false |
CHECK_IP_LEAK |
IP leak detection | false |
TRANSMISSION_WEB_UI_AUTO |
Auto-download web UI | (none) |
A huge thank you to the developers of haugene/transmission-openvpn for their incredible work over the years. This project would not be possible without their efforts.