RPi5 Home Server — Part 3: VPN, Remote Access & Private Downloads
Part 3 of the RPi5 home server series. Set up Gluetun as a Mullvad VPN gateway, route qBittorrent and MeTube through it with a kill switch, and install Tailscale for secure remote access behind CGNAT.
This is Part 3 of the Raspberry Pi 5 Home Server series. This part covers the privacy and remote access layer — a VPN gateway that protects download traffic, and a zero-config remote access solution that works behind CGNAT.
This guide uses Mullvad as the VPN provider — it's what runs on this server and what every configuration here has been tested against. Gluetun supports many other providers including NordVPN, ExpressVPN, ProtonVPN, Surfshark, and more. The Docker Compose structure and kill switch behavior are identical regardless of provider — only the environment variables change. See the Gluetun wiki for your provider's specific configuration.
What We're Installing
| Service | Purpose | Port |
|---|---|---|
| Gluetun | Mullvad VPN gateway for containers | Internal |
| qBittorrent | Torrent client routed through Gluetun | 8090 |
| MeTube | Video downloader routed through Gluetun | 8081 |
| Tailscale | Secure remote access — bypasses CGNAT | Native |
Understanding the Architecture
Before installing anything, it's worth understanding how these services relate to each other:
Internet
|
v
Mullvad VPN (via Gluetun)
|
+-- qBittorrent
+-- MeTube
|
v
/mnt/data/storage/DownloadsGluetun runs as a dedicated VPN client container. Instead of routing all Pi traffic through a VPN, Gluetun acts as a network gateway that only specific containers use. Everything else — Homepage, Filebrowser, Pi-hole — continues using your normal internet connection.
The kill switch — if the Mullvad connection drops, Gluetun immediately blocks all network traffic from containers using it. qBittorrent and MeTube cannot leak your real IP even during a VPN reconnect.
Tailscale is completely independent — it gives you remote access to your Pi from anywhere in the world, solving the CGNAT problem that prevents traditional port forwarding on mobile internet providers.
Gluetun — Mullvad VPN Gateway
1. Get your Mullvad WireGuard credentials
- Log into mullvad.net and go to Manage Account
- Navigate to WireGuard configuration
- Generate a new key pair — copy the Private Key
- Note the WireGuard address assigned to your key (looks like
10.x.x.x/32)
2. Create the directory structure
mkdir -p /mnt/data/docker-compose/appdata/gluetun
mkdir -p /mnt/data/storage/Downloads/torrents
mkdir -p /mnt/data/storage/Downloads/metube3. Docker Compose configuration
Add this to your docker-compose.yml under services::
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=your_mullvad_wireguard_private_key
- WIREGUARD_ADDRESSES=10.x.x.x/32
- SERVER_COUNTRIES=Netherlands
- TZ=Etc/UTC
volumes:
- /mnt/data/docker-compose/appdata/gluetun:/gluetun
ports:
- "8090:8090"
- "8081:8081"Replace your_mullvad_wireguard_private_key with your actual private key
and 10.x.x.x/32 with the address assigned to your key. Never share your
private key or commit it to version control.
SERVER_COUNTRIES=Netherlands sets the VPN exit location. Change this to
any country Mullvad supports — Sweden, United States, Switzerland,
etc. See the Gluetun wiki
for supported values.
Why NET_ADMIN and /dev/net/tun? Gluetun creates a virtual TUN
network interface to route traffic through the VPN tunnel. NET_ADMIN
allows it to manage network interfaces and /dev/net/tun is the Linux
kernel device it uses to create the tunnel. Both are required for VPN
functionality.
Why are ports declared on Gluetun? Containers using
network_mode: service:gluetun have no network of their own — they share
Gluetun's entirely. All ports for those containers must be declared in the
Gluetun block.
4. Verify Gluetun is working
Deploy Gluetun first before adding dependent containers:
cd /mnt/data/docker-compose
docker compose up -d gluetunCheck the logs to confirm the VPN connected:
docker compose logs gluetunLook for:
Connected to Mullvad with WireGuard
Your IP is: x.x.x.xIf you see this, the VPN tunnel is up and you're ready to proceed.
qBittorrent — Torrent Client via VPN
1. Docker Compose configuration
Add this after the Gluetun block in docker-compose.yml:
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
network_mode: "service:gluetun"
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- WEBUI_PORT=8090
volumes:
- /mnt/data/docker-compose/appdata/qbittorrent:/config
- /mnt/data/storage/Downloads/torrents:/downloads
depends_on:
- gluetunnetwork_mode: "service:gluetun" is the kill switch mechanism. qBittorrent
shares Gluetun's network stack entirely — if Gluetun stops for any reason,
qBittorrent immediately loses all network access. There is no fallback to
your home IP.
Deploy:
cd /mnt/data/docker-compose
docker compose up -d gluetun qbittorrentAccess the qBittorrent web UI at http://192.168.1.150:8090
Default credentials: admin / adminadmin
Change the default password immediately. Go to Tools → Options → Web UI and set a strong password.
2. Verify the kill switch
Confirm qBittorrent's traffic goes through Mullvad:
docker exec -it qbittorrent curl ifconfig.meThe returned IP must be a Mullvad IP — not your home IP. If it shows your home IP, the VPN routing is not working. Check Gluetun logs.
3. Configure qBittorrent
- Downloads path — Go to Tools → Options → Downloads and set the
default save path to
/downloads - Connection limits — Go to Tools → Options → Connection. A maximum of 200 connections and 50 per torrent is a sensible starting point
- Speed limits — Go to Tools → Options → Speed and set upload limits appropriate for your connection
4. Homepage integration
- Tools:
- qBittorrent:
icon: qbittorrent.png
href: http://192.168.1.150:8090
description: Torrent Client (via Mullvad)
widget:
type: qbittorrent
url: http://192.168.1.150:8090
username: admin
password: your_passwordMeTube — Video Downloader via VPN
MeTube is a web interface for yt-dlp — the most capable video downloader
available. It supports YouTube and hundreds of other platforms, downloading
directly to your NVMe drive without involving your main computer.
Routing MeTube through Gluetun means all downloads go through Mullvad — your home IP is never exposed to video platforms or their CDN infrastructure.
1. Docker Compose configuration
Add this after the qBittorrent block in docker-compose.yml:
metube:
image: ghcr.io/alexta69/metube:latest
container_name: metube
restart: unless-stopped
network_mode: "service:gluetun"
volumes:
- /mnt/data/storage/Downloads/metube:/downloads
environment:
- PUID=1000
- PGID=1000
- DELETE_FILE_ON_TRASHCAN=true
depends_on:
- gluetunDeploy all three Gluetun-dependent containers together:
cd /mnt/data/docker-compose
docker compose up -d gluetun qbittorrent metubeAccess MeTube at http://192.168.1.150:8081
2. Verify the VPN is active
docker exec -it metube wget -qO- ifconfig.meThe returned IP should be a Mullvad IP — not your home IP.
3. Using MeTube
Paste any video URL into the input field and click Add. MeTube queues the download and shows real-time progress.
Useful options:
- Format — video+audio, audio only, or specific quality levels. Audio-only downloads as MP3 or M4A
- Subtitles — optionally download and embed subtitles
- Auto-delete — with
DELETE_FILE_ON_TRASHCAN=true, trashing a completed download in the UI permanently deletes it from disk
4. Homepage integration
- Tools:
- MeTube:
icon: metube.png
href: http://192.168.1.150:8081
description: Video Downloader (via Mullvad)Tailscale — Remote Access Behind CGNAT
CGNAT (Carrier-Grade NAT) is common with mobile internet providers like T-Mobile. It means your connection shares a public IP with many other customers — making traditional port forwarding impossible since you don't have a dedicated public IP.
Tailscale creates an encrypted mesh network between your devices using a relay infrastructure. Your Pi and your phone connect to each other through Tailscale's coordination servers without needing any inbound ports open on your router.
1. Install Tailscale natively
Tailscale is installed directly on the Pi — not as a Docker container. Native installation allows the Pi to act as a subnet router and exit node more reliably than the containerized version.
curl -fsSL https://tailscale.com/install.sh | shAuthenticate:
sudo tailscale upA login URL appears in the terminal. Copy it into your browser to link the Pi to your Tailscale account.
2. Verify the connection
tailscale statusYour Pi should appear as online with a Tailscale IP in the 100.x.x.x
range.
3. Configure the admin console
Two settings in the Tailscale Admin Console are worth configuring immediately:
Disable key expiry — by default, Tailscale keys expire every 180 days. For a home server, click the three dots next to your Pi and select Disable key expiry so it never goes offline unexpectedly.
Enable MagicDNS — in the DNS tab, enable MagicDNS. This assigns your Pi a hostname on the Tailscale network so you can access it by name:
http://pi-server:3000 ← Homepage
http://pi-server:8080 ← Filebrowser
http://pi-server:5000 ← Kavita4. Access your server remotely
- Install Tailscale on your phone, tablet, or laptop from tailscale.com/download
- Log in with the same account used on the Pi
- Access any service using the Pi's Tailscale IP (
100.x.x.x) and the service port — from anywhere in the world
Tailscale only connects your enrolled devices to each other. Your other home devices remain invisible to the Tailscale network unless you specifically configure subnet routing. Your home network stays private.
5. Use the Pi as a VPN exit node (optional)
To route all traffic from your phone through your home connection when on public Wi-Fi:
sudo tailscale up --advertise-exit-nodeApprove the exit node in the admin console, then enable it from the Tailscale app on your device.
This is distinct from Gluetun/Mullvad. Gluetun routes specific containers through a commercial VPN provider. Tailscale exit node routes your device's traffic through your home internet connection. Both are useful — they serve different purposes.
Troubleshooting
Gluetun fails to connect
docker compose logs gluetunCommon causes:
- Wrong private key — verify against your Mullvad account
- Wrong
WIREGUARD_ADDRESSES— must match the address assigned to your key - Server country unavailable — try a different
SERVER_COUNTRIESvalue
qBittorrent or MeTube web UI is unreachable
Since both share Gluetun's network, if Gluetun is down neither is reachable. Always check Gluetun first:
docker compose logs gluetun
docker compose restart gluetunqBittorrent or MeTube shows my real IP
Bring down all Gluetun-dependent containers and redeploy together:
docker compose down gluetun qbittorrent metube
docker compose up -d gluetun qbittorrent metubeMeTube download fails immediately
Pull a fresh image — MeTube updates frequently to keep pace with platform changes:
docker compose pull metube
docker compose up -d metubeTailscale shows the Pi as offline
sudo systemctl status tailscaled
sudo systemctl restart tailscaled
sudo tailscale upCan't reach Pi services via Tailscale
Confirm Tailscale is connected and note the IP:
tailscale statusThen verify your containers are running:
docker psLegal Notice
Gluetun, Mullvad, qBittorrent, MeTube, and Tailscale are legitimate tools with many lawful uses. This guide is provided for educational and personal use. You are responsible for ensuring your use complies with your local laws and the terms of service of any platforms you interact with.