Purpose of this guide
While there are many Linux based guides for implementing the *Arr stack, some of us are stuck with Windows either by choice or by circumstance.
This guide is intended to help you implement a performant and reliable *arr stack on a Windows 11 PC using:
- WSL2 with Ubuntu 24.04.1 LTS
- Docker Desktop for Windows (to run the linux based *Arr stack):
- gluetun
- qbittorrent
- lidarr
- bazarr
- radarr
- sonarr
- flaresolverr
- prowlarr
- sabnzbd
- jellyseerr
- Plex Server and/or Jellyfin Server to serve Media (you’ll want the Windows versions of these)
- Tailscale (for simple and secure remote access to all Windows and Docker apps) (Windows version)
I’m not gonna pretend this is a super beginner friendly guide - some level of computer literacy is assumed and you will need to read specific guides for setting up the various apps to your liking. E.g., https://trash-guides.info/. This guide is more of an overview of setting up your system in a way that takes advantage of WSL2 while hopefully avoiding potential pitfalls.
Prerequisites and initial setup
-
Install Windows 11, preferably on an NVME or SSD. You will also be using this drive as a download cache*, and to run your Docker containers. (*Or you can optionally use a dedicated SSD as the download cache, so it’s cheap and easy to replace if/when it wears out).
-
Install/enable WSL2 on Windows. There are many guides online on how to do this. E.g. this one.
-
Install Ubuntu 22.04.1 LTS (or go wild and pick another version, ymmv) from the Microsoft Store. Once installed, open a Ubuntu terminal and you’ll be prompted to setup an account. This account is separate from your Windows account. Remember the username and password. You should also be sure to check for any Ubuntu updates after installing. I recommend downloading the Windows Terminal app from the app store so you can access CMD/Powershell/Ubuntu terminal tabs in the one app.
-
Install Docker Desktop for Windows with the following options:
-
Settings > General:
- Start Docker Desktop when you sign in to your computer;
- Use the WSL 2 based engine;
- Use containerd for pulling and storing images
-
Resources > Network:
- Enable host networking;
- Also note you can see your Docker subnet details here.
-
Resources > WSL Integration:
- Enable integration with default WSL distro;
- Enable integration with additional distros (select Ubuntu-24.04)
- If you don’t like the WSL2 defaults (see below), you can configure memory limits and swap size by creating a file called
.wslconfig
in your windowsuserprofile%
folder. I set these settings as follows, but it will depend on your available hardware.
# Note: Microsoft sets a default maximum RAM available to 50% of the physical memory and a swap-space that is 1/4 of the maximum WSL RAM. You can scale those numbers up or down to allocate more or less RAM to the Linux instance.
# Settings apply across all Linux distros running on WSL 2
[wsl2]
# Limits VM memory to use no more than 8 GB, this can be set as whole numbers using GB or MB
memory=8GB
# Sets amount of swap storage space to 4GB
swap=4GB
-
VERY IMPORTANT DOCKER SETTING - Edit
APPDATA%\Docker\settings.json
and changevpnKitMaxPortIdleTime=300
tovpnKitMaxPortIdleTime=0
to prevent network connections to your docker apps being forcibly closed after 5 minutes, which can cause a lot of laggy behaviour and connection errors (ask me how I know!). See https://emerle.dev/2022/05/06/the-nasty-gotcha-in-docker/ for a more detailed explanation. -
Make sure you reboot before proceeding to ensure docker & wsl2 config changes are applied.
Folder structure (Windows file system)
The data folder/drive has sub-folders for torrents and usenet and each of these have sub-folders for tv, movie, books, software and music downloads to keep things neat.
Typically your data folder/drive will be a large capacity HDD, a NAS share, or some other for of cheap bulk storage. While this is usually fine for streaming video and other media content, if you have a fast gigabit internet connection, the drive write speeds will potentially be a significant bottleneck when downloading. Ideally, you want a decent NVME or SSD for the OS and also to act as a cache for incomplete downloads. Or you can optionally assign a dedicated ssd/nvme for the download cache since it will potentially have a LOT of reads and writes.
For this setup, you’ll want to keep all your completed Media on a windows NTFS drive/folder. We’ll be running Windows binaries for Jellyfin/Plex and they work best with NTFS. By running windows binaries for the Media servers, you’ll have no problems enabling hardware transcoding. Example setup below:
data (D:) [NTFS formatted Windows partition]
├── torrents
│ ├── books
│ ├── movies
│ ├── music
│ ├── software
│ └── tv
├── usenet
│ └── complete
│ ├── books
│ ├── movies
│ ├── music
│ ├── software
│ └── tv
└── media
├── books
├── movies
├── music
├── software
└── tv
Folder structure (WSL2 file system)
You’ll want to keep all the application data from the Docker stack in the WSL2 filesystem, to eliminate the chance of data corruption and to improve disk r/w performance.
- Open an Ubuntu terminal which will land you in your home (~) folder. Note that Ubuntu uses an ext4 based filesystem in the WSL2 VM. It’s important to use this filesystem for your docker stack for best compatibility.
- Create a folder to store your docker stack data with
mkdir arrstack
or a name of your choice, and then setup a folder structure similar to the one below. Note that you may wish to locate the usenetcache and torrentcache folders on a discrete SSD/NVME if you have one available, to extend the lifepan of your boot drive.
~/arrstack [ext4 formatted Ubuntu partition]
├── config [used to store config folders for each of the docker apps]
│ ├── bazarr
│ ├── jellyseerr
│ ├── lidarr
│ ├── prowlarr
│ ├── qbittorrent
│ ├── radarr
│ ├── sabnzbd
│ └── sonarr
├── usenetcache
└── torrentcache
Docker compose
In an Ubuntu terminal:
cd ~/arrstack
- Create a new
docker-compose.yaml
file in your~/arrstack
folder using the commandnotepad.exe docker-compose.yaml
. Here’s my docker compose contents for reference. I’m not going to go through how to setup each app as there are many other guides for that:
services:
gluetun: # Used to provide wireguard VPN for qbittorrent
image: qmcgaw/gluetun:v3
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_PORT_FORWARDING_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=**************** # you'll need to lookup the relevant details for your VPN provider of choice
- SERVER_COUNTRIES=Canada
- PORT_FORWARD_ONLY=on
- VPN_PORT_FORWARDING=on
- TZ=America/Halifax
ports:
- 8090:8090
- 6881:6881
- 6881:6881/udp
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
jellyseerr:
image: fallenbagel/jellyseerr:latest
container_name: jellyseerr
environment:
- LOG_LEVEL=debug
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/jellyseerr:/app/config # all config data saved persistently in ext4 filesystem
restart: unless-stopped
ports:
- 5055:5055
depends_on:
radarr:
condition: service_started
sonarr:
condition: service_started
radarr:
container_name: radarr
image: ghcr.io/hotio/radarr:latest
restart: unless-stopped
logging:
driver: json-file
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/radarr:/config # all config data saved persistently in ext4 filesystem
- /mnt/d:/data # provides access to NTFS filesystem for completed media downloads
ports:
- 7878:7878
depends_on:
qbittorrent:
condition: service_started
sabnzbd:
condition: service_started
sonarr:
container_name: sonarr
image: ghcr.io/hotio/sonarr:latest
restart: unless-stopped
logging:
driver: json-file
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/sonarr:/config # all config data saved persistently in ext4 filesystem
- /mnt/d:/data # provides access to NTFS filesystem for completed media downloads
ports:
- 8989:8989
depends_on:
qbittorrent:
condition: service_started
sabnzbd:
condition: service_started
bazarr:
container_name: bazarr
image: ghcr.io/hotio/bazarr:latest
restart: unless-stopped
logging:
driver: json-file
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/bazarr:/config # all config data saved persistently in ext4 filesystem
- /mnt/d:/data # provides access to NTFS filesystem for completed media downloads
ports:
- 6767:6767
depends_on:
qbittorrent:
condition: service_started
sabnzbd:
condition: service_started
sabnzbd:
container_name: sabnzbd
image: ghcr.io/hotio/sabnzbd:latest
restart: unless-stopped
logging:
driver: json-file
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/sabnzbd:/config # all config data saved persistently in ext4 filesystem
- ~/arrstack/usenetcache:/usenetcache # cache folder (or drive) for incomplete usenet downloads
- /mnt/d/usenet:/data/usenet:rw # provides access to NTFS filesystem for completed usenet downloads
ports:
- 8080:8080
- 9090:9090
lidarr:
container_name: lidarr
image: lscr.io/linuxserver/lidarr:latest
restart: unless-stopped
logging:
driver: json-file
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/lidarr:/config # all config data saved persistently in ext4 filesystem
- /mnt/d:/data
ports:
- 8686:8686
depends_on:
qbittorrent:
condition: service_started
sabnzbd:
condition: service_started
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
- WEBUI_PORT=8090
- TORRENTING_PORT=6881 # for example
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/qbittorrent:/config # all config data saved persistently in ext4 filesystem
- ~/arrstack/torrentcache:/torrentcache # cache folder (or drive) for incomplete torrent downloads
- /mnt/d/torrents:/data/torrents:rw # provides access to NTFS filesystem for completed torrent downloads
network_mode: "service:gluetun" # torrents go through VPN network connection - also set the network interface in qbittorrent's advanced settings to "tun0" when you configure the app
depends_on:
gluetun:
condition: service_healthy
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
environment:
- PUID=1000
- PGID=1000
- TZ=America/Halifax
volumes:
- /etc/localtime:/etc/localtime:ro
- ~/arrstack/config/prowlarr:/config # all config data saved persistently in ext4 filesystem
ports:
- 9696:9696
restart: unless-stopped
depends_on:
flaresolverr:
condition: service_started
qbittorrent:
condition: service_started
sabnzbd:
condition: service_started
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- LOG_HTML=${LOG_HTML:-false}
- CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}
- TZ=America/Halifax
- HEADLESS=true
volumes:
- /etc/localtime:/etc/localtime:ro
ports:
- "${PORT:-8191}:8191"
restart: unless-stopped
- Once you have saved the docker-compose.yaml file, run
docker compose pull
to download all the relevant docker images, thendocker compose up -d
to spin them up. Once you’ve done this the first time, you can open Docker Desktop in Windows to manage your containers & images, access logs etc, using the GUI interface.
Completing your setup
-
Install the Windows versions of Jellyfin and/or Plex, using the data (D:) [NTFS formatted Windows partition] for all your media libraries. They don’t need any access to your WSL ext4 partitions. If you wish to use Plex remote access you will need to port forward port
32400
from your router to your PC. However you don’t need to open any other ports since we will be using Tailscale for simplicity. -
Setup a free Tailscale account if you haven’t already got one. Install Tailscale on your media server PC and also on any other devices you wish to use to access your media server remotely. Once tailscale is installed, you will be able to use your machine name (in my case
media-pc
) to connect to your apps remotely using the following links, without needing to do any port forwarding:
Tailscale remote links:
Plex http://media-pc:32400/web/index.html#!/ or you can use https://app.plex.tv/ if you have forwarded port 32400
Jellyfin http://media-pc:8096/web/#/home.html
Jellyseerr http://media-pc:5055/
Sonarr http://media-pc:8989/
Radarr http://media-pc:7878/
Lidarr http://media-pc:8686/
Bazarr http://media-pc:6767/
SABnzbd http://media-pc:8080/
qBittorrent http://media-pc:8090/
Prowlarr http://media-pc:9696/
localhost links:
Plex localhost:32400/web/index.html#!/
Jellyfin localhost:8096/web/#/home.html
Jellyseerr localhost:5055/
Sonarr localhost:8989/
Radarr localhost:7878/
Lidarr localhost:8686/
Bazarr localhost:6767/
SABnzbd localhost:8080/
qBittorrent localhost:8090/
Prowlarr localhost:9696/
Note that if you are accessing your media using tailscale, your browser may complain about the lack of a secure connection, however behind the scenes your tailscale connection is in fact fully secured with TLS and a wireguard VPN connection. On Firefox, you can click the padlock icon and set automatically upgrade this site to a secure connection
to off
for each link so you don’t get a security warning each time you connect.
- If you wish to share your media library with other people, you can either add them to your tailscale network (beyond the scope of this guide) or simply give them the https://app.plex.tv/ link and relevant login details if you have forwarded port 32400.
Auto-starting Docker containers on windows boot
I like to have this automated so that on reboot all the docker containers/images spin up. Unfortunately there is no option to do this automatically in Docker Desktop. There are a couple of ways to do this, but this is the way I have got working reliably:
- Create folder
C:\Users\windows-username\startup-scripts
, substitutingwindows-username
with your Windows username. - Create a powershell script
docker_compose_up.ps1
in this folder with contents as per below, substitutingubuntu-username
below with your Ubuntu username. This script will load the WSL2 VM, then spin up your docker container using your previously created docker-compose.yaml configuration file:
wsl ~ -u ubuntu-username -d Ubuntu-24.04 -e sh -c "docker compose -f ~/arrstack/docker-compose.yaml up -d"
- Create a .vbs script called
launcher.vbs
in this folder with content as per below. The only purpose of this file is to allow you to silently launch your powershell script created in step 2 using a scheduled task, without having to install any 3rd party apps. It’s a bit hacky but it works great.
On Error Resume Next
ReDim args(WScript.Arguments.Count-1)
For i = 0 To WScript.Arguments.Count-1
If InStr(WScript.Arguments(i), " ") > 0 Then
args(i) = Chr(34) & WScript.Arguments(i) & Chr(34)
Else
args(i) = WScript.Arguments(i)
End If
Next
CreateObject("WScript.Shell").Run Join(args, " "), 0, False
- Open windows task scheduler and create a new task called
start docker containers
- Set the task to trigger at log on of your windows user
- Set the action to
Start a program
with program/script ofWscript.exe
with arguments//B "launcher.vbs" powershell.exe -ExecutionPolicy ByPass -file "docker-compose-up.ps1"
and start inC:\Users\windows-username\startup-scripts
Some reflections on this setup
While obviously this setup isn’t for everyone, and is by no means a recommended way to setup your *arr stack, if you enjoy working with Windows or have Windows-specific hardware/drivers you want to use then this is a viable alternative to running a Linux server. My personal motivation for doing this was to get 5.1 surround sound audio working reliably - something I’ve tried and failed to do on Ubuntu several times due to my own incompetence linux driver issues with my particular Soundblaster S/PDIF audio configuration (and the fact my TV doesn’t support 5.1 HDMI ARC passthrough).
If anyone can suggest any improvements or errors in this guide, then I’d be very happy to hear from you! I’m not pretending to be an authority on any of this - but this setup works well for me and hopefully there’s some info that other folks will find useful.
Finally, I hope I haven’t missed any important steps (as I’m writing this guide from memory, rather than as I was setting things up) but if you run into any issues or notice a missing step then please let me know so I can edit the guide.
Lol I feel ya. This setup doesn’t require any windows firewall or port forwarding unless you choose to do so for Plex, so hopefully avoids most of those pitfalls. But yeah it’s still a bit fragile as a WSL2 update might break things down the track.
I commend ya for the write up though. Would have been insanely helpful back when I was using WSL2 to emulate a raspberry pi to run Pihole 🤣