# docker-compose-nas: a Docker Compose NAS stack with PIA WireGuard, Traefik and Jellyfin

> AdrienPoupa/docker-compose-nas is an opinionated Compose setup that wires Sonarr, Radarr, Prowlarr, qBittorrent, Jellyfin and Traefik together on one Linux box. It is a good fit only if you accept its routing and storage assumptions.

**AdrienPoupa/docker-compose-nas** — Simple Docker Compose NAS featuring Sonarr, Radarr, Prowlarr, Jellyfin, qBittorrent, PIA VPN and Traefik with SSL support

- Repository: https://github.com/AdrienPoupa/docker-compose-nas
- Stars: 2,148 · Forks: 263
- Language: Shell
- License: not declared
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/adrienpoupa-docker-compose-nas

## What docker-compose-nas replaces, and who it is written for

The README opens with the author's own framing: after looking for a NAS solution, the conclusion was that "what I wanted could be achieved with some Docker containers on a vanilla Linux box". That is the scope. This is not a NAS operating system and not a packaged appliance image. It is a Compose file plus per-application directories that turn a general purpose Linux host into a media download and playback machine.

The target user is someone who has already decided against a turnkey NAS OS and is comfortable editing a .env file. The stated requirements are Docker Engine and Docker Compose V2 on a recent Linux box. The author runs it on Ubuntu Server 22.04 and reports testing on a Synology DS220+ with DSM 7.1, with a dedicated quirks section for that hardware. The application table lists Sonarr, Radarr, Bazarr, Prowlarr, qBittorrent, Unpackerr, Jellyfin, Jellyseer, Homepage and Traefik as the core set, with optional services such as FlareSolverr, SABnzbd, AdGuard Home, Calibre-Web, Decluttarr, Tandoor, Joplin, Home Assistant, Immich, Vaultwarden and Paperless Ngx in separate directories.

It solves a specific integration problem rather than a hosting problem. Anyone can run Jellyfin in a container. Getting indexers, download clients, a VPN with port forwarding, subtitle management and HTTPS reverse proxying to agree on hostnames, ports and shared volumes is the part that takes an afternoon. This repository is that afternoon, already spent.

## How the stack is wired: Traefik, the VPN namespace and shared volumes

Traefik is the entry point. The compose file pins ghcr.io/traefik/traefik:3.7, publishes ports 80 and 443, and configures two entrypoints, web and web-secure, with a permanent redirect from the former to the latter. Certificates come from Let's Encrypt through a DNS challenge rather than an HTTP one, which is why the Cloudflare environment variables and the acme.dnschallenge flags are present. The resolver is configured with 1.1.1.1:53 and 8.8.8.8:53, and the ACME storage file is /letsencrypt/acme.json under the CONFIG_ROOT mount. A healthcheck runs traefik healthcheck --ping every minute with 10 retries.

qBittorrent does not sit on the default network. The README states it uses the VPN network, provided by thrnz/docker-wireguard-pia, which encapsulates traffic through PIA over WireGuard with port forwarding. That is the architectural decision that shapes everything else: the torrent client's traffic leaves through the tunnel, and other services reach it over the shared container network rather than through the host. The README also documents an optional customization to route the *arr applications through the VPN as well, which is off by default.

Storage is split by purpose. .env.example defines CONFIG_ROOT for application configuration, DATA_ROOT for media, DOWNLOAD_ROOT for torrents, and IMMICH_UPLOAD_LOCATION for photos. The README has a section titled "Use Separate Paths for Torrents and Storage", which is the standard advice for avoiding cross-device moves when a completed download is imported into the library. If you ignore that and put both on one filesystem, imports become copies. That is a real cost on large files.

## Installing docker-compose-nas on Ubuntu and starting the first service

The README does not give a numbered install sequence. What it gives is the requirements line and the repository layout: a docker-compose.yml at the root, per-application directories, and .env.example. The practical first step is to copy the example environment file and fill in the values you actually need. The variables below are the ones from .env.example that the compose file reads.

