Shell MIT

uncompressed

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

L

Lackoftactics

Dernière activité 9 mai 2026
Lackoftactics/uncompressed

216

étoiles

9

forks

3

issues ouvertes

arrdockerdocker-composehomelabjellyfinmedia-serverqbittorrentradarrself-hostedsonarrtailscaletraefikunraidvpnwireguard

Ce README est souvent en anglais.

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

Prerequisites

  • Docker + Compose

  • Tailscale account. Open these ports in your Tailscale ACL for the host running this stack: tcp:80, tcp:443 (Traefik, bound to your Tailscale IP) and tcp:8096 (Jellyfin direct, for LAN clients like Infuse / Apple TV). Nothing else is published to the host.

  • ProtonVPN account with WireGuard keys (P2P-enabled servers in NL/CH).

  • Domain on Cloudflare DNS with a scoped API token (not the Global API Key). Create the token at dash.cloudflare.com → My Profile → API Tokens with these permissions on the target zone:

    • Zone → Zone → Read
    • Zone → DNS → Edit

    This is the minimum required for the ACME DNS-01 challenge. See .env.example for the full variable list.

Quick Start

Fast track — download and run the guided setup wizard (no git required):

curl -L https://github.com/Lackoftactics/uncompressed/archive/main.tar.gz | tar xz
cd uncompressed-main && ./setup.sh

Or with git (gives you git pull for future updates):

git clone https://github.com/Lackoftactics/uncompressed.git
cd uncompressed && ./setup.sh

Manual setup — if you prefer to do it yourself:

cp .env.example .env              # fill in secrets, domain, Tailscale IP, WG keys, CF token
ln -s ../.env arr/.env            # each compose stack reads its own .env
ln -s ../.env infra/.env
docker network create traefik_proxy

Then start the stack (from the repo root — no cd needed):

docker compose -f infra/docker-compose.yml up -d  # traefik first
docker compose -f arr/docker-compose.yml up -d     # media pipeline (9 containers)

Each compose file declares env_file: ./.env, resolved relative to its own directory — so arr/docker-compose.yml needs arr/.env. Symlinking keeps one source of truth at the repo root.

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, with one exception — only Jellyfin publishes :8096 to the host so LAN clients (Infuse, Apple TV) can hit it directly. Everything else is reachable only through Traefik over the Docker network — there's no way to hit Sonarr/Radarr/Prowlarr/etc. 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 networks — traefik_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

Media architecture — dockerized solution

Cloudflare is used only as the ACME DNS-01 challenge target for cert renewal — control plane, not in the user-traffic path.

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.

License

MIT

Projets similaires

Simple Docker Compose NAS featuring Sonarr, Radarr, Prowlarr, Jellyfin, qBittorrent, PIA VPN and Traefik with SSL support

Shelladguardhomedockerdocker-compose
AAdrienPoupa
2,1 k étoiles262

Self-hosted complete media server Jellyfin with sonarr, radarr, jackett, prowlarr, qbittorrent, flaredolverr and gluetun Nord VPN Proton VPN in docker compose

Shelldockerdocker-composefilm
MMorzomb
570 étoiles62

The ultimate Docker Compose files and configs to build your desired media stack, quickly and easily, with secure outbound network traffic and secure remote access using multifactor authentication.

Shellauthentikcrowdsecflaresolverr
Ggeekau
1,8 k étoiles161