Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Lackoftactics/uncompressed

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
61 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

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 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

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

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

 
 
 

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.