```bash
cp .env.example .env
```

Open .env and set at minimum USER_ID, GROUP_ID, TIMEZONE, CONFIG_ROOT, DATA_ROOT and DOWNLOAD_ROOT. The example ships with USER_ID=1000 and GROUP_ID=1000, which matches the first user on many Ubuntu installs. CONFIG_ROOT defaults to ".", DATA_ROOT to /mnt/data, and DOWNLOAD_ROOT to /mnt/data/torrents. Also set PIA_USER and PIA_PASS, which are empty in the example, and change QBITTORRENT_PASSWORD, which ships as adminadmin.

The COMPOSE_FILE variable is what makes the optional services load. The example chains the base file with adguardhome, tandoor, joplin, homeassistant, immich, vaultwarden, privoxy and paperless, separated by colons because COMPOSE_PATH_SEPARATOR is set to ":". If you only want the core media stack, trim that list to docker-compose.yml before running anything.

```bash
COMPOSE_FILE=docker-compose.yml docker compose up -d traefik
```

That starts Traefik alone. Check that the container reports healthy with docker compose ps, then bring up the rest. The README's application table maps each service to a path: /sonarr, /radarr, /bazarr, /prowlarr, /qbittorrent, /jellyfin, and the dashboard at /. Jellyseer is not on a path; it is addressed through SEERR_HOSTNAME, which .env.example defines as seerr.${BASE_HOSTNAME}.

```bash
docker compose up -d
```

The README also ships update-config.sh at the repository root. It is not described in the table of contents, so its exact behaviour is not documented in the README text.

## Where docker-compose-nas breaks: DNS challenge, ports and the missing licence

The certificate setup assumes a DNS provider. The compose command sets acme.dnschallenge to true and acme.dnschallenge.provider to cloudflare by default, and the Traefik service reads CLOUDFLARE_EMAIL, CLOUDFLARE_DNS_API_TOKEN and CLOUDFLARE_ZONE_API_TOKEN. If your DNS is not on Cloudflare, you are not simply entering a different email. You have to change the provider flag and supply that provider's credentials in a way the compose file does not currently model. Anyone without a public domain pointed at their DNS provider gets nothing from this path, and the README's answer to that case is Tailscale for remote access, which is a different mechanism entirely.

Ports 80 and 443 are hard requirements in the compose file. The Synology quirks section exists precisely because those ports are often occupied on that platform, with subsections for freeing 80 and 443, installing Synology WireGuard, freeing port 1900, user permissions, and a DHCP conflict between the Synology DHCP server and AdGuard Home. If you cannot free those ports, Traefik cannot bind and the whole reverse proxy layer is dead. This is not a configuration detail you can defer.

The licence is the sharpest limitation. The repository metadata does not state one, and the README does not discuss licensing. That is not the same as permissive. It means you cannot tell from the repository what you are allowed to do with the Compose files, and the images it pulls each carry their own terms, including linuxserver images and the PIA WireGuard container. If you need a clear legal footing before deploying, this repository does not give you one.

Finally, the README documents a laptop-specific configuration section and an NFS share section, which hints at deployment shapes beyond a fixed home server. Neither is expanded in the table of contents, so treat them as starting points rather than supported paths.

## Compared with a plain Sonarr and qBittorrent compose file

The obvious alternative is writing your own Compose file with just the services you need. That is what most people do, and it is a real option rather than a straw man. A hand-written file for Sonarr, Radarr, Prowlarr and qBittorrent is perhaps forty lines. What it will not have is the VPN integration: routing qBittorrent through thrnz/docker-wireguard-pia with port forwarding, and having the *arr services reach it across the shared network, is the part that takes reading and iteration. It also will not have the Traefik DNS challenge configuration, the Let's Encrypt storage mount, or the per-application subdirectories that keep configuration out of the compose file.

