the arr stack is ten containers that portainer deploys from git as one stack on
truenas1: the arr apps, jellyfin, and two
download clients that send their traffic through a VPN. it is a stack because
the truenas catalog can't give the download clients their VPN.
services:prowlarr:image:linuxserver/prowlarr@sha256:c96b56d94d116a9f4de94bc23d3381689492e6c3cfb7435320e8d982e406f99aenvironment:-TZ=America/Los_Angelescontainer_name:prowlarrhealthcheck:test:-CMD-SHELL-'curl-fsS--max-time10http://127.0.0.1:9696/ping>/dev/null||{echo"prowlarr/pingfailed";exit1;}'interval:60stimeout:15sretries:3start_period:120slabels:-homepage.group=Arr-homepage.name=Prowlarr-homepage.icon=prowlarr.png-homepage.href=https://prowlarr.mydomain.com-homepage.description=Indexer manager-homepage.widget.type=prowlarr-homepage.widget.url=http://192.168.1.86:9696-homepage.widget.key={{HOMEPAGE_FILE_PROWLARR_KEY}}restart:unless-stoppednetworks:[arrstack]ports:-9696:9696volumes:-/mnt/fast/configs/prowlarr:/configradarr:image:linuxserver/radarr@sha256:c960f2b52ec6542dbe6707c5a21e696a7c74fd8b17997454f4d10a55dacee133container_name:radarrhealthcheck:test:-CMD-SHELL-'curl-fsS--max-time10http://127.0.0.1:7878/ping>/dev/null||{echo"radarr/pingfailed";exit1;}'interval:60stimeout:15sretries:3start_period:120slabels:-homepage.group=Arr-homepage.name=Radarr-homepage.icon=radarr.png-homepage.href=https://radarr.mydomain.com-homepage.description=Film management-homepage.widget.type=radarr-homepage.widget.url=http://192.168.1.86:7878-homepage.widget.key={{HOMEPAGE_FILE_RADARR_KEY}}restart:unless-stoppednetworks:[arrstack]ports:-7878:7878environment:-PUID=568-PGID=568-TZ=America/Los_Angelesvolumes:-/mnt/fast/configs/radarr:/config-/mnt/rust/media:/mediasonarr:image:linuxserver/sonarr@sha256:a5c1a5fecbef946927ab90ad68df319ac5fe644057e5fc18cd993f01ac07b2b2container_name:sonarrhealthcheck:test:-CMD-SHELL-'curl-fsS--max-time10http://127.0.0.1:8989/ping>/dev/null||{echo"sonarr/pingfailed";exit1;}'interval:60stimeout:15sretries:3start_period:120slabels:-homepage.group=Arr-homepage.name=Sonarr-homepage.icon=sonarr.png-homepage.href=https://sonarr.mydomain.com-homepage.description=TV series management-homepage.widget.type=sonarr-homepage.widget.url=http://192.168.1.86:8989-homepage.widget.key={{HOMEPAGE_FILE_SONARR_KEY}}restart:unless-stoppednetworks:[arrstack]ports:-8989:8989environment:-PUID=568-PGID=568-TZ=America/Los_Angelesvolumes:-/mnt/fast/configs/sonarr:/config-/mnt/rust/media:/mediabazarr:image:linuxserver/bazarr@sha256:d24bd0048c759a468970989e9df11a6b96a7628d556d00f923e60a35ba59237bcontainer_name:bazarrhealthcheck:test:-CMD-SHELL-'curl-fsS--max-time10http://127.0.0.1:6767/api/system/ping|grep-qOK||{echo"bazarr/api/system/pingfailed";exit1;}'interval:60stimeout:15sretries:3start_period:120slabels:-homepage.group=Arr-homepage.name=Bazarr-homepage.icon=bazarr.png-homepage.href=https://bazarr.mydomain.com-homepage.description=Subtitles for Sonarr and Radarr-homepage.widget.type=bazarr-homepage.widget.url=http://192.168.1.86:6767-homepage.widget.key={{HOMEPAGE_FILE_BAZARR_KEY}}restart:unless-stoppednetworks:[arrstack]ports:-6767:6767environment:-PUID=568-PGID=568-TZ=America/Los_Angelesvolumes:-/mnt/fast/configs/bazarr:/config-/mnt/rust/media:/mediajellyfin:image:lscr.io/linuxserver/jellyfin@sha256:0f42497a69fa0441bfd5f9d6bba8694f2a656ec984e571d0ac04c6dd91250039container_name:jellyfinhealthcheck:test:-CMD-SHELL-'b=$$(curl-fsS--max-time10http://127.0.0.1:8096/health)||{echo"jellyfin/healthunreachable";exit1;};["$$b"=Healthy]||{echo"jellyfin/healthreturned:$$b";exit1;}'interval:60stimeout:15sretries:3start_period:180slabels:-homepage.group=Media-homepage.name=Jellyfin-homepage.icon=jellyfin.png-homepage.href=https://jellyfin.mydomain.com-homepage.description=Media serverrestart:unless-stoppednetworks:[arrstack]ports:-'8096:8096'environment:-PUID=568-PGID=568-TZ=America/Los_Angelesvolumes:-/mnt/fast/configs/jellyfin:/config-/mnt/rust/media:/mediaseerr:image:ghcr.io/seerr-team/seerr@sha256:f4768de5f616248d723e05891f3345a1402123775d03bf0890dbfedc0831bda1container_name:seerrlabels:-homepage.group=Arr-homepage.name=Seerr-homepage.icon=jellyseerr.png-homepage.href=https://seerr.mydomain.com-homepage.description=Requests-homepage.widget.type=seerr-homepage.widget.url=http://192.168.1.86:5055-homepage.widget.key={{HOMEPAGE_FILE_SEERR_KEY}}restart:unless-stoppeduser:"568:568"# (1)!init:true# (2)!networks:[arrstack]ports:-5055:5055environment:-LOG_LEVEL=info-TZ=America/Los_Angeles-PORT=5055healthcheck:test:wget --no-verbose --tries=1 --spider http://localhost:5055/api/v1/status || exit 1start_period:20stimeout:3sinterval:15sretries:3volumes:-/mnt/fast/configs/seerr:/app/configprofilarr:image:ghcr.io/dictionarry-hub/profilarr@sha256:ddcdd0f340043c2ec0a85ca74b9a6be9be42b1c0288c75fc36a26a43f695860bcontainer_name:profilarrlabels:-homepage.group=Arr-homepage.name=Profilarr-homepage.icon=profilarr.png-homepage.href=https://profilarr.mydomain.com-homepage.description=Quality profilesrestart:unless-stoppednetworks:[arrstack]ports:-"6868:6868"environment:-PUID=568-PGID=568-UMASK=022-TZ=America/Los_Angeles-ORIGIN=https://profilarr.mydomain.comvolumes:-type:bind# (3)!source:/usr/share/zoneinfotarget:/usr/share/zoneinforead_only:truebind:create_host_path:false-/mnt/fast/configs/profilarr:/configflaresolverr:image:ghcr.io/flaresolverr/flaresolverr@sha256:c80ae007ce2ccdcd217a12426e4f039ef763ff90738c808d38810c3e59323767container_name:flaresolverrhealthcheck:test:-CMD-SHELL-'b=$$(curl-fsS--max-time10http://127.0.0.1:8191/health)||{echo"flaresolverr/healthunreachable";exit1;};echo"$$b"|grep-qok||{echo"flaresolverr/healthreturned:$$b";exit1;}'interval:60stimeout:15sretries:3start_period:120srestart:unless-stoppednetworks:[arrstack]ports:-8191:8191environment:-LOG_LEVEL=info-LOG_HTML=false-CAPTCHA_SOLVER=none-TZ=America/Los_Angelesqbittorrent:image:ghcr.io/hotio/qbittorrent@sha256:3d61c8f205ed5edd5263ff24c4028b86486db8d330b63fe05582c7980f20aabecontainer_name:qbittorrenthealthcheck:test:-CMD-SHELL-'curl-fsS--max-time10http://127.0.0.1:8080/>/dev/null||{echo"qbittorrentwebuinotanswering";exit1;};wgshowwg0latest-handshakes|awk-vnow=$$(date+%s)''{if($$2>0&&now-$$2<300)ok=1}END{exitok?0:1}''||{echo"wireguardwg0handshakestaleormissing";exit1;}'interval:60stimeout:15sretries:3start_period:180slabels:-homepage.group=Downloads-homepage.name=qBittorrent-homepage.icon=qbittorrent.png-homepage.href=https://qbittorrent.mydomain.com-homepage.description=Torrent client, via its own VPN-homepage.widget.type=qbittorrent-homepage.widget.url=http://192.168.1.86:8080-homepage.widget.username=admin-homepage.widget.password={{HOMEPAGE_FILE_QBITTORRENT_PASSWORD}}restart:unless-stoppednetworks:[arrstack]ports:-8080:8080environment:# (4)!-PUID=568-PGID=568-UMASK=002-TZ=America/Los_Angeles-WEBUI_PORTS=8080/tcp,8080/udp-VPN_ENABLED=true-VPN_CONF=wg0-VPN_PROVIDER=generic-VPN_LAN_NETWORK=192.168.1.0/24-VPN_LAN_LEAK_ENABLED=false-VPN_EXPOSE_PORTS_ON_LAN=-VPN_PORT_REDIRECTS=-VPN_FIREWALL_TYPE=auto-VPN_HEALTHCHECK_ENABLED=false-VPN_NAMESERVERS=wg-PRIVOXY_ENABLED=falsecap_add:-NET_ADMINsysctls:-net.ipv4.conf.all.src_valid_mark=1-net.ipv6.conf.all.disable_ipv6=1volumes:-/mnt/fast/configs/qbittorrent:/config-/mnt/rust/media:/mediasabnzbd:image:ghcr.io/hotio/sabnzbd@sha256:ce9930087e0f6ba3ba375ab8005e6b6015a73252bbb7d98ef5b4a01008e1877econtainer_name:sabnzbdhealthcheck:test:-CMD-SHELL-'curl-fsS--max-time10"http://127.0.0.1:8080/api?mode=version">/dev/null||{echo"sabnzbdapinotanswering";exit1;};wgshowwg0latest-handshakes|awk-vnow=$$(date+%s)''{if($$2>0&&now-$$2<300)ok=1}END{exitok?0:1}''||{echo"wireguardwg0handshakestaleormissing";exit1;}'interval:60stimeout:15sretries:3start_period:180slabels:-homepage.group=Downloads-homepage.name=SABnzbd-homepage.icon=sabnzbd.png-homepage.href=https://sabnzbd.mydomain.com-homepage.description=Usenet client, via its own VPN-homepage.widget.type=sabnzbd-homepage.widget.url=http://192.168.1.86:8081-homepage.widget.key={{HOMEPAGE_FILE_SABNZBD_KEY}}restart:unless-stoppednetworks:[arrstack]ports:-8081:8080# (5)!environment:-PUID=568-PGID=568-UMASK=002-TZ=America/Los_Angeles-WEBUI_PORTS=8080/tcp,8080/udp-VPN_ENABLED=true-VPN_CONF=wg0-VPN_PROVIDER=generic-VPN_LAN_NETWORK=192.168.1.0/24-VPN_LAN_LEAK_ENABLED=false-VPN_EXPOSE_PORTS_ON_LAN=-VPN_AUTO_PORT_FORWARD=false-VPN_PORT_REDIRECTS=-VPN_FIREWALL_TYPE=auto-VPN_HEALTHCHECK_ENABLED=false-VPN_NAMESERVERS=wg-PRIVOXY_ENABLED=falsecap_add:-NET_ADMINsysctls:-net.ipv4.conf.all.src_valid_mark=1-net.ipv6.conf.all.disable_ipv6=1volumes:-/mnt/fast/configs/sabnzbd:/config-/mnt/rust/media:/medianetworks:arrstack:driver:bridge
seerr's image runs as uid 1000, and user: runs it as truenas's apps user, 568, instead. its
config folder has to be writable by 568 before the first start.
runs docker's init as PID 1, which passes stop signals on to seerr and reaps finished child
processes. seerr's example compose sets it.
the profilarr image has no time zone data, so TZ alone changes nothing. profilarr mounts the
host's /usr/share/zoneinfo read-only.
VPN_PROVIDER=generic runs the wg0.conf you supply, from any wireguard provider, and
VPN_LAN_NETWORK lists the networks that reach the web UI outside the tunnel. sabnzbd sets
both the same way, see the download clients' VPN.
8081 on the host, because qbittorrent has 8080. inside the container sabnzbd still listens on
8080, the port its healthcheck asks.
create a config folder under /mnt/fast/configs/ for each app except
flaresolverr, which keeps nothing. seerr runs as uid 568, so give its folder
to 568:
the binds are plain, so docker creates a missing folder empty and owned
by root, and the app starts against it with no error. seerr can't write
to a folder owned by root
the library, /mnt/rust/media, has to exist as well, writable by uid 568
put each download client's wireguard config at wireguard/wg0.conf in its
config folder: /mnt/fast/configs/qbittorrent/wireguard/wg0.conf and
/mnt/fast/configs/sabnzbd/wireguard/wg0.conf
it holds the tunnel's private key, so it is not in git and the compose
can't carry it
before this stack starts, put qbittorrent's web UI password in homepage's
key file, homepage_config/secrets/qbittorrent_password. see
homepage's steps
homepage logs in on every refresh, and qbittorrent bans an address for an
hour after five failed logins. homepage's address is its swarm node's, so
a wrong password locks homepage out
all ten are in the truenas community catalog, but the catalog can't express
the download clients' VPN: the app definitions fix the image and offer no
capabilities or sysctls. the apps talk to each other by container name, and
splitting them would put each catalog app on a separate network, so the whole
set is one stack.
every service is on one bridge, arrstack, and they address each other by
name: sonarr:8989, radarr:7878, prowlarr:9696. a service missing the
networks: key lands on the project's default network instead. it can't
resolve the others, and it still reports healthy.
VPN_PROVIDER=generic uses the wg0.conf you put in /config/wireguard/,
so any provider that gives you a wireguard config works. hotio also has
proton and pia, which can fetch a forwarded port for you, and pia
writes the wg0.conf as well
VPN_LAN_NETWORK lists the networks that can reach the web UI outside the
tunnel. 192.168.1.0/24 is my lan: put your own lan here, and any other
range that needs the web UI. with VPN_LAN_LEAK_ENABLED=false, nothing else
leaves for the lan
the web UI answers with the tunnel down, so both healthchecks fail when
wireguard's last handshake on wg0 is missing or older than 5 minutes.
docker doesn't act on a standalone container's health, so an unhealthy client
keeps running
each app's config and database, on the fast pool, covered by one recursive snapshot task
/mnt/rust/media
the library and downloads, one mount shared by every app that moves files, so a move is a rename, not a copy
sonarr, radarr, bazarr, jellyfin, qbittorrent and sabnzbd write to the library.
they run as uid and gid 568, truenas's apps user, set with PUID and PGID,
so they can all read and write the same files.
the apps carry homepage labels, and most have a widget. truenas1 runs plain
containers, so the labels sit under labels:. swarm stacks put theirs under
deploy.labels. api keys and the qbittorrent password come from files, never
the labels, see homepage.
gatus runs these four checks every two minutes. for
qbittorrent and sabnzbd the check is their healthcheck, which also tests the
tunnel, see the download clients' VPN.