Skip to content

Lackoftactics/uncompressed

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

uncompressed

My arr stack. Hardened Docker Compose config for Jellyfin + Sonarr/Radarr + qBittorrent with VPN namespace isolation and zero-trust ingress.

I run this on Unraid. It took a few months to get the networking right — most guides just slap a firewall rule on the VPN and call it a day. I wanted actual isolation, not "it probably works." Here's what I landed on.

My family uses Seerr to request movies/shows and Infuse on Apple TV to watch them.

Jellyfin media library Radarr movie management Sonarr TV show management

Quick Start

cp .env.example .env              # configure credentials and domain
docker network create traefik_proxy

cd infra && docker compose up -d  # traefik first
cd ../arr && docker compose up -d # everything else

You need Docker + Compose, a Tailscale account, a ProtonVPN account with WireGuard keys, and a domain on Cloudflare DNS. See .env.example for all the variables.

Networking & Security

This is the part that's actually interesting. The services themselves are standard — the value is in how they're wired together.

VPN namespace isolation — qBittorrent doesn't just "use" the VPN. It runs inside gluetun's network namespace (network_mode: service:gluetun), meaning it shares gluetun's entire network stack. The container has no network interface of its own. An init script (10-config.sh) additionally forces BIND_TO_INTERFACE: tun0 as defense in depth. If the VPN drops, there is no path for traffic to take — it's a kernel boundary, not a firewall rule that could be misconfigured.

No published ports — None of the services expose ports to the host. Traefik routes to containers through the Docker network directly. There's no way to hit Sonarr/Radarr/Jellyfin by going to host:port and bypassing TLS + security headers.

Tailscale-only ingress — Traefik binds to ${TAILSCALE_IP}:443, not 0.0.0.0:443. You must be on the Tailscale mesh to reach any service. No ports face the public internet. HTTPS certs are auto-renewed via Cloudflare DNS challenge.

Three isolated networkstraefik_proxy for HTTPS ingress, arr_internal (marked internal: true) for service-to-service, vpn_network for tunnel traffic. Port forwarding from ProtonVPN is automatic — gluetun gets the forwarded port and pushes it to qBittorrent's API.

Architecture

                       ┌────────────┐
                       │ Cloudflare │
                       │    DNS     │
                       └─────┬──────┘
                             │
                       ┌─────┴──────┐
                       │ Tailscale  │
                       │   Mesh     │
                       └─────┬──────┘
                             │
                ┌────────────┴────────────┐
                │      Traefik v2.10      │
                │   Let's Encrypt (ACME)  │
                │  bound to Tailscale IP  │
                └────────┬────────┬───┘
                         │        │
                ┌────────┘        └────────┐
                ▼                          ▼
  ┌─────────────────┐    ┌─────────────────┐
  │    Jellyfin     │    │   *arr suite    │
  │                 │    │ Sonarr  Radarr  │
  │                 │    │Prowlarr Bazarr  │
  │                 │    │     Seerr       │
  └─────────────────┘    └────────┬────────┘
                          │
                 ┌────────┴────────┐
                 │   qBittorrent   │  network_mode: service:gluetun
                 │  bound to tun0  │  (namespace isolation)
                 └────────┬────────┘
                          │
  ┌───────────────────────┼───────────────────────────┐
  │    vpn_network        │                           │
  │              ┌────────┴────────┐                  │
  │              │     Gluetun     │                  │
  │              │ ProtonVPN (WG)  │                  │
  │              │   NL/CH (P2P)   │                  │
  │              └─────────────────┘                  │
  └───────────────────────────────────────────────────┘

What's in the stack

arr/ — Gluetun, qBittorrent, Jellyfin, Sonarr, Radarr, Prowlarr, Seerr, Bazarr, Autoheal. Every container has an endpoint-specific health check. Autoheal restarts anything that fails. depends_on: service_healthy prevents cascading startup issues.

infra/ — Traefik v2.10.7 reverse proxy with auto HTTPS via Cloudflare DNS challenge.

Other stuff (DNS, books, dashboard) lives in a separate homelab repo.

License

MIT

About

My arr stack. Hardened Docker Compose config for Jellyfin + Sonarr/Radarr + qBittorrent with VPN namespace isolation and zero-trust ingress.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages