homelab-ultimate-setup/advanced-compose.yml
Richard Nixon e895c533bf Migrate xxx media to dedicated NFS share and add reddit-counter
- Move Stash data mount from MEDIA_SHARE/xxx to /mnt/appdata/xxx
- Add dedicated xxx mount to Whisparr for hardlinking compatibility
- Fix AdGuard port mapping (3000 -> 80) after initial setup
- Update Homepage volumes for disk monitoring (media, backup, appdata)
- Add reddit-counter container for reddit media collection stats
2026-01-30 14:36:03 +00:00

1055 lines
34 KiB
YAML

# Welcome to my Ultimate Plex Stack! (Modernized Fork)
#
# Make sure to rename this file to "docker-compose.yaml" if you are using git clone
#
# This is a modernized version of the original ultimate-plex-stack
# Updated to use current tools and replace deprecated ones (2025/2026)
#
# Environment Variable Examples:
# PUID = 99
# GUID = 101
# TZ = America/Edmonton
# BASE_PATH = /home/username/docker
#
# https://trash-guides.info/Hardlinks/Hardlinks-and-Instant-Moves/ # This can be useful for establishing how the media will be presented below
# MEDIA_SHARE = /mnt/media # This can also be renamed to "SHARE" or "MEDIA" this is where you will present your media
#
# NOTE: This is not a plug and play solution, some research / customization will be required to make this work as intended
# Feel free to customize ie: remove/change/add containers as needed - one size does not fit all
---
version: "3.0"
networks:
proxy:
driver: bridge
services:
# ============================================
# DNS & AD BLOCKING
# ============================================
#AdGuard Home - Network-wide DNS with ad blocking
#
#First run: Access http://SERVER_IP:3001 to complete setup
#After setup, configure your router/devices to use SERVER_IP as DNS
adguard:
image: adguard/adguardhome:latest
container_name: adguard
hostname: adguard
ports:
- 53:53/tcp
- 53:53/udp
- 3001:80/tcp # Admin UI
- 8443:443/tcp # HTTPS admin
- 853:853/tcp # DNS-over-TLS
volumes:
- ${BASE_PATH}/adguard/work:/opt/adguardhome/work
- ${BASE_PATH}/adguard/conf:/opt/adguardhome/conf
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.adguard.rule=Host(`dns.homelab`)"
- "traefik.http.routers.adguard.entrypoints=websecure"
- "traefik.http.routers.adguard.tls=true"
- "traefik.http.services.adguard.loadbalancer.server.port=80"
restart: unless-stopped
# ============================================
# REVERSE PROXY
# ============================================
#Traefik - Reverse proxy for friendly URLs
traefik:
image: traefik:latest
container_name: traefik
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--providers.file.filename=/etc/traefik/dynamic.yml"
ports:
- "80:80"
- "443:443"
- "8888:8080" # Traefik dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${BASE_PATH}/traefik:/etc/traefik
networks:
- proxy
restart: unless-stopped
#Plex - used to display the media
#
#This can also be replaced by Emby/Jellyfin
plex:
image: lscr.io/linuxserver/plex:latest
container_name: plex
network_mode: host
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
- VERSION=docker
- PLEX_CLAIM=${PLEX_CLAIM}
ports:
- 32400:32400
devices:
- /dev/dri:/dev/dri #Required for plex HW transcoding / QuickSync
volumes:
- ${BASE_PATH}/plex/config:/config
- ${MEDIA_SHARE}/tv:/tv
- ${MEDIA_SHARE}/movies:/movies
- ${MEDIA_SHARE}/music:/music
- ${MEDIA_SHARE}/concerts:/concerts
restart: unless-stopped
#Radarr - used to find movies automatically
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- /opt/docker-configs/radarr/config:/config
- ${MEDIA_SHARE}:/share #Access to the entire share
networks:
- proxy
ports:
- 7878:7878
labels:
- "traefik.enable=true"
- "traefik.http.routers.radarr.rule=Host(`radarr.homelab`)"
- "traefik.http.routers.radarr.entrypoints=websecure"
- "traefik.http.routers.radarr.tls=true"
- "traefik.http.services.radarr.loadbalancer.server.port=7878"
restart: unless-stopped
#Sonarr - used to find tv shows automatically
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- /opt/docker-configs/sonarr/config:/config
- ${MEDIA_SHARE}:/share #Access to the entire share
networks:
- proxy
ports:
- 8989:8989
labels:
- "traefik.enable=true"
- "traefik.http.routers.sonarr.rule=Host(`sonarr.homelab`)"
- "traefik.http.routers.sonarr.entrypoints=websecure"
- "traefik.http.routers.sonarr.tls=true"
- "traefik.http.services.sonarr.loadbalancer.server.port=8989"
restart: unless-stopped
#Readarr - Used to download books
#
#NOTE: DISABLED - LinuxServer image deprecated and no amd64 support available
#TODO: Find alternative or wait for upstream fix
#readarr:
# image: linuxserver/readarr:develop
# container_name: readarr
# environment:
# - PUID=${PUID}
# - PGID=${GUID}
# - TZ=${TZ}
# volumes:
# - ${BASE_PATH}/readarr/config:/config
# - ${MEDIA_SHARE}:/share
# ports:
# - 8787:8787
# restart: unless-stopped
#Lidarr - Used to download music
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- /opt/docker-configs/lidarr/config:/config
- ${MEDIA_SHARE}:/share
networks:
- proxy
ports:
- 8686:8686
labels:
- "traefik.enable=true"
- "traefik.http.routers.lidarr.rule=Host(`lidarr.homelab`)"
- "traefik.http.routers.lidarr.entrypoints=websecure"
- "traefik.http.routers.lidarr.tls=true"
- "traefik.http.services.lidarr.loadbalancer.server.port=8686"
restart: unless-stopped
#Prowlarr - manages your Sonarr, Radarr and download client
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- /opt/docker-configs/prowlarr/config:/config
networks:
- proxy
ports:
- 9696:9696
labels:
- "traefik.enable=true"
- "traefik.http.routers.prowlarr.rule=Host(`prowlarr.homelab`)"
- "traefik.http.routers.prowlarr.entrypoints=websecure"
- "traefik.http.routers.prowlarr.tls=true"
- "traefik.http.services.prowlarr.loadbalancer.server.port=9696"
restart: unless-stopped
#Autobrr - used to grab torrents using the trackers IRC channel - Increases seeding due to grabbing content before RSS feed
autobrr:
container_name: autobrr
image: ghcr.io/autobrr/autobrr:latest
restart: unless-stopped
environment:
- TZ=${TZ}
- PUID=${PUID}
- PGID=${GUID}
volumes:
- ${BASE_PATH}/autobrr/config:/config
ports:
- 7474:7474
#Seerr - allows users to request media on their own
#
#UPDATED: Replaces Overseerr. Seerr is the unified successor to Overseerr and Jellyseerr
#Supports Plex, Jellyfin, and Emby
#Note: Using develop tag until stable release is available
#Migration guide: https://docs.seerr.dev/
seerr:
image: ghcr.io/seerr-team/seerr:develop
container_name: seerr
environment:
- TZ=${TZ}
volumes:
- ${BASE_PATH}/seerr/config:/app/config
- ${MEDIA_SHARE}:/share # Access to validate root folders
networks:
- proxy
ports:
- 5055:5055
labels:
- "traefik.enable=true"
- "traefik.http.routers.seerr.rule=Host(`pedirfilmes.homelab`)"
- "traefik.http.routers.seerr.entrypoints=websecure"
- "traefik.http.routers.seerr.tls=true"
- "traefik.http.services.seerr.loadbalancer.server.port=5055"
restart: unless-stopped
#Byparr - Used as a proxy server to bypass Cloudflare and DDoS-GUARD protection
#
#UPDATED: Replaces Flaresolverr. Byparr is a modern drop-in replacement using Camoufox
#Uses the same API as Flaresolverr, so no changes needed in Prowlarr config
#https://github.com/ThePhaseless/Byparr
byparr:
image: ghcr.io/thephaseless/byparr:latest
container_name: byparr
init: true # Reaps zombie processes from Camoufox/Firefox
environment:
- LOG_LEVEL=info
- TZ=${TZ}
ports:
- 8191:8191
restart: unless-stopped
#Qbittorrent - torrenting software
#
#All traffic routed through Gluetun VPN container
#Ports are defined on the gluetun container since qBittorrent uses its network
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun"
depends_on:
- gluetun
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
- WEBUI_PORT=8080
- TORRENTING_PORT=8694
volumes:
- ${BASE_PATH}/qbittorrent/config:/config
- ${MEDIA_SHARE}:/share
restart: unless-stopped
#Tautulli - for plex statistics. Very useful when troubleshooting performance issues
tautulli:
image: lscr.io/linuxserver/tautulli:latest
container_name: tautulli
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- ${BASE_PATH}/tautulli:/config
networks:
- proxy
ports:
- 8181:8181
labels:
- "traefik.enable=true"
- "traefik.http.routers.tautulli.rule=Host(`tautulli.homelab`)"
- "traefik.http.routers.tautulli.entrypoints=websecure"
- "traefik.http.routers.tautulli.tls=true"
- "traefik.http.services.tautulli.loadbalancer.server.port=8181"
restart: unless-stopped
#Tdarr - to transcode videos from one format to another like x265 or H.265
#
#This container requires a decent amount of horse power to run but will save space in the long run
#Alternative open-source option: Unmanic (https://github.com/Unmanic/unmanic)
tdarr:
container_name: tdarr
image: ghcr.io/haveagitgat/tdarr:latest
restart: unless-stopped
ports:
- 8265:8265 # webUI port
- 8266:8266 # server port
environment:
- TZ=${TZ}
- PUID=${PUID}
- PGID=${GUID}
- UMASK_SET=002
- nodeName=ServerNode
- serverIP=0.0.0.0
- serverPort=8266
- webUIPort=8265
- internalNode=true
- inContainer=true
- ffmpegVersion=6
volumes:
- ${BASE_PATH}/tdarr/server:/app/server
- ${BASE_PATH}/tdarr/configs:/app/configs
- ${BASE_PATH}/tdarr/logs:/app/logs
- ${MEDIA_SHARE}:/media
- /transcode_cache:/temp
devices:
- /dev/dri:/dev/dri #Required for HW transcoding / QuickSync
#Membarr - to invite users via discord
#
#DISABLED - Using Wizarr instead
#membarr:
# container_name: membarr
# image: yoruio/membarr:latest
# restart: unless-stopped
# environment:
# - token=${MEMBARR_TOKEN}
# volumes:
# - ${BASE_PATH}/membarr/config:/app/app/config
#Bazarr - for subtitles. Try to use SRT format if you can rather than PGS due to performance issues
bazarr:
container_name: bazarr
image: lscr.io/linuxserver/bazarr:latest
restart: unless-stopped
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- /opt/docker-configs/bazarr/config:/config
- ${MEDIA_SHARE}:/share
networks:
- proxy
ports:
- 6767:6767
labels:
- "traefik.enable=true"
- "traefik.http.routers.bazarr.rule=Host(`legendas.homelab`)"
- "traefik.http.routers.bazarr.entrypoints=websecure"
- "traefik.http.routers.bazarr.tls=true"
- "traefik.http.services.bazarr.loadbalancer.server.port=6767"
#Plex Auto Languages - This switches languages automatically example: watching english show and non english speaks you get subtitle
#
#UPDATED: Using JourneyOver fork which is actively maintained
#Alternative: thesammykins TypeScript rewrite (https://github.com/thesammykins/plex-auto-languages)
plexautolanguages:
image: journeyover/plex-auto-languages:latest
container_name: plex-auto-languages
environment:
- PLEX_URL=${PLEX_URL} #This is your local URL example: http://192.168.1.10:32400
#To find your plex token go to https://app.plex.tv/, go to your library, click on the 3 dots on the bottom right of one of your tv/movie posters
#Then click "View XML" in the bottom right of that popup, look at the URL of the XML window and find your X-Plex-Token= in the URL (at the very end)
- PLEX_TOKEN=${PLEX_TOKEN}
- TZ=${TZ}
volumes:
- ${BASE_PATH}/pal/config:/config
restart: unless-stopped
#Cross Seed - used to take torrents from one tracker and seed them on another without leeching first. Increases ratio
#
#DISABLED - Requires advanced configuration
#cross-seed:
# image: ghcr.io/cross-seed/cross-seed:6
# container_name: cross-seed
# environment:
# - PUID=${PUID}
# - PGID=${GUID}
# ports:
# - "2468:2468"
# volumes:
# - ${BASE_PATH}/cross-seed/config:/config
# - ${BASE_PATH}/qbittorrent/config/qBittorrent/BT_backup:/torrents:ro
# - ${MEDIA_SHARE}/cross-seed/current-cross-seeds:/cross-seeds
# command: daemon
# restart: unless-stopped
#Kometa - used to create collections in plex. Example: "Most Popular Movies This Week", "Best of horror", etc.
#
#UPDATED: Replaces Plex Meta Manager which was deprecated in April 2024
#Migration guide: https://kometa.wiki/en/latest/kometa/guides/rebrand/
kometa:
image: kometateam/kometa:latest
container_name: kometa
environment:
- TZ=${TZ}
#- KOMETA_OVERLAYS_ONLY=true #Tells Kometa to run overlays only
- KOMETA_CONFIG=/config/config.yml
- KOMETA_TIME=03:00 #Runs daily at 3 AM
#- KOMETA_RUN=true #Runs Kometa immediately (use for one-off manual runs)
#- KOMETA_RUN_LIBRARIES=Movies #Tells Kometa to process only a library called "Movies"
volumes:
- ${BASE_PATH}/kometa/config:/config
restart: unless-stopped
#Wizarr - Allows you to create a share link that you can send to users to invite them to your media server
#Supports Plex, Jellyfin, Emby, Audiobookshelf, Romm, Komga and Kavita
wizarr:
container_name: wizarr
image: ghcr.io/wizarrrr/wizarr:latest
networks:
- proxy
ports:
- 5690:5690
volumes:
- ${BASE_PATH}/wizarr/data/database:/data/database
labels:
- "traefik.enable=true"
- "traefik.http.routers.wizarr.rule=Host(`convites.homelab`)"
- "traefik.http.routers.wizarr.entrypoints=websecure"
- "traefik.http.routers.wizarr.tls=true"
- "traefik.http.services.wizarr.loadbalancer.server.port=5690"
restart: unless-stopped
#Dozzle - Used to easily view logs of any container in real time!
#For historical logs/search, consider Loki + Grafana
dozzle:
container_name: dozzle
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- proxy
ports:
- 9999:8080
labels:
- "traefik.enable=true"
- "traefik.http.routers.dozzle.rule=Host(`logs.homelab`)"
- "traefik.http.routers.dozzle.entrypoints=websecure"
- "traefik.http.routers.dozzle.tls=true"
- "traefik.http.services.dozzle.loadbalancer.server.port=8080"
restart: unless-stopped
#Unpackerr - Used to unzip zipped files
unpackerr:
image: golift/unpackerr:latest
container_name: unpackerr
volumes:
# You need at least this one volume mapped so Unpackerr can find your files to extract.
# Make sure this matches your Starr apps; the folder mount (/downloads or /data) should be identical.
- ${MEDIA_SHARE}:/share
#- ${BASE_PATH}/unpackerr/config:/config
restart: unless-stopped
user: 1000:1000 #Needs to run as 1000
# What you see below are defaults for this compose. You only need to modify things specific to your environment.
# Remove apps and feature configs you do not use or need.
# ie. Remove all lines that begin with UN_CMDHOOK, UN_WEBHOOK, UN_FOLDER, UN_WEBSERVER, and other apps you do not use.
environment:
- UN_START_DELAY=1m
#- UMASK=002
- TZ=${TZ}
#- UN_DEBUG=true
# Sonarr Config
- UN_SONARR_0_URL=http://${SERVER_IP}:8989
- UN_SONARR_0_API_KEY=${SONARR_KEY}
#- UN_SONARR_0_PATHS_0=/share/downloads/tv
- UN_SONARR_0_TIMEOUT=10s
#- UN_SONARR_0_PATHS_0=/share/downloads/tv
# Radarr Config
- UN_RADARR_0_URL=http://${SERVER_IP}:7878
- UN_RADARR_0_API_KEY=${RADARR_KEY}
#- UN_RADARR_0_PATHS_0=/share/downloads/movies
- UN_RADARR_0_TIMEOUT=10s
#- UN_RADARR_0_PATHS_0=/share/downloads/movies
# Recyclarr - Used to sync Trash Guides config's to radarr and sonarr
# Only accessible via command line! No GUI
# Supports Radarr and Sonarr v4+
recyclarr:
image: ghcr.io/recyclarr/recyclarr:latest
container_name: recyclarr
user: ${PUID}:${GUID}
volumes:
- ${BASE_PATH}/recyclarr/config:/config
environment:
- TZ=${TZ}
restart: unless-stopped
# ============================================
# MEDIA DOWNLOADS
# ============================================
#MeTube - Web UI for yt-dlp (YouTube and 1000+ sites)
#
#Browser extensions available for one-click downloads
#https://github.com/alexta69/metube
metube:
image: ghcr.io/alexta69/metube:latest
container_name: metube
environment:
- UID=${PUID}
- GID=${GUID}
- TZ=${TZ}
volumes:
- ${MEDIA_SHARE}/downloads:/downloads
networks:
- proxy
ports:
- 8081:8081
labels:
- "traefik.enable=true"
- "traefik.http.routers.metube.rule=Host(`metube.homelab`)"
- "traefik.http.routers.metube.entrypoints=websecure"
- "traefik.http.routers.metube.tls=true"
- "traefik.http.services.metube.loadbalancer.server.port=8081"
restart: unless-stopped
# ============================================
# DASHBOARD
# ============================================
#Homepage - Modern dashboard for all your services
#
#https://gethomepage.dev/
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
group_add:
- "999" # docker group GID for socket access
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
- HOMEPAGE_ALLOWED_HOSTS=10.10.11.201:3000,localhost:3000,homelab:3000,dashboard.homelab
volumes:
- ${BASE_PATH}/homepage/config:/app/config
- /var/run/docker.sock:/var/run/docker.sock:ro
- /mnt/truenas/movies:/mnt/truenas/movies:ro
- /mnt/truenas/backup-appdata:/mnt/truenas/backup-appdata:ro
- /mnt/appdata/xxx:/mnt/appdata/xxx:ro
networks:
- proxy
ports:
- 3000:3000
labels:
- "traefik.enable=true"
- "traefik.http.routers.homepage.rule=Host(`dashboard.homelab`)"
- "traefik.http.routers.homepage.entrypoints=websecure"
- "traefik.http.routers.homepage.tls=true"
- "traefik.http.services.homepage.loadbalancer.server.port=3000"
restart: unless-stopped
# ============================================
# SOURCE CODE MANAGEMENT
# ============================================
#Forgejo - Self-hosted Git service (lightweight Gitea fork)
#
#Community-driven fork focused on sustainability and independence
#First run: Access http://SERVER_IP:4000 to complete setup
#SSH access available on port 2222
#https://forgejo.org/
forgejo:
image: codeberg.org/forgejo/forgejo:13
container_name: forgejo
environment:
- USER_UID=${PUID}
- USER_GID=${GUID}
- TZ=${TZ}
- FORGEJO__database__DB_TYPE=sqlite3
- FORGEJO__server__ROOT_URL=http://${SERVER_IP}:4000/
- FORGEJO__server__SSH_PORT=2222
- FORGEJO__server__SSH_LISTEN_PORT=22
volumes:
- ${BASE_PATH}/forgejo/data:/data
networks:
- proxy
ports:
- 4000:3000 # Web UI
- 2222:22 # SSH for Git
labels:
- "traefik.enable=true"
- "traefik.http.routers.forgejo.rule=Host(`git.homelab`)"
- "traefik.http.routers.forgejo.entrypoints=websecure"
- "traefik.http.routers.forgejo.tls=true"
- "traefik.http.services.forgejo.loadbalancer.server.port=3000"
restart: unless-stopped
# ============================================
# ADULT CONTENT MANAGEMENT
# ============================================
#Whisparr - Automated adult content management (part of the Servarr family)
#
#Works like Radarr/Sonarr but for adult content
#Integrates with Prowlarr for indexers
#https://wiki.servarr.com/whisparr
whisparr:
image: ghcr.io/hotio/whisparr:latest
container_name: whisparr
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
volumes:
- /opt/docker-configs/whisparr/config:/config
- ${MEDIA_SHARE}:/share # Full share access for hardlinking (downloads)
- /mnt/appdata/xxx:/share/xxx # Dedicated xxx share mapped to same internal path
networks:
- proxy
ports:
- 6969:6969
labels:
- "traefik.enable=true"
- "traefik.http.routers.whisparr.rule=Host(`whisparr.homelab`)"
- "traefik.http.routers.whisparr.entrypoints=websecure"
- "traefik.http.routers.whisparr.tls=true"
- "traefik.http.services.whisparr.loadbalancer.server.port=6969"
restart: unless-stopped
#Stash - Adult media organizer with metadata scraping
#
#Features: metadata scraping, scene detection, performer identification,
#tagging system, duplicate detection, web-based streaming
#https://stashapp.cc/
stash:
image: stashapp/stash:latest
container_name: stash
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
- STASH_STASH=/data/
- STASH_GENERATED=/generated/
- STASH_METADATA=/metadata/
- STASH_CACHE=/cache/
volumes:
- /opt/docker-configs/stash/config:/root/.stash
- /opt/docker-configs/stash/generated:/generated
- /opt/docker-configs/stash/metadata:/metadata
- /opt/docker-configs/stash/cache:/cache
- /opt/docker-configs/stash/blobs:/blobs
- /mnt/appdata/xxx:/data # Adult content library (dedicated NFS share)
devices:
- /dev/dri:/dev/dri # Hardware transcoding (VAAPI)
networks:
- proxy
ports:
- 9998:9999
labels:
- "traefik.enable=true"
- "traefik.http.routers.stash.rule=Host(`stash.homelab`)"
- "traefik.http.routers.stash.entrypoints=websecure"
- "traefik.http.routers.stash.tls=true"
- "traefik.http.services.stash.loadbalancer.server.port=9999"
restart: unless-stopped
# ============================================
# SECURITY & AUTHENTICATION
# ============================================
#Docker Socket Proxy - Secure proxy for Docker socket access
#
#Prevents containers from getting root-equivalent access to the host
#Traefik and Dozzle connect to this instead of the raw socket
#https://github.com/Tecnativa/docker-socket-proxy
docker-socket-proxy:
image: tecnativa/docker-socket-proxy:latest
container_name: docker-socket-proxy
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- NETWORKS=1
- IMAGES=1
- INFO=1
- EVENTS=1
- PING=1
- VERSION=1
- POST=0 # Read-only
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
ports:
- 2375:2375
privileged: true
restart: unless-stopped
#Authelia - Single Sign-On and 2FA portal
#
#Protects all services behind Traefik with a unified login page
#Supports TOTP, WebAuthn/FIDO2, and Duo push
#https://www.authelia.com/
authelia:
image: authelia/authelia:4.37
container_name: authelia
environment:
- TZ=${TZ}
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
volumes:
- ${BASE_PATH}/authelia/config:/config
networks:
- proxy
ports:
- 9091:9091
labels:
- "traefik.enable=true"
- "traefik.http.routers.authelia.rule=Host(`auth.homelab`)"
- "traefik.http.routers.authelia.entrypoints=websecure"
- "traefik.http.routers.authelia.tls=true"
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
restart: unless-stopped
#Vaultwarden - Lightweight Bitwarden-compatible password manager
#
#Use official Bitwarden clients (browser, mobile, desktop)
#Requires HTTPS for browser extensions (use via Traefik)
#https://github.com/dani-garcia/vaultwarden
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
environment:
- TZ=${TZ}
- DOMAIN=https://vault.homelab
- SIGNUPS_ALLOWED=true
volumes:
- ${BASE_PATH}/vaultwarden/data:/data
networks:
- proxy
ports:
- 8222:80
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultwarden.rule=Host(`vault.homelab`)"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.routers.vaultwarden.tls=true"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
restart: unless-stopped
# ============================================
# VPN & NETWORKING
# ============================================
#Gluetun - VPN container for routing traffic through WireGuard/OpenVPN
#
#qBittorrent routes all traffic through this container
#Built-in kill switch prevents leaks
#https://github.com/qdm12/gluetun
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
- VPN_TYPE=${VPN_TYPE}
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
- SERVER_COUNTRIES=Ireland
- TZ=${TZ}
- FIREWALL_OUTBOUND_SUBNETS=10.10.11.0/24,172.16.0.0/12
- HTTPPROXY=off
- SHADOWSOCKS=off
- DOT=off
networks:
- proxy
ports:
- 8000:8000 # Gluetun control server
- 8080:8080 # qBittorrent WebUI
- 8694:8694 # qBittorrent torrenting
- 8694:8694/udp
labels:
- "traefik.enable=true"
- "traefik.http.routers.qbittorrent.rule=Host(`downloads.homelab`)"
- "traefik.http.routers.qbittorrent.entrypoints=websecure"
- "traefik.http.routers.qbittorrent.tls=true"
- "traefik.http.services.qbittorrent.loadbalancer.server.port=8080"
restart: unless-stopped
#WireGuard Easy - VPN server with web UI
#
#Secure remote access to your homelab from anywhere
#QR codes for easy mobile setup
#https://github.com/wg-easy/wg-easy
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest
container_name: wg-easy
environment:
- LANGUAGE=en
- WG_HOST=${SERVER_IP}
- PASSWORD_HASH=$$2a$$12$$placeholder
- WG_DEFAULT_DNS=10.10.11.201
volumes:
- ${BASE_PATH}/wg-easy/config:/etc/wireguard
networks:
- proxy
ports:
- 51820:51820/udp
- 51821:51821/tcp
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
labels:
- "traefik.enable=true"
- "traefik.http.routers.wg-easy.rule=Host(`vpn.homelab`)"
- "traefik.http.routers.wg-easy.entrypoints=websecure"
- "traefik.http.routers.wg-easy.tls=true"
- "traefik.http.services.wg-easy.loadbalancer.server.port=51821"
restart: unless-stopped
# ============================================
# MONITORING & MAINTENANCE
# ============================================
#Gotify - Self-hosted push notification server
#
#Unified notification endpoint for all services
#Android app available for push notifications
#https://gotify.net/
gotify:
image: gotify/server:latest
container_name: gotify
environment:
- TZ=${TZ}
volumes:
- ${BASE_PATH}/gotify/data:/app/data
networks:
- proxy
ports:
- 8083:80
labels:
- "traefik.enable=true"
- "traefik.http.routers.gotify.rule=Host(`notificacoes.homelab`)"
- "traefik.http.routers.gotify.entrypoints=websecure"
- "traefik.http.routers.gotify.tls=true"
- "traefik.http.services.gotify.loadbalancer.server.port=80"
restart: unless-stopped
#Maintainerr - Automated Plex media cleanup
#
#Creates rules to remove unwatched content with grace periods
#Shows "Leaving Soon" collection on Plex
#https://github.com/jorenn92/Maintainerr
maintainerr:
image: ghcr.io/jorenn92/maintainerr:latest
container_name: maintainerr
environment:
- TZ=${TZ}
volumes:
- ${BASE_PATH}/maintainerr/data:/opt/data
networks:
- proxy
ports:
- 6246:6246
labels:
- "traefik.enable=true"
- "traefik.http.routers.maintainerr.rule=Host(`maintainerr.homelab`)"
- "traefik.http.routers.maintainerr.entrypoints=websecure"
- "traefik.http.routers.maintainerr.tls=true"
- "traefik.http.services.maintainerr.loadbalancer.server.port=6246"
restart: unless-stopped
#Speedtest Tracker - Automated internet speed testing with history
#
#Scheduled tests with graphs and trend analysis
#https://github.com/alexjustesen/speedtest-tracker
speedtest-tracker:
image: lscr.io/linuxserver/speedtest-tracker:latest
container_name: speedtest-tracker
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
- DB_CONNECTION=sqlite
- APP_KEY=base64:V1NVcVVIVmk5cDVnelUwMzNOQjVRM1R2NEZYSmpGZGg=
volumes:
- ${BASE_PATH}/speedtest-tracker/config:/config
networks:
- proxy
ports:
- 8084:80
labels:
- "traefik.enable=true"
- "traefik.http.routers.speedtest.rule=Host(`speedtest.homelab`)"
- "traefik.http.routers.speedtest.entrypoints=websecure"
- "traefik.http.routers.speedtest.tls=true"
- "traefik.http.services.speedtest.loadbalancer.server.port=80"
restart: unless-stopped
# ============================================
# PRODUCTIVITY & TOOLS
# ============================================
#IT-Tools - Collection of 100+ developer utilities
#
#JSON/YAML converters, hash generators, Base64, JWT decoder, etc.
#https://github.com/CorentinTh/it-tools
it-tools:
image: corentinth/it-tools:latest
container_name: it-tools
networks:
- proxy
ports:
- 8085:80
labels:
- "traefik.enable=true"
- "traefik.http.routers.it-tools.rule=Host(`tools.homelab`)"
- "traefik.http.routers.it-tools.entrypoints=websecure"
- "traefik.http.routers.it-tools.tls=true"
- "traefik.http.services.it-tools.loadbalancer.server.port=80"
restart: unless-stopped
#Mealie - Recipe manager and meal planner
#
#Import recipes by URL, strips ads and blog posts
#Shopping lists and household management
#https://mealie.io/
mealie:
image: ghcr.io/mealie-recipes/mealie:latest
container_name: mealie
environment:
- PUID=${PUID}
- PGID=${GUID}
- TZ=${TZ}
- ALLOW_SIGNUP=false
- MAX_WORKERS=1
- WEB_CONCURRENCY=1
- BASE_URL=http://${SERVER_IP}:9925
volumes:
- ${BASE_PATH}/mealie/data:/app/data
networks:
- proxy
ports:
- 9925:9000
labels:
- "traefik.enable=true"
- "traefik.http.routers.mealie.rule=Host(`receitas.homelab`)"
- "traefik.http.routers.mealie.entrypoints=websecure"
- "traefik.http.routers.mealie.tls=true"
- "traefik.http.services.mealie.loadbalancer.server.port=9000"
restart: unless-stopped
# ============================================
# BACKUP & RECOVERY
# ============================================
#Backrest - Web UI for restic backups
#
#Backs up all homelab configs (NFS + local SQLite DBs) to TrueNAS
#First run: Access https://backups.homelab to create admin account
#https://github.com/garethgeorge/backrest
backrest:
image: ghcr.io/garethgeorge/backrest:latest
container_name: backrest
environment:
- TZ=${TZ}
- BACKREST_DATA=/data
- BACKREST_CONFIG=/config/config.json
- XDG_CACHE_HOME=/cache
volumes:
- ${BASE_PATH}/backrest/data:/data
- ${BASE_PATH}/backrest/config:/config
- ${BASE_PATH}/backrest/cache:/cache
- /mnt/truenas/config:/sources/truenas-config:ro
- /opt/docker-configs:/sources/local-configs:ro
- ${DOCKER_PATH}:/sources/docker-persistent:ro
- /mnt/truenas/backup-appdata:/repos/truenas-backups
networks:
- proxy
ports:
- 9898:9898
labels:
- "traefik.enable=true"
- "traefik.http.routers.backrest.rule=Host(`backups.homelab`)"
- "traefik.http.routers.backrest.entrypoints=websecure"
- "traefik.http.routers.backrest.tls=true"
- "traefik.http.services.backrest.loadbalancer.server.port=9898"
restart: unless-stopped
#Reddit Counter - Counts media files in reddit collection
reddit-counter:
image: python:3-alpine
container_name: reddit-counter
command: python /app/serve.py
volumes:
- /opt/docker-configs/reddit-counter/serve.py:/app/serve.py:ro
- /mnt/appdata/xxx:/data:ro
ports:
- 8086:8080
restart: unless-stopped
#Actual Budget - Personal finance and budgeting (YNAB alternative)
#
#Local-first architecture, works offline, syncs across devices
#https://actualbudget.org/
actual-budget:
image: actualbudget/actual-server:latest
container_name: actual-budget
environment:
- TZ=${TZ}
volumes:
- ${BASE_PATH}/actual-budget/data:/data
networks:
- proxy
ports:
- 5006:5006
labels:
- "traefik.enable=true"
- "traefik.http.routers.actual.rule=Host(`orcamento.homelab`)"
- "traefik.http.routers.actual.entrypoints=websecure"
- "traefik.http.routers.actual.tls=true"
- "traefik.http.services.actual.loadbalancer.server.port=5006"
restart: unless-stopped