2. Docker Compose File
Sweet! So you want to install the arr stack on your self hosted device. Well, as with any app you need decide which app workflow deployment you want to use. Here, we focus on the docker compose deployment using a dedicated VM. Now, personally I like to run the arr stack in a dedicated VM and Jellyfin in another Docker VM. This gives you the possibility to move your Jellyfin instance around easily to different hosts and if something is wrong with your arr stack then your users can still enjoy the already downloaded media. Eventually, it is up to you and the amount of resources you have at your disposal what your decision will be. Hence, you can also run all of this in your existing docker vm.
1. Create a new docker vm by using the proxmox helper scripts ;) Or if you want to continue using your existing docker vm then you need
2.
I keep every tunable value in a .env file rather than hardcoding it in the Compose file. Ports, paths, and the user IDs all live in one place, which makes the Compose file readable and easy to reuse. I also run every service on an external network called media_network, which means the network is created once, outside of Compose, and the stack attaches to it. That keeps the network stable even when I tear the stack down and bring it back up.
Create a project directory and the .env file:
Hottio Decluttar
Whisparr is the Sonarr/Radarr equivalent for adult content
Whisper-AI generates subtitles on the fly for your media
compose.yml
services:
# Sonarr - TV Shows
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./sonarr/config:/config
- ${ROOT_MEDIA_PATH}:/data # Access to /data/downloads and /data/media/tv
ports:
- 8989:8989
restart: unless-stopped
networks:
- internal-proxy
# Radarr - Movies
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./radarr/config:/config
- ${ROOT_MEDIA_PATH}:/data # Access to /data/downloads and /data/media/movies
ports:
- 7878:7878
restart: unless-stopped
networks:
- internal-proxy
networks:
internal-proxy:
driver: bridge
# Prowlarr - Indexer centralized management
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./prowlarr/config:/config
ports:
- 9696:9696
restart: unless-stopped
networks:
- internal-proxy
# Profilarr & Decluttarr (Config only, no media access needed usually)
profilarr:
image: santiagosayshey/profilarr:latest
container_name: profilarr
environment:
- TZ=${TZ}
volumes:
- ./profilarr/config:/config
ports:
- 6868:6868
restart: unless-stopped
networks:
- internal-proxy
depends_on:
- sonarr
- radarr
# FlareSolverr - Bypasses Cloudflare protection for Indexers
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- TZ=${TZ}
ports:
- 8191:8191
restart: unless-stopped
networks:
- internal-proxy
# Gluetun - VPN Client
# gluetun:
# image: qmcgaw/gluetun:latest
# container_name: gluetun
# cap_add:
# - NET_ADMIN
# environment:
# - PUID=${PUID}
# - PGID=${PGID}
# - TZ=${TZ}
# - VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
# - VPN_TYPE=${VPN_TYPE}
# - WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
# - WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
# - VPN_PORT_FORWARDING=${VPN_PORT_FORWARDING}
# ports:
# - 8888:8888 # Health check and control
# - 6881:6881 # qBittorrent P2P port
# - 6881:6881/udp
# restart: unless-stopped
# networks:
# - internal-proxy
# If using Gluetun, use qbittorrent from linuxserver io, NOT the hotio image below!
#qbittorrent:
# image: lscr.io/linuxserver/qbittorrent
# network_mode: "service:gluetun" # The "Magic" line
# depends_on:
# gluetun:
# condition: service_healthy # Wait for VPN to be up first
#qbittorrent-vpn:
# image: hotio/qbittorrent:latest
# container_name: qbittorrent-vpn
# cap_add:
# - NET_ADMIN
# environment:
# - PUID=${PUID}
# - PGID=${PGID}
# - TZ=${TZ}
# - VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
# - VPN_USER=${VPN_USER}
# - VPN_PASS=${VPN_PASS}
# - OPENVPN_CONFIG=${VPN_OPENVPN_CONFIG}
# volumes:
# - ./qbittorrent-vpn/config:/config
# - ${ROOT_MEDIA_PATH}:/data # This maps the whole root to /data
# ports:
# - 8080:8080
# - 6881:6881
# - 6881:6881/udp
# restart: unless-stopped
# networks:
# - internal-proxy
# Lidarr - Music
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./lidarr/config:/config
- ${ROOT_MEDIA_PATH}:/data
ports:
- 8686:8686
restart: unless-stopped
networks:
- internal-proxy
# Bazarr - Subtitles
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./bazarr/config:/config
- ${ROOT_MEDIA_PATH}:/data
ports:
- 6767:6767
restart: unless-stopped
networks:
- internal-proxy
# whisper-ai - Local Subtitle Generation (Requires GPU)
whisper-ai:
image: ${WHISPER_IMAGE_URL} # Use a pre-built image like "jellyfin-whisper-lab/whisper-container"
container_name: whisper-ai
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./whisper/config:/config
- ${ROOT_MEDIA_PATH}:/data
# Enable GPU access if you have one, or comment out the 'deploy' section for CPU-only:
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: all
# capabilities: [gpu]
restart: unless-stopped
networks:
- internal-proxy
# Decluttarr - Queue Cleaner
decluttarr:
image: hotio/decluttarr:latest
container_name: decluttarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
- DECLUTTARR_CRON="*/15 * * * *"
# API Keys/URLs for Sonarr, Radarr, etc. go here post-setup!
volumes:
- ./decluttarr/config:/config
restart: unless-stopped
networks:
- internal-proxy
# Whisparr - Adult # Optional, niche
whisparr:
image: ghcr.io/whisparr/whisparr:latest
container_name: whisparr
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- ./whisparr/config:/config
- ${ROOT_MEDIA_PATH}:/data
ports:
- 6969:6969
restart: unless-stopped
networks:
- internal-proxy
# Dozzle - Real-Time Container Log Viewer
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
volumes:
# CRITICAL: Mounts the Docker socket to read logs from ALL other containers
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 8081:8080 # Using 8081 to avoid conflict with qBittorrent's 8080 UI
restart: always
# Note: Dozzle does not need to be on the internal-proxy network.