$ User requests movie Radarr searches indexers qBittorrent downloads
via Jellyseerr --> via Prowlarr --> to NVMe SSD | v Download completes | v Radarr imports movie qBittorrent seeds to to media library <-- ratio 1.0, auto-removes | v Jellyfin detects new movie Bazarr auto-downloads in library --> subtitles (.srt/.ass) | v Tdarr (optional) transcodes to H.265 using Intel QSV | v Movie available for playback on all devices (TV, phone, web)
User requests movie Radarr searches indexers qBittorrent downloads
via Jellyseerr --> via Prowlarr --> to NVMe SSD | v Download completes | v Radarr imports movie qBittorrent seeds to to media library <-- ratio 1.0, auto-removes | v Jellyfin detects new movie Bazarr auto-downloads in library --> subtitles (.srt/.ass) | v Tdarr (optional) transcodes to H.265 using Intel QSV | v Movie available for playback on all devices (TV, phone, web)
User requests movie Radarr searches indexers qBittorrent downloads
via Jellyseerr --> via Prowlarr --> to NVMe SSD | v Download completes | v Radarr imports movie qBittorrent seeds to to media library <-- ratio 1.0, auto-removes | v Jellyfin detects new movie Bazarr auto-downloads in library --> subtitles (.srt/.ass) | v Tdarr (optional) transcodes to H.265 using Intel QSV | v Movie available for playback on all devices (TV, phone, web)
NVMe SSD (boot drive) — OS, Docker, downloads, configs
├── / OS + Docker (~20GB)
├── /opt/appdata Container configs + databases (~5-10GB)
├── /opt/-weight: 500;">docker Docker compose files + scripts
├── /mnt/media Media library root
│ ├── downloads/ Torrent downloads (temporary storage)
│ ├── movies/ -> BIND MOUNT to /mnt/ssd/movies (second drive)
│ ├── tv/ TV shows
│ ├── music/ Music
│ └── books/ Books
└── /var/lib/-weight: 500;">docker Container images + volumes (~10-20GB) Second SSD/HDD (/dev/sda1)
└── /mnt/ssd Mounted via fstab (by UUID) └── movies/ Movie library, bind-mounted to /mnt/media/movies
NVMe SSD (boot drive) — OS, Docker, downloads, configs
├── / OS + Docker (~20GB)
├── /opt/appdata Container configs + databases (~5-10GB)
├── /opt/-weight: 500;">docker Docker compose files + scripts
├── /mnt/media Media library root
│ ├── downloads/ Torrent downloads (temporary storage)
│ ├── movies/ -> BIND MOUNT to /mnt/ssd/movies (second drive)
│ ├── tv/ TV shows
│ ├── music/ Music
│ └── books/ Books
└── /var/lib/-weight: 500;">docker Container images + volumes (~10-20GB) Second SSD/HDD (/dev/sda1)
└── /mnt/ssd Mounted via fstab (by UUID) └── movies/ Movie library, bind-mounted to /mnt/media/movies
NVMe SSD (boot drive) — OS, Docker, downloads, configs
├── / OS + Docker (~20GB)
├── /opt/appdata Container configs + databases (~5-10GB)
├── /opt/-weight: 500;">docker Docker compose files + scripts
├── /mnt/media Media library root
│ ├── downloads/ Torrent downloads (temporary storage)
│ ├── movies/ -> BIND MOUNT to /mnt/ssd/movies (second drive)
│ ├── tv/ TV shows
│ ├── music/ Music
│ └── books/ Books
└── /var/lib/-weight: 500;">docker Container images + volumes (~10-20GB) Second SSD/HDD (/dev/sda1)
└── /mnt/ssd Mounted via fstab (by UUID) └── movies/ Movie library, bind-mounted to /mnt/media/movies
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y
-weight: 600;">sudo reboot
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y
-weight: 600;">sudo reboot
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y
-weight: 600;">sudo reboot
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-logind
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-logind
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-logind
# For Ethernet:
-weight: 600;">sudo nmcli con add con-name "static-eth" ifname YOUR_ETH_INTERFACE type ethernet \ ipv4.method manual ipv4.addresses YOUR_IP/24 \ ipv4.gateway YOUR_GATEWAY ipv4.dns "1.1.1.1,8.8.8.8"
-weight: 600;">sudo nmcli con up static-eth # For WiFi (adjust connection name from 'nmcli con show'):
-weight: 600;">sudo nmcli con modify "YOUR_WIFI_CONNECTION" \ ipv4.method manual ipv4.addresses YOUR_IP/24 \ ipv4.gateway YOUR_GATEWAY ipv4.dns "1.1.1.1,8.8.8.8" # Verify
ip addr show
# For Ethernet:
-weight: 600;">sudo nmcli con add con-name "static-eth" ifname YOUR_ETH_INTERFACE type ethernet \ ipv4.method manual ipv4.addresses YOUR_IP/24 \ ipv4.gateway YOUR_GATEWAY ipv4.dns "1.1.1.1,8.8.8.8"
-weight: 600;">sudo nmcli con up static-eth # For WiFi (adjust connection name from 'nmcli con show'):
-weight: 600;">sudo nmcli con modify "YOUR_WIFI_CONNECTION" \ ipv4.method manual ipv4.addresses YOUR_IP/24 \ ipv4.gateway YOUR_GATEWAY ipv4.dns "1.1.1.1,8.8.8.8" # Verify
ip addr show
# For Ethernet:
-weight: 600;">sudo nmcli con add con-name "static-eth" ifname YOUR_ETH_INTERFACE type ethernet \ ipv4.method manual ipv4.addresses YOUR_IP/24 \ ipv4.gateway YOUR_GATEWAY ipv4.dns "1.1.1.1,8.8.8.8"
-weight: 600;">sudo nmcli con up static-eth # For WiFi (adjust connection name from 'nmcli con show'):
-weight: 600;">sudo nmcli con modify "YOUR_WIFI_CONNECTION" \ ipv4.method manual ipv4.addresses YOUR_IP/24 \ ipv4.gateway YOUR_GATEWAY ipv4.dns "1.1.1.1,8.8.8.8" # Verify
ip addr show
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y intel-media-va-driver vainfo mesa-va-drivers
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y intel-media-va-driver vainfo mesa-va-drivers
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y intel-media-va-driver vainfo mesa-va-drivers
VA-API version: 1.20
Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0
VA-API version: 1.20
Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0
VA-API version: 1.20
Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0
# Install Docker using the official convenience script
-weight: 500;">curl -fsSL https://get.-weight: 500;">docker.com | sh # Add your user to the -weight: 500;">docker group (avoids needing -weight: 600;">sudo for -weight: 500;">docker commands)
-weight: 600;">sudo usermod -aG -weight: 500;">docker $USER # Log out and back in for group change to take effect
# Verify:
-weight: 500;">docker --version
-weight: 500;">docker compose version
# Install Docker using the official convenience script
-weight: 500;">curl -fsSL https://get.-weight: 500;">docker.com | sh # Add your user to the -weight: 500;">docker group (avoids needing -weight: 600;">sudo for -weight: 500;">docker commands)
-weight: 600;">sudo usermod -aG -weight: 500;">docker $USER # Log out and back in for group change to take effect
# Verify:
-weight: 500;">docker --version
-weight: 500;">docker compose version
# Install Docker using the official convenience script
-weight: 500;">curl -fsSL https://get.-weight: 500;">docker.com | sh # Add your user to the -weight: 500;">docker group (avoids needing -weight: 600;">sudo for -weight: 500;">docker commands)
-weight: 600;">sudo usermod -aG -weight: 500;">docker $USER # Log out and back in for group change to take effect
# Verify:
-weight: 500;">docker --version
-weight: 500;">docker compose version
# Expand LVM to use full disk (Ubuntu often allocates only ~100GB during -weight: 500;">install)
-weight: 600;">sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
-weight: 600;">sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
# Expand LVM to use full disk (Ubuntu often allocates only ~100GB during -weight: 500;">install)
-weight: 600;">sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
-weight: 600;">sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
# Expand LVM to use full disk (Ubuntu often allocates only ~100GB during -weight: 500;">install)
-weight: 600;">sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
-weight: 600;">sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
# Create all directories
-weight: 600;">sudo mkdir -p /opt/appdata /opt/-weight: 500;">docker/compose
-weight: 600;">sudo mkdir -p /mnt/media/{downloads/{torrents/{movies,tv,music},usenet/{movies,tv,music}},movies,tv,music,books,nextcloud} # Set ownership to your user (UID/GID 1000)
-weight: 600;">sudo chown -R 1000:1000 /opt/appdata /opt/-weight: 500;">docker /mnt/media
# Create all directories
-weight: 600;">sudo mkdir -p /opt/appdata /opt/-weight: 500;">docker/compose
-weight: 600;">sudo mkdir -p /mnt/media/{downloads/{torrents/{movies,tv,music},usenet/{movies,tv,music}},movies,tv,music,books,nextcloud} # Set ownership to your user (UID/GID 1000)
-weight: 600;">sudo chown -R 1000:1000 /opt/appdata /opt/-weight: 500;">docker /mnt/media
# Create all directories
-weight: 600;">sudo mkdir -p /opt/appdata /opt/-weight: 500;">docker/compose
-weight: 600;">sudo mkdir -p /mnt/media/{downloads/{torrents/{movies,tv,music},usenet/{movies,tv,music}},movies,tv,music,books,nextcloud} # Set ownership to your user (UID/GID 1000)
-weight: 600;">sudo chown -R 1000:1000 /opt/appdata /opt/-weight: 500;">docker /mnt/media
# Disable sleep/suspend/hibernate
-weight: 600;">sudo -weight: 500;">systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target # Disable Wi-Fi power management (prevents random disconnects if using WiFi)
# Edit /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf
# Change wifi.powersave = 2 (or 3) to:
# wifi.powersave = 0
# Disable sleep/suspend/hibernate
-weight: 600;">sudo -weight: 500;">systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target # Disable Wi-Fi power management (prevents random disconnects if using WiFi)
# Edit /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf
# Change wifi.powersave = 2 (or 3) to:
# wifi.powersave = 0
# Disable sleep/suspend/hibernate
-weight: 600;">sudo -weight: 500;">systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target # Disable Wi-Fi power management (prevents random disconnects if using WiFi)
# Edit /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf
# Change wifi.powersave = 2 (or 3) to:
# wifi.powersave = 0
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y unattended-upgrades
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y unattended-upgrades
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y unattended-upgrades
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y thermald
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y thermald
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y thermald
# User/Group IDs (run 'id' to check yours — usually 1000:1000)
PUID=1000
PGID=1000
TZ=YOUR_TIMEZONE # Paths
MEDIA_ROOT=/mnt/media
APPDATA=/opt/appdata # Gluetun VPN (optional — fill in if you want VPN-protected torrents)
# VPN_SERVICE_PROVIDER=mullvad
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_key_here
# WIREGUARD_ADDRESSES=your_address_here
# SERVER_COUNTRIES=Netherlands
# User/Group IDs (run 'id' to check yours — usually 1000:1000)
PUID=1000
PGID=1000
TZ=YOUR_TIMEZONE # Paths
MEDIA_ROOT=/mnt/media
APPDATA=/opt/appdata # Gluetun VPN (optional — fill in if you want VPN-protected torrents)
# VPN_SERVICE_PROVIDER=mullvad
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_key_here
# WIREGUARD_ADDRESSES=your_address_here
# SERVER_COUNTRIES=Netherlands
# User/Group IDs (run 'id' to check yours — usually 1000:1000)
PUID=1000
PGID=1000
TZ=YOUR_TIMEZONE # Paths
MEDIA_ROOT=/mnt/media
APPDATA=/opt/appdata # Gluetun VPN (optional — fill in if you want VPN-protected torrents)
# VPN_SERVICE_PROVIDER=mullvad
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_key_here
# WIREGUARD_ADDRESSES=your_address_here
# SERVER_COUNTRIES=Netherlands
-weight: 500;">docker run -d \ --name portainer \ ---weight: 500;">restart=always \ -p 9000:9000 \ -v /var/run/-weight: 500;">docker.sock:/var/run/-weight: 500;">docker.sock \ -v /opt/appdata/portainer:/data \ portainer/portainer-ce:latest
-weight: 500;">docker run -d \ --name portainer \ ---weight: 500;">restart=always \ -p 9000:9000 \ -v /var/run/-weight: 500;">docker.sock:/var/run/-weight: 500;">docker.sock \ -v /opt/appdata/portainer:/data \ portainer/portainer-ce:latest
-weight: 500;">docker run -d \ --name portainer \ ---weight: 500;">restart=always \ -p 9000:9000 \ -v /var/run/-weight: 500;">docker.sock:/var/run/-weight: 500;">docker.sock \ -v /opt/appdata/portainer:/data \ portainer/portainer-ce:latest
# Install Tailscale directly on the host (not Docker — more reliable on laptops)
-weight: 500;">curl -fsSL https://tailscale.com/-weight: 500;">install.sh | sh # Start and authenticate
-weight: 600;">sudo tailscale up # Follow the URL printed to authenticate with your Tailscale account
# (create free account at https://tailscale.com if you don't have one) # Verify
tailscale -weight: 500;">status
# Install Tailscale directly on the host (not Docker — more reliable on laptops)
-weight: 500;">curl -fsSL https://tailscale.com/-weight: 500;">install.sh | sh # Start and authenticate
-weight: 600;">sudo tailscale up # Follow the URL printed to authenticate with your Tailscale account
# (create free account at https://tailscale.com if you don't have one) # Verify
tailscale -weight: 500;">status
# Install Tailscale directly on the host (not Docker — more reliable on laptops)
-weight: 500;">curl -fsSL https://tailscale.com/-weight: 500;">install.sh | sh # Start and authenticate
-weight: 600;">sudo tailscale up # Follow the URL printed to authenticate with your Tailscale account
# (create free account at https://tailscale.com if you don't have one) # Verify
tailscale -weight: 500;">status
-weight: 600;">sudo tailscale set --accept-dns=false
-weight: 600;">sudo tailscale set --accept-dns=false
-weight: 500;">docker run -d \ --name watchtower \ ---weight: 500;">restart=always \ -v /var/run/-weight: 500;">docker.sock:/var/run/-weight: 500;">docker.sock \ -e DOCKER_API_VERSION=1.53 \ -e TZ=Europe/London \ -e WATCHTOWER_CLEANUP=true \ -e WATCHTOWER_SCHEDULE="0 0 4 * * *" \ containrrr/watchtower:latest
-weight: 500;">docker run -d \ --name watchtower \ ---weight: 500;">restart=always \ -v /var/run/-weight: 500;">docker.sock:/var/run/-weight: 500;">docker.sock \ -e DOCKER_API_VERSION=1.53 \ -e TZ=Europe/London \ -e WATCHTOWER_CLEANUP=true \ -e WATCHTOWER_SCHEDULE="0 0 4 * * *" \ containrrr/watchtower:latest
-weight: 500;">docker run -d \ --name watchtower \ ---weight: 500;">restart=always \ -v /var/run/-weight: 500;">docker.sock:/var/run/-weight: 500;">docker.sock \ -e DOCKER_API_VERSION=1.53 \ -e TZ=Europe/London \ -e WATCHTOWER_CLEANUP=true \ -e WATCHTOWER_SCHEDULE="0 0 4 * * *" \ containrrr/watchtower:latest
-weight: 500;">docker run -d \ --name adguardhome \ ---weight: 500;">restart=always \ -p 53:53/tcp \ -p 53:53/udp \ -p 3000:3000/tcp \ -p 8053:80/tcp \ -v /opt/appdata/adguardhome/work:/opt/adguardhome/work \ -v /opt/appdata/adguardhome/conf:/opt/adguardhome/conf \ adguard/adguardhome:latest
-weight: 500;">docker run -d \ --name adguardhome \ ---weight: 500;">restart=always \ -p 53:53/tcp \ -p 53:53/udp \ -p 3000:3000/tcp \ -p 8053:80/tcp \ -v /opt/appdata/adguardhome/work:/opt/adguardhome/work \ -v /opt/appdata/adguardhome/conf:/opt/adguardhome/conf \ adguard/adguardhome:latest
-weight: 500;">docker run -d \ --name adguardhome \ ---weight: 500;">restart=always \ -p 53:53/tcp \ -p 53:53/udp \ -p 3000:3000/tcp \ -p 8053:80/tcp \ -v /opt/appdata/adguardhome/work:/opt/adguardhome/work \ -v /opt/appdata/adguardhome/conf:/opt/adguardhome/conf \ adguard/adguardhome:latest
-weight: 600;">sudo snap set nextcloud ports.http=8088 && -weight: 600;">sudo snap -weight: 500;">restart nextcloud
-weight: 600;">sudo snap set nextcloud ports.http=8088 && -weight: 600;">sudo snap -weight: 500;">restart nextcloud
-weight: 500;">docker run -d \ --name nginx-proxy-manager \ ---weight: 500;">restart=always \ -p 80:80 \ -p 443:443 \ -p 81:81 \ -v /opt/appdata/-weight: 500;">npm/data:/data \ -v /opt/appdata/-weight: 500;">npm/letsencrypt:/etc/letsencrypt \ jc21/nginx-proxy-manager:latest
-weight: 500;">docker run -d \ --name nginx-proxy-manager \ ---weight: 500;">restart=always \ -p 80:80 \ -p 443:443 \ -p 81:81 \ -v /opt/appdata/-weight: 500;">npm/data:/data \ -v /opt/appdata/-weight: 500;">npm/letsencrypt:/etc/letsencrypt \ jc21/nginx-proxy-manager:latest
-weight: 500;">docker run -d \ --name nginx-proxy-manager \ ---weight: 500;">restart=always \ -p 80:80 \ -p 443:443 \ -p 81:81 \ -v /opt/appdata/-weight: 500;">npm/data:/data \ -v /opt/appdata/-weight: 500;">npm/letsencrypt:/etc/letsencrypt \ jc21/nginx-proxy-manager:latest
services: qbittorrent: image: linuxserver/qbittorrent:latest container_name: qbittorrent ports: - 8080:8080 # qBittorrent WebUI - 6881:6881 # Torrent port (TCP) - 6881:6881/udp environment: - PUID=${PUID} - PGID=${PGID} - TZ=${TZ} - WEBUI_PORT=8080 volumes: - /opt/appdata/qbittorrent:/config - ${MEDIA_ROOT}/downloads/torrents:/downloads -weight: 500;">restart: always
services: qbittorrent: image: linuxserver/qbittorrent:latest container_name: qbittorrent ports: - 8080:8080 # qBittorrent WebUI - 6881:6881 # Torrent port (TCP) - 6881:6881/udp environment: - PUID=${PUID} - PGID=${PGID} - TZ=${TZ} - WEBUI_PORT=8080 volumes: - /opt/appdata/qbittorrent:/config - ${MEDIA_ROOT}/downloads/torrents:/downloads -weight: 500;">restart: always
services: qbittorrent: image: linuxserver/qbittorrent:latest container_name: qbittorrent ports: - 8080:8080 # qBittorrent WebUI - 6881:6881 # Torrent port (TCP) - 6881:6881/udp environment: - PUID=${PUID} - PGID=${PGID} - TZ=${TZ} - WEBUI_PORT=8080 volumes: - /opt/appdata/qbittorrent:/config - ${MEDIA_ROOT}/downloads/torrents:/downloads -weight: 500;">restart: always
cd /opt/-weight: 500;">docker
-weight: 500;">docker compose -f compose/downloads.yml --env-file .env up -d
cd /opt/-weight: 500;">docker
-weight: 500;">docker compose -f compose/downloads.yml --env-file .env up -d
cd /opt/-weight: 500;">docker
-weight: 500;">docker compose -f compose/downloads.yml --env-file .env up -d
-weight: 500;">docker logs qbittorrent 2>&1 | grep "temporary password"
-weight: 500;">docker logs qbittorrent 2>&1 | grep "temporary password"
-weight: 500;">docker logs qbittorrent 2>&1 | grep "temporary password"
-weight: 500;">docker run -d \ --name sabnzbd \ ---weight: 500;">restart=always \ -p 8888:8080 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/sabnzbd:/config \ -v /mnt/media/downloads/usenet:/downloads \ linuxserver/sabnzbd:latest
-weight: 500;">docker run -d \ --name sabnzbd \ ---weight: 500;">restart=always \ -p 8888:8080 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/sabnzbd:/config \ -v /mnt/media/downloads/usenet:/downloads \ linuxserver/sabnzbd:latest
-weight: 500;">docker run -d \ --name sabnzbd \ ---weight: 500;">restart=always \ -p 8888:8080 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/sabnzbd:/config \ -v /mnt/media/downloads/usenet:/downloads \ linuxserver/sabnzbd:latest
-weight: 500;">docker run -d \ --name prowlarr \ ---weight: 500;">restart=always \ -p 9696:9696 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/prowlarr:/config \ linuxserver/prowlarr:latest
-weight: 500;">docker run -d \ --name prowlarr \ ---weight: 500;">restart=always \ -p 9696:9696 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/prowlarr:/config \ linuxserver/prowlarr:latest
-weight: 500;">docker run -d \ --name prowlarr \ ---weight: 500;">restart=always \ -p 9696:9696 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/prowlarr:/config \ linuxserver/prowlarr:latest
-weight: 500;">docker run -d \ --name radarr \ ---weight: 500;">restart=always \ -p 7878:7878 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/radarr:/config \ -v /mnt/media:/media \ linuxserver/radarr:latest
-weight: 500;">docker run -d \ --name radarr \ ---weight: 500;">restart=always \ -p 7878:7878 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/radarr:/config \ -v /mnt/media:/media \ linuxserver/radarr:latest
-weight: 500;">docker run -d \ --name radarr \ ---weight: 500;">restart=always \ -p 7878:7878 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/radarr:/config \ -v /mnt/media:/media \ linuxserver/radarr:latest
-weight: 500;">docker run -d \ --name sonarr \ ---weight: 500;">restart=always \ -p 8989:8989 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/sonarr:/config \ -v /mnt/media:/media \ linuxserver/sonarr:latest
-weight: 500;">docker run -d \ --name sonarr \ ---weight: 500;">restart=always \ -p 8989:8989 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/sonarr:/config \ -v /mnt/media:/media \ linuxserver/sonarr:latest
-weight: 500;">docker run -d \ --name sonarr \ ---weight: 500;">restart=always \ -p 8989:8989 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/sonarr:/config \ -v /mnt/media:/media \ linuxserver/sonarr:latest
-weight: 500;">docker run -d \ --name lidarr \ ---weight: 500;">restart=always \ -p 8686:8686 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/lidarr:/config \ -v /mnt/media:/media \ linuxserver/lidarr:latest
-weight: 500;">docker run -d \ --name lidarr \ ---weight: 500;">restart=always \ -p 8686:8686 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/lidarr:/config \ -v /mnt/media:/media \ linuxserver/lidarr:latest
-weight: 500;">docker run -d \ --name lidarr \ ---weight: 500;">restart=always \ -p 8686:8686 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/lidarr:/config \ -v /mnt/media:/media \ linuxserver/lidarr:latest
-weight: 500;">docker run -d \ --name bazarr \ ---weight: 500;">restart=always \ -p 6767:6767 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/bazarr:/config \ -v /mnt/media:/media \ linuxserver/bazarr:latest
-weight: 500;">docker run -d \ --name bazarr \ ---weight: 500;">restart=always \ -p 6767:6767 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/bazarr:/config \ -v /mnt/media:/media \ linuxserver/bazarr:latest
-weight: 500;">docker run -d \ --name bazarr \ ---weight: 500;">restart=always \ -p 6767:6767 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -v /opt/appdata/bazarr:/config \ -v /mnt/media:/media \ linuxserver/bazarr:latest
-weight: 500;">docker run -d \ --name jellyseerr \ ---weight: 500;">restart=always \ -p 5055:5055 \ -e TZ=Europe/London \ -v /opt/appdata/jellyseerr:/app/config \ fallenbagel/jellyseerr:latest
-weight: 500;">docker run -d \ --name jellyseerr \ ---weight: 500;">restart=always \ -p 5055:5055 \ -e TZ=Europe/London \ -v /opt/appdata/jellyseerr:/app/config \ fallenbagel/jellyseerr:latest
-weight: 500;">docker run -d \ --name jellyseerr \ ---weight: 500;">restart=always \ -p 5055:5055 \ -e TZ=Europe/London \ -v /opt/appdata/jellyseerr:/app/config \ fallenbagel/jellyseerr:latest
-weight: 500;">docker run -d \ --name jellyfin \ ---weight: 500;">restart=always \ -p 8096:8096 \ --device=/dev/dri/renderD128:/dev/dri/renderD128 \ --device=/dev/dri/card1:/dev/dri/card1 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -e JELLYFIN_PublishedServerUrl=http://YOUR_IP:8096 \ -v /opt/appdata/jellyfin:/config \ -v /mnt/media/movies:/data/movies \ -v /mnt/media/tv:/data/tv \ -v /mnt/media/music:/data/music \ -v /mnt/media/books:/data/books \ jellyfin/jellyfin:latest
-weight: 500;">docker run -d \ --name jellyfin \ ---weight: 500;">restart=always \ -p 8096:8096 \ --device=/dev/dri/renderD128:/dev/dri/renderD128 \ --device=/dev/dri/card1:/dev/dri/card1 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -e JELLYFIN_PublishedServerUrl=http://YOUR_IP:8096 \ -v /opt/appdata/jellyfin:/config \ -v /mnt/media/movies:/data/movies \ -v /mnt/media/tv:/data/tv \ -v /mnt/media/music:/data/music \ -v /mnt/media/books:/data/books \ jellyfin/jellyfin:latest
-weight: 500;">docker run -d \ --name jellyfin \ ---weight: 500;">restart=always \ -p 8096:8096 \ --device=/dev/dri/renderD128:/dev/dri/renderD128 \ --device=/dev/dri/card1:/dev/dri/card1 \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -e JELLYFIN_PublishedServerUrl=http://YOUR_IP:8096 \ -v /opt/appdata/jellyfin:/config \ -v /mnt/media/movies:/data/movies \ -v /mnt/media/tv:/data/tv \ -v /mnt/media/music:/data/music \ -v /mnt/media/books:/data/books \ jellyfin/jellyfin:latest
# Should show GPU activity (render/video percentages above 0%)
-weight: 600;">sudo intel_gpu_top
# (-weight: 500;">install with: -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y intel-gpu-tools)
# Should show GPU activity (render/video percentages above 0%)
-weight: 600;">sudo intel_gpu_top
# (-weight: 500;">install with: -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y intel-gpu-tools)
# Should show GPU activity (render/video percentages above 0%)
-weight: 600;">sudo intel_gpu_top
# (-weight: 500;">install with: -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y intel-gpu-tools)
-weight: 600;">sudo snap -weight: 500;">install nextcloud
-weight: 600;">sudo snap -weight: 500;">install nextcloud
-weight: 600;">sudo snap -weight: 500;">install nextcloud
-weight: 600;">sudo snap set nextcloud ports.http=8088
-weight: 600;">sudo snap -weight: 500;">restart nextcloud
-weight: 600;">sudo snap set nextcloud ports.http=8088
-weight: 600;">sudo snap -weight: 500;">restart nextcloud
-weight: 600;">sudo snap set nextcloud ports.http=8088
-weight: 600;">sudo snap -weight: 500;">restart nextcloud
-weight: 500;">docker run -d \ --name nextcloud-db \ ---weight: 500;">restart=always \ -e MYSQL_ROOT_PASSWORD=CHANGE_ME_ROOT_PW \ -e MYSQL_DATABASE=nextcloud \ -e MYSQL_USER=nextcloud \ -e MYSQL_PASSWORD=CHANGE_ME_NC_PW \ -v /opt/appdata/nextcloud-db:/var/lib/mysql \ mariadb:latest \ --transaction-isolation=READ-COMMITTED \ --log-bin=binlog \ --binlog-format=ROW
-weight: 500;">docker run -d \ --name nextcloud-db \ ---weight: 500;">restart=always \ -e MYSQL_ROOT_PASSWORD=CHANGE_ME_ROOT_PW \ -e MYSQL_DATABASE=nextcloud \ -e MYSQL_USER=nextcloud \ -e MYSQL_PASSWORD=CHANGE_ME_NC_PW \ -v /opt/appdata/nextcloud-db:/var/lib/mysql \ mariadb:latest \ --transaction-isolation=READ-COMMITTED \ --log-bin=binlog \ --binlog-format=ROW
-weight: 500;">docker run -d \ --name nextcloud-db \ ---weight: 500;">restart=always \ -e MYSQL_ROOT_PASSWORD=CHANGE_ME_ROOT_PW \ -e MYSQL_DATABASE=nextcloud \ -e MYSQL_USER=nextcloud \ -e MYSQL_PASSWORD=CHANGE_ME_NC_PW \ -v /opt/appdata/nextcloud-db:/var/lib/mysql \ mariadb:latest \ --transaction-isolation=READ-COMMITTED \ --log-bin=binlog \ --binlog-format=ROW
-weight: 500;">docker run -d \ --name nextcloud \ ---weight: 500;">restart=always \ -p 8443:80 \ -e MYSQL_HOST=nextcloud-db \ -e MYSQL_DATABASE=nextcloud \ -e MYSQL_USER=nextcloud \ -e MYSQL_PASSWORD=CHANGE_ME_NC_PW \ -e NEXTCLOUD_ADMIN_USER=admin \ -e NEXTCLOUD_ADMIN_PASSWORD=CHANGE_ME_ADMIN_PW \ -e NEXTCLOUD_TRUSTED_DOMAINS="YOUR_IP localhost" \ --link nextcloud-db:nextcloud-db \ -v /opt/appdata/nextcloud:/var/www/html \ -v /mnt/media/nextcloud:/var/www/html/data \ nextcloud:latest
-weight: 500;">docker run -d \ --name nextcloud \ ---weight: 500;">restart=always \ -p 8443:80 \ -e MYSQL_HOST=nextcloud-db \ -e MYSQL_DATABASE=nextcloud \ -e MYSQL_USER=nextcloud \ -e MYSQL_PASSWORD=CHANGE_ME_NC_PW \ -e NEXTCLOUD_ADMIN_USER=admin \ -e NEXTCLOUD_ADMIN_PASSWORD=CHANGE_ME_ADMIN_PW \ -e NEXTCLOUD_TRUSTED_DOMAINS="YOUR_IP localhost" \ --link nextcloud-db:nextcloud-db \ -v /opt/appdata/nextcloud:/var/www/html \ -v /mnt/media/nextcloud:/var/www/html/data \ nextcloud:latest
-weight: 500;">docker run -d \ --name nextcloud \ ---weight: 500;">restart=always \ -p 8443:80 \ -e MYSQL_HOST=nextcloud-db \ -e MYSQL_DATABASE=nextcloud \ -e MYSQL_USER=nextcloud \ -e MYSQL_PASSWORD=CHANGE_ME_NC_PW \ -e NEXTCLOUD_ADMIN_USER=admin \ -e NEXTCLOUD_ADMIN_PASSWORD=CHANGE_ME_ADMIN_PW \ -e NEXTCLOUD_TRUSTED_DOMAINS="YOUR_IP localhost" \ --link nextcloud-db:nextcloud-db \ -v /opt/appdata/nextcloud:/var/www/html \ -v /mnt/media/nextcloud:/var/www/html/data \ nextcloud:latest
-weight: 500;">docker run -d \ --name tdarr \ ---weight: 500;">restart=always \ -p 8265:8265 \ -p 8266:8266 \ --device=/dev/dri:/dev/dri \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -e serverIP=0.0.0.0 \ -e serverPort=8266 \ -e webUIPort=8265 \ -e internalNode=true \ -e inContainer=true \ -e nodeName=MediaServer \ -v /opt/appdata/tdarr/server:/app/server \ -v /opt/appdata/tdarr/configs:/app/configs \ -v /opt/appdata/tdarr/logs:/app/logs \ -v /mnt/media:/media \ -v /opt/appdata/tdarr/transcode_cache:/temp \ ghcr.io/haveagitgat/tdarr:latest
-weight: 500;">docker run -d \ --name tdarr \ ---weight: 500;">restart=always \ -p 8265:8265 \ -p 8266:8266 \ --device=/dev/dri:/dev/dri \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -e serverIP=0.0.0.0 \ -e serverPort=8266 \ -e webUIPort=8265 \ -e internalNode=true \ -e inContainer=true \ -e nodeName=MediaServer \ -v /opt/appdata/tdarr/server:/app/server \ -v /opt/appdata/tdarr/configs:/app/configs \ -v /opt/appdata/tdarr/logs:/app/logs \ -v /mnt/media:/media \ -v /opt/appdata/tdarr/transcode_cache:/temp \ ghcr.io/haveagitgat/tdarr:latest
-weight: 500;">docker run -d \ --name tdarr \ ---weight: 500;">restart=always \ -p 8265:8265 \ -p 8266:8266 \ --device=/dev/dri:/dev/dri \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Europe/London \ -e serverIP=0.0.0.0 \ -e serverPort=8266 \ -e webUIPort=8265 \ -e internalNode=true \ -e inContainer=true \ -e nodeName=MediaServer \ -v /opt/appdata/tdarr/server:/app/server \ -v /opt/appdata/tdarr/configs:/app/configs \ -v /opt/appdata/tdarr/logs:/app/logs \ -v /mnt/media:/media \ -v /opt/appdata/tdarr/transcode_cache:/temp \ ghcr.io/haveagitgat/tdarr:latest
-weight: 500;">docker run -d \ --name ollama \ ---weight: 500;">restart=unless-stopped \ -p 11434:11434 \ -v /opt/appdata/ollama:/root/.ollama \ ollama/ollama:latest
-weight: 500;">docker run -d \ --name ollama \ ---weight: 500;">restart=unless-stopped \ -p 11434:11434 \ -v /opt/appdata/ollama:/root/.ollama \ ollama/ollama:latest
-weight: 500;">docker run -d \ --name ollama \ ---weight: 500;">restart=unless-stopped \ -p 11434:11434 \ -v /opt/appdata/ollama:/root/.ollama \ ollama/ollama:latest
-weight: 500;">docker exec ollama ollama pull mistral:7b
# Or a smaller model if RAM is tight:
-weight: 500;">docker exec ollama ollama pull phi3:mini
-weight: 500;">docker exec ollama ollama pull mistral:7b
# Or a smaller model if RAM is tight:
-weight: 500;">docker exec ollama ollama pull phi3:mini
-weight: 500;">docker exec ollama ollama pull mistral:7b
# Or a smaller model if RAM is tight:
-weight: 500;">docker exec ollama ollama pull phi3:mini
-weight: 500;">docker exec ollama ollama run mistral:7b "What is a homelab?"
-weight: 500;">docker exec ollama ollama run mistral:7b "What is a homelab?"
-weight: 500;">docker exec ollama ollama run mistral:7b "What is a homelab?"
-weight: 500;">docker run -d \ --name uptime-kuma \ ---weight: 500;">restart=always \ -p 3001:3001 \ -v /opt/appdata/uptime-kuma:/app/data \ louislam/uptime-kuma:latest
-weight: 500;">docker run -d \ --name uptime-kuma \ ---weight: 500;">restart=always \ -p 3001:3001 \ -v /opt/appdata/uptime-kuma:/app/data \ louislam/uptime-kuma:latest
-weight: 500;">docker run -d \ --name uptime-kuma \ ---weight: 500;">restart=always \ -p 3001:3001 \ -v /opt/appdata/uptime-kuma:/app/data \ louislam/uptime-kuma:latest
User requests movie Radarr searches indexers qBittorrent downloads
via Jellyseerr (:5055) --> via Prowlarr (:9696) --> to NVMe: /downloads/torrents/ | v Download completes | v Radarr detects completion, qBittorrent seeds to imports (copies) movie <-- ratio 1.0, then auto-removes to SSD: /media/movies/ torrent from list | v Radarr deletes source files from /downloads/torrents/ (removeCompletedDownloads: true) | v Jellyfin detects new movie Bazarr auto-downloads in /data/movies/ library --> subtitles (.srt/.ass) | v Tdarr (optional) transcodes to H.265 using Intel QSV | v Movie available for playback on all devices (TV, phone, web)
User requests movie Radarr searches indexers qBittorrent downloads
via Jellyseerr (:5055) --> via Prowlarr (:9696) --> to NVMe: /downloads/torrents/ | v Download completes | v Radarr detects completion, qBittorrent seeds to imports (copies) movie <-- ratio 1.0, then auto-removes to SSD: /media/movies/ torrent from list | v Radarr deletes source files from /downloads/torrents/ (removeCompletedDownloads: true) | v Jellyfin detects new movie Bazarr auto-downloads in /data/movies/ library --> subtitles (.srt/.ass) | v Tdarr (optional) transcodes to H.265 using Intel QSV | v Movie available for playback on all devices (TV, phone, web)
User requests movie Radarr searches indexers qBittorrent downloads
via Jellyseerr (:5055) --> via Prowlarr (:9696) --> to NVMe: /downloads/torrents/ | v Download completes | v Radarr detects completion, qBittorrent seeds to imports (copies) movie <-- ratio 1.0, then auto-removes to SSD: /media/movies/ torrent from list | v Radarr deletes source files from /downloads/torrents/ (removeCompletedDownloads: true) | v Jellyfin detects new movie Bazarr auto-downloads in /data/movies/ library --> subtitles (.srt/.ass) | v Tdarr (optional) transcodes to H.265 using Intel QSV | v Movie available for playback on all devices (TV, phone, web)
NVMe (boot drive) -- Fast, temporary storage Second Drive -- Movie library
+----------------------------------+ +------------------------------+
| /mnt/media/downloads/torrents/ | | /mnt/ssd/movies/ |
| +-- Movie.Name.1080p.x264/ |--(Radarr)----| +-- Inception (2010)/ |
| | +-- movie.mkv (temp) | copies & | | +-- Inception.mkv |
| +-- (auto-cleaned after 7d) | deletes | +-- Interstellar (2014)/ |
| | source | | +-- Interstellar.mkv |
| /mnt/media/tv/ | | +-- ... |
| +-- Show Name/Season 01/ | | |
| +-- ... | | Bind-mounted to: |
| | | /mnt/media/movies/ |
| /mnt/media/music/ | | (transparent to containers) |
| /mnt/media/books/ | +------------------------------+
+----------------------------------+
NVMe (boot drive) -- Fast, temporary storage Second Drive -- Movie library
+----------------------------------+ +------------------------------+
| /mnt/media/downloads/torrents/ | | /mnt/ssd/movies/ |
| +-- Movie.Name.1080p.x264/ |--(Radarr)----| +-- Inception (2010)/ |
| | +-- movie.mkv (temp) | copies & | | +-- Inception.mkv |
| +-- (auto-cleaned after 7d) | deletes | +-- Interstellar (2014)/ |
| | source | | +-- Interstellar.mkv |
| /mnt/media/tv/ | | +-- ... |
| +-- Show Name/Season 01/ | | |
| +-- ... | | Bind-mounted to: |
| | | /mnt/media/movies/ |
| /mnt/media/music/ | | (transparent to containers) |
| /mnt/media/books/ | +------------------------------+
+----------------------------------+
NVMe (boot drive) -- Fast, temporary storage Second Drive -- Movie library
+----------------------------------+ +------------------------------+
| /mnt/media/downloads/torrents/ | | /mnt/ssd/movies/ |
| +-- Movie.Name.1080p.x264/ |--(Radarr)----| +-- Inception (2010)/ |
| | +-- movie.mkv (temp) | copies & | | +-- Inception.mkv |
| +-- (auto-cleaned after 7d) | deletes | +-- Interstellar (2014)/ |
| | source | | +-- Interstellar.mkv |
| /mnt/media/tv/ | | +-- ... |
| +-- Show Name/Season 01/ | | |
| +-- ... | | Bind-mounted to: |
| | | /mnt/media/movies/ |
| /mnt/media/music/ | | (transparent to containers) |
| /mnt/media/books/ | +------------------------------+
+----------------------------------+
#!/bin/bash
# Backup all container configs
BACKUP_DIR="/mnt/media/backups"
DATE=$(date +%Y%m%d) mkdir -p $BACKUP_DIR # Stop containers that use databases for clean backup
-weight: 500;">docker -weight: 500;">stop nextcloud-db # Create tarball of all configs
tar -czf $BACKUP_DIR/appdata-$DATE.tar.gz -C /opt appdata # Restart stopped containers
-weight: 500;">docker -weight: 500;">start nextcloud-db # Keep only last 7 backups
ls -tp $BACKUP_DIR/appdata-*.tar.gz | tail -n +8 | xargs -I {} rm -- {} echo "Backup completed: $BACKUP_DIR/appdata-$DATE.tar.gz"
#!/bin/bash
# Backup all container configs
BACKUP_DIR="/mnt/media/backups"
DATE=$(date +%Y%m%d) mkdir -p $BACKUP_DIR # Stop containers that use databases for clean backup
-weight: 500;">docker -weight: 500;">stop nextcloud-db # Create tarball of all configs
tar -czf $BACKUP_DIR/appdata-$DATE.tar.gz -C /opt appdata # Restart stopped containers
-weight: 500;">docker -weight: 500;">start nextcloud-db # Keep only last 7 backups
ls -tp $BACKUP_DIR/appdata-*.tar.gz | tail -n +8 | xargs -I {} rm -- {} echo "Backup completed: $BACKUP_DIR/appdata-$DATE.tar.gz"
#!/bin/bash
# Backup all container configs
BACKUP_DIR="/mnt/media/backups"
DATE=$(date +%Y%m%d) mkdir -p $BACKUP_DIR # Stop containers that use databases for clean backup
-weight: 500;">docker -weight: 500;">stop nextcloud-db # Create tarball of all configs
tar -czf $BACKUP_DIR/appdata-$DATE.tar.gz -C /opt appdata # Restart stopped containers
-weight: 500;">docker -weight: 500;">start nextcloud-db # Keep only last 7 backups
ls -tp $BACKUP_DIR/appdata-*.tar.gz | tail -n +8 | xargs -I {} rm -- {} echo "Backup completed: $BACKUP_DIR/appdata-$DATE.tar.gz"
chmod +x /opt/-weight: 500;">docker/backup.sh # Test it
/opt/-weight: 500;">docker/backup.sh # Schedule weekly (Sunday 3AM)
crontab -e
# Add this line:
# 0 3 * * 0 /opt/-weight: 500;">docker/backup.sh >> /var/log/backup.log 2>&1
chmod +x /opt/-weight: 500;">docker/backup.sh # Test it
/opt/-weight: 500;">docker/backup.sh # Schedule weekly (Sunday 3AM)
crontab -e
# Add this line:
# 0 3 * * 0 /opt/-weight: 500;">docker/backup.sh >> /var/log/backup.log 2>&1
chmod +x /opt/-weight: 500;">docker/backup.sh # Test it
/opt/-weight: 500;">docker/backup.sh # Schedule weekly (Sunday 3AM)
crontab -e
# Add this line:
# 0 3 * * 0 /opt/-weight: 500;">docker/backup.sh >> /var/log/backup.log 2>&1
Session\GlobalMaxRatio=1
Session\GlobalMaxSeedingMinutes=1440
Session\GlobalMaxInactiveSeedingMinutes=1440
Session\ShareLimitAction=Remove
Session\GlobalMaxRatio=1
Session\GlobalMaxSeedingMinutes=1440
Session\GlobalMaxInactiveSeedingMinutes=1440
Session\ShareLimitAction=Remove
Session\GlobalMaxRatio=1
Session\GlobalMaxSeedingMinutes=1440
Session\GlobalMaxInactiveSeedingMinutes=1440
Session\ShareLimitAction=Remove
# Root crontab entry:
*/15 * * * * /opt/-weight: 500;">docker/disk-guard.sh
# Root crontab entry:
*/15 * * * * /opt/-weight: 500;">docker/disk-guard.sh
# Root crontab entry:
*/15 * * * * /opt/-weight: 500;">docker/disk-guard.sh
# View recent log entries
tail -20 /var/log/disk-guard.log # Manual run
-weight: 600;">sudo /opt/-weight: 500;">docker/disk-guard.sh # Check current disk usage
df -h / /mnt/ssd
# View recent log entries
tail -20 /var/log/disk-guard.log # Manual run
-weight: 600;">sudo /opt/-weight: 500;">docker/disk-guard.sh # Check current disk usage
df -h / /mnt/ssd
# View recent log entries
tail -20 /var/log/disk-guard.log # Manual run
-weight: 600;">sudo /opt/-weight: 500;">docker/disk-guard.sh # Check current disk usage
df -h / /mnt/ssd
# Identify the new drive
lsblk # Create GPT partition table + single partition
-weight: 600;">sudo fdisk /dev/sda
# g (new GPT), n (new partition), 1, Enter, Enter, w (write) # Format as ext4
-weight: 600;">sudo mkfs.ext4 -L media-ssd /dev/sda1 # Create mount point and mount
-weight: 600;">sudo mkdir -p /mnt/ssd
-weight: 600;">sudo mount /dev/sda1 /mnt/ssd # Get UUID and add to fstab for persistent mounting
UUID=$(-weight: 600;">sudo blkid -s UUID -o value /dev/sda1)
echo "UUID=$UUID /mnt/ssd ext4 defaults 0 2" | -weight: 600;">sudo tee -a /etc/fstab
# Identify the new drive
lsblk # Create GPT partition table + single partition
-weight: 600;">sudo fdisk /dev/sda
# g (new GPT), n (new partition), 1, Enter, Enter, w (write) # Format as ext4
-weight: 600;">sudo mkfs.ext4 -L media-ssd /dev/sda1 # Create mount point and mount
-weight: 600;">sudo mkdir -p /mnt/ssd
-weight: 600;">sudo mount /dev/sda1 /mnt/ssd # Get UUID and add to fstab for persistent mounting
UUID=$(-weight: 600;">sudo blkid -s UUID -o value /dev/sda1)
echo "UUID=$UUID /mnt/ssd ext4 defaults 0 2" | -weight: 600;">sudo tee -a /etc/fstab
# Identify the new drive
lsblk # Create GPT partition table + single partition
-weight: 600;">sudo fdisk /dev/sda
# g (new GPT), n (new partition), 1, Enter, Enter, w (write) # Format as ext4
-weight: 600;">sudo mkfs.ext4 -L media-ssd /dev/sda1 # Create mount point and mount
-weight: 600;">sudo mkdir -p /mnt/ssd
-weight: 600;">sudo mount /dev/sda1 /mnt/ssd # Get UUID and add to fstab for persistent mounting
UUID=$(-weight: 600;">sudo blkid -s UUID -o value /dev/sda1)
echo "UUID=$UUID /mnt/ssd ext4 defaults 0 2" | -weight: 600;">sudo tee -a /etc/fstab
# Create movies directory on the new drive
-weight: 600;">sudo mkdir -p /mnt/ssd/movies
-weight: 600;">sudo chown YOUR_USER:YOUR_USER /mnt/ssd /mnt/ssd/movies # Copy movies to the new drive
cp -av /mnt/media/movies/* /mnt/ssd/movies/ # Verify both locations match
ls /mnt/media/movies/ | wc -l
ls /mnt/ssd/movies/ | wc -l # Remove originals from the boot drive
rm -rf /mnt/media/movies/* # Bind mount (immediate)
-weight: 600;">sudo mount --bind /mnt/ssd/movies /mnt/media/movies # Persist in fstab
echo "/mnt/ssd/movies /mnt/media/movies none bind 0 0" | -weight: 600;">sudo tee -a /etc/fstab # Restart Jellyfin to pick up the bind mount
-weight: 500;">docker -weight: 500;">restart jellyfin
# Create movies directory on the new drive
-weight: 600;">sudo mkdir -p /mnt/ssd/movies
-weight: 600;">sudo chown YOUR_USER:YOUR_USER /mnt/ssd /mnt/ssd/movies # Copy movies to the new drive
cp -av /mnt/media/movies/* /mnt/ssd/movies/ # Verify both locations match
ls /mnt/media/movies/ | wc -l
ls /mnt/ssd/movies/ | wc -l # Remove originals from the boot drive
rm -rf /mnt/media/movies/* # Bind mount (immediate)
-weight: 600;">sudo mount --bind /mnt/ssd/movies /mnt/media/movies # Persist in fstab
echo "/mnt/ssd/movies /mnt/media/movies none bind 0 0" | -weight: 600;">sudo tee -a /etc/fstab # Restart Jellyfin to pick up the bind mount
-weight: 500;">docker -weight: 500;">restart jellyfin
# Create movies directory on the new drive
-weight: 600;">sudo mkdir -p /mnt/ssd/movies
-weight: 600;">sudo chown YOUR_USER:YOUR_USER /mnt/ssd /mnt/ssd/movies # Copy movies to the new drive
cp -av /mnt/media/movies/* /mnt/ssd/movies/ # Verify both locations match
ls /mnt/media/movies/ | wc -l
ls /mnt/ssd/movies/ | wc -l # Remove originals from the boot drive
rm -rf /mnt/media/movies/* # Bind mount (immediate)
-weight: 600;">sudo mount --bind /mnt/ssd/movies /mnt/media/movies # Persist in fstab
echo "/mnt/ssd/movies /mnt/media/movies none bind 0 0" | -weight: 600;">sudo tee -a /etc/fstab # Restart Jellyfin to pick up the bind mount
-weight: 500;">docker -weight: 500;">restart jellyfin
-weight: 600;">sudo tune2fs -m 1 /dev/mapper/ubuntu--vg-ubuntu--lv
-weight: 600;">sudo tune2fs -m 1 /dev/mapper/ubuntu--vg-ubuntu--lv
-weight: 600;">sudo tune2fs -m 1 /dev/mapper/ubuntu--vg-ubuntu--lv
-weight: 600;">sudo tailscale set --accept-dns=false
-weight: 600;">sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-resolved
-weight: 600;">sudo tailscale set --accept-dns=false
-weight: 600;">sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-resolved
-weight: 600;">sudo tailscale set --accept-dns=false
-weight: 600;">sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-resolved
-weight: 500;">docker logs <container_name>
# Check for permission errors — usually fix with:
-weight: 600;">sudo chown -R 1000:1000 /opt/appdata/<container_name>
-weight: 500;">docker logs <container_name>
# Check for permission errors — usually fix with:
-weight: 600;">sudo chown -R 1000:1000 /opt/appdata/<container_name>
-weight: 500;">docker logs <container_name>
# Check for permission errors — usually fix with:
-weight: 600;">sudo chown -R 1000:1000 /opt/appdata/<container_name>
# Check GPU device exists
ls -la /dev/dri/
# Should show: card0 (or card1) and renderD128 # Check VA-API driver
vainfo
# Should show "iHD driver" # Fix permissions
-weight: 600;">sudo usermod -aG render $USER
-weight: 600;">sudo usermod -aG video $USER
# Log out/in, then -weight: 500;">restart Jellyfin container
-weight: 500;">docker -weight: 500;">restart jellyfin
# Check GPU device exists
ls -la /dev/dri/
# Should show: card0 (or card1) and renderD128 # Check VA-API driver
vainfo
# Should show "iHD driver" # Fix permissions
-weight: 600;">sudo usermod -aG render $USER
-weight: 600;">sudo usermod -aG video $USER
# Log out/in, then -weight: 500;">restart Jellyfin container
-weight: 500;">docker -weight: 500;">restart jellyfin
# Check GPU device exists
ls -la /dev/dri/
# Should show: card0 (or card1) and renderD128 # Check VA-API driver
vainfo
# Should show "iHD driver" # Fix permissions
-weight: 600;">sudo usermod -aG render $USER
-weight: 600;">sudo usermod -aG video $USER
# Log out/in, then -weight: 500;">restart Jellyfin container
-weight: 500;">docker -weight: 500;">restart jellyfin
# Install thermal management
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y thermald # Check temperatures
sensors
# (-weight: 500;">install with: -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y lm-sensors && -weight: 600;">sudo sensors-detect)
# Install thermal management
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y thermald # Check temperatures
sensors
# (-weight: 500;">install with: -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y lm-sensors && -weight: 600;">sudo sensors-detect)
# Install thermal management
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y thermald # Check temperatures
sensors
# (-weight: 500;">install with: -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y lm-sensors && -weight: 600;">sudo sensors-detect)
# Verify Tailscale is connected
tailscale -weight: 500;">status # Check if firewall is blocking
-weight: 600;">sudo ufw -weight: 500;">status
# If active, allow ports:
-weight: 600;">sudo ufw allow 8096/tcp # Jellyfin
-weight: 600;">sudo ufw allow 5055/tcp # Jellyseerr
# (add other ports as needed)
# Verify Tailscale is connected
tailscale -weight: 500;">status # Check if firewall is blocking
-weight: 600;">sudo ufw -weight: 500;">status
# If active, allow ports:
-weight: 600;">sudo ufw allow 8096/tcp # Jellyfin
-weight: 600;">sudo ufw allow 5055/tcp # Jellyseerr
# (add other ports as needed)
# Verify Tailscale is connected
tailscale -weight: 500;">status # Check if firewall is blocking
-weight: 600;">sudo ufw -weight: 500;">status
# If active, allow ports:
-weight: 600;">sudo ufw allow 8096/tcp # Jellyfin
-weight: 600;">sudo ufw allow 5055/tcp # Jellyseerr
# (add other ports as needed)
# Disable USB autosuspend
echo -1 | -weight: 600;">sudo tee /sys/module/usbcore/parameters/autosuspend # Make permanent — add to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub:
# usbcore.autosuspend=-1
-weight: 600;">sudo -weight: 500;">update-grub
-weight: 600;">sudo reboot
# Disable USB autosuspend
echo -1 | -weight: 600;">sudo tee /sys/module/usbcore/parameters/autosuspend # Make permanent — add to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub:
# usbcore.autosuspend=-1
-weight: 600;">sudo -weight: 500;">update-grub
-weight: 600;">sudo reboot
# Disable USB autosuspend
echo -1 | -weight: 600;">sudo tee /sys/module/usbcore/parameters/autosuspend # Make permanent — add to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub:
# usbcore.autosuspend=-1
-weight: 600;">sudo -weight: 500;">update-grub
-weight: 600;">sudo reboot
# Check disk usage
df -h / /mnt/ssd # Run disk cleanup manually
-weight: 600;">sudo /opt/-weight: 500;">docker/disk-guard.sh # Check what is eating space
du -sh /mnt/media/downloads/torrents/ # Torrent downloads
du -sh /var/lib/-weight: 500;">docker/ # Docker images/layers
du -sh /var/lib/snapd/cache/ # Snap cache # If DNS is broken (Tailscale took over resolv.conf):
-weight: 600;">sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-resolved # Check disk-guard log
tail -20 /var/log/disk-guard.log
# Check disk usage
df -h / /mnt/ssd # Run disk cleanup manually
-weight: 600;">sudo /opt/-weight: 500;">docker/disk-guard.sh # Check what is eating space
du -sh /mnt/media/downloads/torrents/ # Torrent downloads
du -sh /var/lib/-weight: 500;">docker/ # Docker images/layers
du -sh /var/lib/snapd/cache/ # Snap cache # If DNS is broken (Tailscale took over resolv.conf):
-weight: 600;">sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-resolved # Check disk-guard log
tail -20 /var/log/disk-guard.log
# Check disk usage
df -h / /mnt/ssd # Run disk cleanup manually
-weight: 600;">sudo /opt/-weight: 500;">docker/disk-guard.sh # Check what is eating space
du -sh /mnt/media/downloads/torrents/ # Torrent downloads
du -sh /var/lib/-weight: 500;">docker/ # Docker images/layers
du -sh /var/lib/snapd/cache/ # Snap cache # If DNS is broken (Tailscale took over resolv.conf):
-weight: 600;">sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart systemd-resolved # Check disk-guard log
tail -20 /var/log/disk-guard.log
# See all running containers
-weight: 500;">docker ps # See all containers (including stopped)
-weight: 500;">docker ps -a # View container logs
-weight: 500;">docker logs <container_name>
-weight: 500;">docker logs -f <container_name> # Follow (live) # Restart a container
-weight: 500;">docker -weight: 500;">restart <container_name> # Stop / Start
-weight: 500;">docker -weight: 500;">stop <container_name>
-weight: 500;">docker -weight: 500;">start <container_name> # Remove a container (data in /opt/appdata is preserved)
-weight: 500;">docker -weight: 500;">stop <container_name> && -weight: 500;">docker rm <container_name> # Update a container manually
-weight: 500;">docker pull <image_name>:latest
-weight: 500;">docker -weight: 500;">stop <container_name> && -weight: 500;">docker rm <container_name>
# Then re-run the original -weight: 500;">docker run command # Check Docker disk usage
-weight: 500;">docker system df # Clean up unused images/containers
-weight: 500;">docker system prune -a
# See all running containers
-weight: 500;">docker ps # See all containers (including stopped)
-weight: 500;">docker ps -a # View container logs
-weight: 500;">docker logs <container_name>
-weight: 500;">docker logs -f <container_name> # Follow (live) # Restart a container
-weight: 500;">docker -weight: 500;">restart <container_name> # Stop / Start
-weight: 500;">docker -weight: 500;">stop <container_name>
-weight: 500;">docker -weight: 500;">start <container_name> # Remove a container (data in /opt/appdata is preserved)
-weight: 500;">docker -weight: 500;">stop <container_name> && -weight: 500;">docker rm <container_name> # Update a container manually
-weight: 500;">docker pull <image_name>:latest
-weight: 500;">docker -weight: 500;">stop <container_name> && -weight: 500;">docker rm <container_name>
# Then re-run the original -weight: 500;">docker run command # Check Docker disk usage
-weight: 500;">docker system df # Clean up unused images/containers
-weight: 500;">docker system prune -a
# See all running containers
-weight: 500;">docker ps # See all containers (including stopped)
-weight: 500;">docker ps -a # View container logs
-weight: 500;">docker logs <container_name>
-weight: 500;">docker logs -f <container_name> # Follow (live) # Restart a container
-weight: 500;">docker -weight: 500;">restart <container_name> # Stop / Start
-weight: 500;">docker -weight: 500;">stop <container_name>
-weight: 500;">docker -weight: 500;">start <container_name> # Remove a container (data in /opt/appdata is preserved)
-weight: 500;">docker -weight: 500;">stop <container_name> && -weight: 500;">docker rm <container_name> # Update a container manually
-weight: 500;">docker pull <image_name>:latest
-weight: 500;">docker -weight: 500;">stop <container_name> && -weight: 500;">docker rm <container_name>
# Then re-run the original -weight: 500;">docker run command # Check Docker disk usage
-weight: 500;">docker system df # Clean up unused images/containers
-weight: 500;">docker system prune -a - Introduction — Why Self-Host a Media Server
- Hardware Overview & Storage Strategy
- Ubuntu Installation & System Configuration
- Environment Configuration — Centralized .env File
- Management Stack — Portainer, Tailscale, Watchtower
- Network & Security — AdGuard Home & Nginx Proxy Manager
- Download Stack — qBittorrent
- *Arr Media Automation Suite
- Jellyfin Media Server
- Nextcloud — Self-Hosted Cloud Storage
- Media Processing — Tdarr & Ollama
- Monitoring — Uptime Kuma
- Integration & Automation — Connecting Everything Together
- Backup & Disk Protection
- Dashboard — Landing Page with Service Links
- Troubleshooting — Common Issues and Fixes - Complete control over your library — no content disappearing overnight
- No monthly fees — one-time hardware cost, then it runs forever
- Privacy — your viewing habits stay on your network
- Hardware transcoding — stream to any device, any format, automatically
- Automated media management — request a movie and it appears in your library, with subtitles, ready to watch on any device
- Network-wide ad blocking — DNS-level filtering for every device in your home
- Self-hosted cloud storage — replace Google Drive/iCloud with Nextcloud
- Remote access — watch your content from anywhere via Tailscale VPN - Lid close: Must -weight: 500;">disable sleep-on-lid-close (the server runs headless 24/7)
- Battery: Built-in battery acts as a free UPS (survives short power outages)
- Network: WiFi works but Ethernet is recommended for media streaming reliability
- Screen/keyboard: Useful for initial setup, then run headless via SSH - Set hostname to something memorable (e.g., mediaserver)
- Create your user account (this guide assumes UID/GID 1000)
- Connect to your network (Ethernet preferred) - Create an admin account on first visit
- Select "Local" environment - Set the admin web interface to port 8053 (avoids conflicts with other services)
- Set DNS to port 53
- Create an admin username/password
- Add filter lists (recommended: AdGuard default + Steven Black's unified hosts) - Settings -> Downloads -> Default Save Path: /downloads/
- Settings -> Downloads -> Keep incomplete in: /downloads/incomplete/
- Settings -> BitTorrent -> Seeding limits: Set max ratio to 1.0 (see Section 14 for details)
- Settings -> Web UI: Change the default password - Settings -> General -> Set authentication (Forms, username/password)
- Indexers -> Add your torrent indexers (1337x, RARBG, etc.)
- Settings -> Apps -> Add Radarr, Sonarr, and Lidarr (after deploying them below) - Settings -> General -> Set authentication
- Settings -> Media Management: Root Folder: /media/movies Enable "Rename Movies"
Enable "Use Hardlinks instead of Copy" -> YES (saves disk space when source and destination are on the same filesystem)
- Root Folder: /media/movies
- Enable "Rename Movies"
- Enable "Use Hardlinks instead of Copy" -> YES (saves disk space when source and destination are on the same filesystem)
- Settings -> Download Clients -> Add qBittorrent: Host: YOUR_IP Port: 8080 Category: radarr
- Host: YOUR_IP
- Category: radarr
- Settings -> Quality Profiles -> Select "HD-1080p" as default
- Settings -> Indexers -> (these sync automatically from Prowlarr) - Root Folder: /media/movies
- Enable "Rename Movies"
- Enable "Use Hardlinks instead of Copy" -> YES (saves disk space when source and destination are on the same filesystem) - Host: YOUR_IP
- Category: radarr - Authentication in Settings -> General
- Root Folder: /media/tv
- Download Client: qBittorrent (category: sonarr)
- Quality Profile: HD-1080p
- Enable hardlinks - Settings -> Subtitles -> Languages: Add your preferred languages
- Settings -> Providers -> Add OpenSubtitles.com (free account required)
- Settings -> Sonarr/Radarr -> Connect to both using their API keys (found in each app under Settings -> General) - Settings -> Apps -> Add: Radarr: URL http://YOUR_IP:7878, API key from Radarr -> Settings -> General Sonarr: URL http://YOUR_IP:8989, API key from Sonarr -> Settings -> General Lidarr: URL http://YOUR_IP:8686, API key from Lidarr -> Settings -> General
- Radarr: URL http://YOUR_IP:7878, API key from Radarr -> Settings -> General
- Sonarr: URL http://YOUR_IP:8989, API key from Sonarr -> Settings -> General
- Lidarr: URL http://YOUR_IP:8686, API key from Lidarr -> Settings -> General
- Click "Sync" — all indexers now appear in every *Arr app automatically - Radarr: URL http://YOUR_IP:7878, API key from Radarr -> Settings -> General
- Sonarr: URL http://YOUR_IP:8989, API key from Sonarr -> Settings -> General
- Lidarr: URL http://YOUR_IP:8686, API key from Lidarr -> Settings -> General - Create an admin account
- Add media libraries: Movies -> /data/movies Shows -> /data/tv Music -> /data/music
- Movies -> /data/movies
- Shows -> /data/tv
- Music -> /data/music
- Set preferred language and metadata language - Movies -> /data/movies
- Shows -> /data/tv
- Music -> /data/music - Dashboard -> Playback -> Transcoding
- Hardware acceleration: Intel QuickSync (QSV)
- Enable hardware decoding for: H264, HEVC, VP9, AV1 (Intel Iris Xe supports all)
- Enable Hardware encoding -> YES
- Enable Tone mapping -> YES
- Preferred hardware encoder: QSV - Play a video in the Jellyfin web player
- During playback, check if hardware transcoding is active: - Samsung Tizen TV: Install Jellyfin from the Samsung app store
- iPhone/iPad: Install "Jellyfin" from the App Store (free)
- Android: Install "Jellyfin" from the Play Store (free)
- Alternative: Infuse Pro (iOS/tvOS, paid — prettier UI, auto-discovers Jellyfin) - Install the Nextcloud app from your phone's app store
- Server: http://YOUR_IP:8088 (snap) or http://YOUR_IP:8443 (Docker) — use the Tailscale IP for remote access
- Login with admin credentials
- For photo auto-upload: Nextcloud app -> Auto Upload -> Enable - Libraries -> Add: Movies: Source /media/movies, Transcode cache /temp TV: Source /media/tv, Transcode cache /temp
- Movies: Source /media/movies, Transcode cache /temp
- TV: Source /media/tv, Transcode cache /temp
- Plugins -> Use community plugin: Tdarr_Plugin_MC93_Migz1FFMPEG Target codec: HEVC (H.265)
Use hardware: Intel QSV
- Target codec: HEVC (H.265)
- Use hardware: Intel QSV
- Schedule (recommended to prevent daytime CPU load): Node -> Schedule -> Enable, set hours like 01:00-07:00
- Node -> Schedule -> Enable, set hours like 01:00-07:00 - Movies: Source /media/movies, Transcode cache /temp
- TV: Source /media/tv, Transcode cache /temp - Target codec: HEVC (H.265)
- Use hardware: Intel QSV - Node -> Schedule -> Enable, set hours like 01:00-07:00 - Settings -> Download Clients -> Add -> qBittorrent Host: YOUR_IP Port: 8080 Username/Password: your qBittorrent credentials
Category: radarr (or sonarr, lidarr respectively)
Test -> Save
- Host: YOUR_IP
- Username/Password: your qBittorrent credentials
- Category: radarr (or sonarr, lidarr respectively)
- Test -> Save - Host: YOUR_IP
- Username/Password: your qBittorrent credentials
- Category: radarr (or sonarr, lidarr respectively)
- Test -> Save - Settings -> Sonarr -> Enable, enter URL + API key
- Settings -> Radarr -> Enable, enter URL + API key
- Settings -> Languages -> Add preferred subtitle languages
- Settings -> Providers -> Add OpenSubtitles.com - Sign in with Jellyfin -> enter URL + credentials
- Add Radarr server: URL + API key + quality profile + root folder
- Add Sonarr server: URL + API key + quality profile + root folder
- Now users can request movies/TV via http://YOUR_IP:5055 - Go to Jellyseerr -> Search for a movie -> Request it
- Check Radarr -> should show the movie as "Searching"
- Check qBittorrent -> should -weight: 500;">start downloading
- Wait for download to complete
- Check Jellyfin -> movie should appear in library
- Play the movie -> verify hardware transcoding works
- Check Bazarr -> subtitles should auto-download - GlobalMaxRatio=1 — Stop seeding after uploading 1x the file size
- GlobalMaxSeedingMinutes=1440 — Max 24 hours of seeding regardless of ratio
- GlobalMaxInactiveSeedingMinutes=1440 — Remove if no upload activity for 24 hours
- ShareLimitAction=Remove — Remove torrent from list when limits are reached - removeCompletedDownloads: true — After import, -weight: 500;">remove torrent + delete source files
- autoRedownloadFailed: true — Retry if download fails - Links to all services with their ports
- System -weight: 500;">status overview
- Quick-access to the most used services (Jellyfin, Jellyseerr, qBittorrent)
- Optionally, an AI chat widget connected to your local Ollama instance - Jellyfin streams your movies, TV shows, and music to any device with Intel QSV hardware transcoding
- The *Arr suite (Radarr, Sonarr, Lidarr, Prowlarr) automates finding, downloading, and organizing media
- Jellyseerr gives users a Netflix-like request interface
- Bazarr automatically downloads subtitles in your preferred languages
- qBittorrent handles downloads with automatic seeding limits and cleanup
- Tdarr re-encodes your library to H.265 for 40-60% space savings
- AdGuard Home blocks ads network-wide at the DNS level
- Nginx Proxy Manager provides clean URLs with SSL certificates
- Tailscale enables secure remote access from anywhere
- Nextcloud replaces cloud storage services
- Uptime Kuma monitors everything and alerts you when services go down
- Watchtower keeps all containers automatically updated
- Portainer gives you a GUI for Docker management
- Three-layer disk protection prevents the disk from filling up - Add torrent indexers in Prowlarr (:9696) for better search results
- Add subtitle providers and languages in Bazarr (:6767)
- Set up Uptime Kuma monitors for each -weight: 500;">service (:3001)
- Configure NPM proxy hosts for clean domain-based URLs (:81)
- Set up Nextcloud auto-upload on your phone for photo backup
- Consider adding a VPN (Gluetun) in front of qBittorrent for privacy
- Explore Navidrome (:4533) if you want a dedicated music streaming server with Subsonic-compatible mobile apps