The trade-off runs the other way too. This repository is opinionated by its own description. It assumes PIA as the VPN provider, Cloudflare as the DNS provider, Traefik as the proxy, linuxserver images for most applications, and a specific split between CONFIG_ROOT, DATA_ROOT and DOWNLOAD_ROOT. Each of those is a decision you inherit. If you use Mullvad, or Caddy, or want qBittorrent outside a tunnel, you are editing rather than configuring, and the further you move from the defaults the less the repository's structure helps you. A smaller hand-rolled file has fewer places to disagree with.

The middle ground is to use this repository as a reference implementation. Read the qBittorrent service definition and the PIA container's environment, copy those two into your own file, and leave the rest. Nothing about the layout prevents that.

## Maintenance, upgrades and what the repository tells you about them

The repository is not archived, and the last push was on 2026-09-08. Dependabot is configured: dependabot.yml sits at the repository root and a .github directory is present, which is consistent with automated dependency update pull requests. That matters more here than in a typical application, because the stack is a set of image tags and CLI flags rather than code you compile. The Traefik image is pinned to 3.7 in the compose file, while most linuxserver services are referenced without a version tag, which means those track latest on every pull.

That split is worth understanding before you deploy. A pinned Traefik gives you a predictable proxy and a manual upgrade step. Untagged linuxserver images give you whatever was published most recently, and a breaking change in Sonarr or Jellyfin arrives on the next docker compose pull rather than on a version bump you chose. There is no release history in the repository, so there is nothing to read for changelogs or migration notes.

update-config.sh at the root is the only upgrade-adjacent script visible in the layout. The README does not document what it does, so it should be read before it is run. The README also does not document rollback. If an image update breaks the stack, the recovery path is whatever your own backups of CONFIG_ROOT allow, and CONFIG_ROOT is where Traefik's acme.json lives, so losing it means reissuing certificates.

On licensing: the repository states no licence, and the images pulled by the compose file have their own terms. That is a question for your own review, not something the README answers.

## Conclusion

Adopt it if you already run Docker Engine and Compose V2 on a recent Linux box, want qBittorrent traffic inside a PIA WireGuard tunnel with port forwarding, and are willing to keep the bundled docker-compose.yml plus the per-app files listed in COMPOSE_FILE in sync. Do not adopt it if you need a supported product with a licence statement, or if you cannot give the stack ports 80 and 443. Verify first that CONFIG_ROOT, DATA_ROOT and DOWNLOAD_ROOT point at real paths on your machine, that PIA_USER and PIA_PASS are filled in, and that the *arr services and qBittorrent share one download volume.

## FAQ

### Can I use Docker on my NAS?

Yes. The README states the author tested this setup on a Synology DS220+ running DSM 7.1, and the repository includes a Synology quirks section covering ports 80 and 443, WireGuard installation, port 1900, user permissions and a DHCP conflict with AdGuard Home.

### Is Docker Compose obsolete?

The repository does not address this. It requires Docker Compose V2 and uses COMPOSE_FILE, COMPOSE_PROFILES and COMPOSE_PATH_SEPARATOR to compose the base file with the optional per-application files, so Compose is the mechanism the stack depends on.

### Is Docker still relevant in 2026?

The repository does not answer this question. Its last push was on 2026-09-08 and it is not archived, and its docker-compose.yml pins ghcr.io/traefik/traefik:3.7 and pulls current linuxserver images, so the stack itself is built on Docker Engine and Compose V2.

### Why are people moving away from Docker?

The repository does not address this. The README's stated requirements are Docker Engine and Docker Compose V2 on a recent Linux box, and every service in the application table is a container image.

## Sources

- [AdrienPoupa/docker-compose-nas on GitHub](https://github.com/AdrienPoupa/docker-compose-nas)
- [Issues](https://github.com/AdrienPoupa/docker-compose-nas/issues)
- [README](https://github.com/AdrienPoupa/docker-compose-nas/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/adrienpoupa-docker-compose-nas
