Refactor docker-compose structure to folder-based organization

- Remove redundant .yml files from main docker-compose folder
- Update deploy script to use folder-based structure for all stacks
- Update documentation to reflect new folder-based organization
- Standardize all stacks to use docker-compose.yml in individual folders

This eliminates confusion between file-based and folder-based structures,
making the repository more maintainable and consistent.
This commit is contained in:
EZ-Homelab
2026-01-22 16:44:44 -05:00
parent 535ae5017b
commit 89760895f2
13 changed files with 51 additions and 1677 deletions

View File

@@ -0,0 +1,23 @@
Install script testing results
Installed debian to start from scratch.
Install Nvidia driver failed in setup-homelab.scratch
Attempted to follow manual directions, manual directions missing how to generate the authelia secrets
script didn't generate
AUTHELIA_JWT_SECRET=generate-with-openssl-rand-hex-64
AUTHELIA_SESSION_SECRET=generate-with-openssl-rand-hex-64
AUTHELIA_STORAGE_ENCRYPTION_KEY=generate-with-openssl-rand-hex-64
in setup-homelab.sh
commented out the apt install nvidia lines
script completed successfully, but still missing authelia secrets
ran deploy script, it completed successfully as well.
however missing authelia secrets prevents proxy access
Fix setup and deploy scripts to ensure an effortless install from scratch for the user experience.
Confirm they work as intended, and services are available by proxy url.

View File

@@ -1,15 +1,22 @@
# Docker Compose Stacks
This directory contains Docker Compose files for managing your homelab services. Each file is organized by functional area to maintain clarity and organization.
This directory contains Docker Compose files for managing your homelab services. Each stack is organized in its own folder for better organization and maintainability.
## Structure
```
docker-compose/
├── infrastructure.yml # Core services (reverse proxy, DNS, etc.)
├── media.yml # Media server services (Plex, Jellyfin, etc.)
├── monitoring.yml # Observability stack (Prometheus, Grafana, etc.)
├── development.yml # Development tools and services
├── core/ # Core infrastructure (Traefik, Authelia, DuckDNS)
├── infrastructure/ # Additional infrastructure (Pi-hole, Dockge, etc.)
├── dashboards/ # Dashboard services (Homepage, Homarr)
├── vpn/ # VPN services (Gluetun, qBittorrent)
├── media/ # Media services (Plex, Jellyfin, etc.)
├── monitoring/ # Observability stack (Prometheus, Grafana, etc.)
├── alternatives/ # Alternative services (Authentik, etc.)
├── homeassistant/ # Home Assistant stack
├── nextcloud/ # Nextcloud stack
├── productivity/ # Productivity tools
├── utilities/ # Utility services
└── README.md # This file
```
@@ -17,31 +24,26 @@ docker-compose/
### Starting Services
Start all services in a compose file:
Start all services in a stack:
```bash
docker compose -f docker-compose/infrastructure.yml up -d
cd docker-compose/core && docker compose up -d
```
Start a specific service:
```bash
docker compose -f docker-compose/media.yml up -d plex
```
Start multiple compose files together:
```bash
docker compose -f docker-compose/infrastructure.yml -f docker-compose/media.yml up -d
cd docker-compose/vpn && docker compose up -d gluetun
```
### Stopping Services
Stop all services in a compose file:
Stop all services in a stack:
```bash
docker compose -f docker-compose/infrastructure.yml down
cd docker-compose/core && docker compose down
```
Stop a specific service:
```bash
docker compose -f docker-compose/media.yml stop plex
cd docker-compose/vpn && docker compose stop qbittorrent
```
### Viewing Status

View File

@@ -1,137 +0,0 @@
# Core Infrastructure Stack
# Essential services required for the homelab to function
# Deploy this stack FIRST before any other services
# Place in /opt/stacks/core/docker-compose.yml
services:
# DuckDNS - Dynamic DNS updater
# Updates your public IP automatically for Let's Encrypt SSL
duckdns:
image: lscr.io/linuxserver/duckdns:latest
container_name: duckdns
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- SUBDOMAINS=${DUCKDNS_SUBDOMAINS} # Your subdomain(s), comma separated
- TOKEN=${DUCKDNS_TOKEN} # Your DuckDNS token
- UPDATE_IP=ipv4 # or ipv6, or both
volumes:
- /opt/stacks/core/duckdns:/config
labels:
- "homelab.category=infrastructure"
- "homelab.description=Dynamic DNS updater"
# Traefik - Reverse proxy with automatic SSL
# Routes all traffic and manages Let's Encrypt certificates
traefik:
image: traefik:v2.11
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-network
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "8080:8080" # Dashboard (protected with Authelia)
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /opt/stacks/core/traefik/traefik.yml:/traefik.yml:ro
- /opt/stacks/core/traefik/dynamic:/dynamic:ro
- /opt/stacks/core/traefik/acme.json:/acme.json
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # If using Cloudflare DNS challenge
- DUCKDNS_TOKEN=${DUCKDNS_TOKEN} # If using DuckDNS
labels:
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik.middlewares=authelia@docker"
- "traefik.http.routers.traefik.service=api@internal"
# Global HTTP to HTTPS redirect
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
depends_on:
- duckdns
# Authelia - SSO authentication
# Protects all admin services with single sign-on
authelia:
image: authelia/authelia:4.37
container_name: authelia
restart: unless-stopped
networks:
- traefik-network
volumes:
- /opt/stacks/core/authelia/configuration.yml:/config/configuration.yml:ro
- /opt/stacks/core/authelia/users_database.yml:/config/users_database.yml
- authelia-data:/config
environment:
- TZ=${TZ}
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
- AUTHELIA_NOTIFIER_SMTP_PASSWORD=${SMTP_PASSWORD} # If using email notifications
labels:
- "traefik.enable=true"
- "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN}`)"
- "traefik.http.routers.authelia.entrypoints=websecure"
- "traefik.http.routers.authelia.tls.certresolver=letsencrypt"
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
# Authelia middleware for other services
- "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.${DOMAIN}"
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"
depends_on:
- traefik
# Gluetun - VPN client (Surfshark WireGuard)
# Routes download clients through VPN for security
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
networks:
- homelab-network
- traefik-network
ports:
- "8888:8888/tcp" # HTTP proxy
- "8388:8388/tcp" # Shadowsocks
- "8388:8388/udp" # Shadowsocks
- "8081:8080" # qBittorrent web UI (mapped to 8081 to avoid Traefik conflict)
- "6881:6881" # qBittorrent
- "6881:6881/udp" # qBittorrent
volumes:
- /opt/stacks/core/gluetun:/gluetun
environment:
- VPN_SERVICE_PROVIDER=surfshark
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${SURFSHARK_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${SURFSHARK_ADDRESSES}
- SERVER_COUNTRIES=${VPN_SERVER_COUNTRIES:-Netherlands}
- TZ=${TZ}
labels:
- "homelab.category=infrastructure"
- "homelab.description=VPN client for secure downloads"
volumes:
authelia-data:
driver: local
networks:
traefik-network:
external: true
homelab-network:
external: true

View File

@@ -1,91 +0,0 @@
# Dashboard Services
# Homepage and Homarr for homelab dashboards
# Place in /opt/stacks/dashboards/docker-compose.yml
# Service Access URLs:
# - Homepage: https://home.${DOMAIN}
# - Homarr: https://homarr.${DOMAIN}
services:
# Homepage - Application dashboard (AI-configurable via YAML)
# Access at: https://home.${DOMAIN}
homepage:
image: ghcr.io/gethomepage/homepage:latest
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
pids: 512
reservations:
cpus: '0.25'
memory: 128M
container_name: homepage
restart: unless-stopped
networks:
- homelab-network
- traefik-network
- dockerproxy-network
volumes:
- ./homepage:/app/config
- /var/run/docker.sock:/var/run/docker.sock # For Docker integration do not mount RO
- /opt/stacks:/opt/stacks # To discover other stacks
environment:
- PUID=995 # Must be set to the docker user ID
- PGID=995 # Must be set to the docker group ID
- TZ=${TZ}
- HOMEPAGE_ALLOWED_HOSTS=home.${DOMAIN}
labels:
- "homelab.category=dashboard"
- "homelab.description=Application dashboard (AI-configurable)"
- "traefik.enable=true"
- "traefik.http.routers.homepage.rule=Host(`home.${DOMAIN}`)"
- "traefik.http.routers.homepage.entrypoints=websecure"
- "traefik.http.routers.homepage.tls=true"
- "traefik.http.routers.homepage.middlewares=authelia@docker"
- "traefik.http.services.homepage.loadbalancer.server.port=3000"
# Homarr - Modern dashboard
# Access at: https://homarr.${DOMAIN}
homarr:
image: ghcr.io/ajnart/homarr:latest
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
pids: 512
reservations:
cpus: '0.25'
memory: 128M
container_name: homarr
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./homarr/config:/app/config/configs
- ./homarr/data:/data
- ./homarr/icons:/app/public/icons
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=${TZ}
labels:
- "homelab.category=dashboard"
- "homelab.description=Modern homelab dashboard"
- "traefik.enable=true"
- "traefik.http.routers.homarr.rule=Host(`homarr.${DOMAIN}`)"
- "traefik.http.routers.homarr.entrypoints=websecure"
- "traefik.http.routers.homarr.tls=true"
- "traefik.http.routers.homarr.middlewares=authelia@docker"
- "traefik.http.services.homarr.loadbalancer.server.port=7575"
- "x-dockge.url=https://homarr.${DOMAIN}"
- "x-dockge.url=https://homarr.${DOMAIN}"
networks:
homelab-network:
external: true
traefik-network:
external: true
dockerproxy-network:
external: true

View File

@@ -1,267 +0,0 @@
# Infrastructure Services
# Core services that other services depend on
# Place in /opt/stacks/infrastructure/docker-compose.yml
# NOTE: Traefik, Authelia, DuckDNS, and Gluetun have their own separate stacks
# See /opt/stacks/traefik/, /opt/stacks/authelia/, etc.
# Service Access URLs:
# - Dockge: https://dockge.${DOMAIN}
# - Portainer: https://portainer.${DOMAIN}
# - Pi-hole: https://pihole.${DOMAIN}
# - Dozzle: https://dozzle.${DOMAIN}
# - Glances: https://glances.${DOMAIN}
# - Netdata: https://netdata.${DOMAIN}
services:
# Dockge - Docker Compose Stack Manager (PRIMARY - preferred over Portainer)
# Access at: https://dockge.${DOMAIN}
dockge:
image: louislam/dockge:1
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
pids: 512
reservations:
cpus: '0.25'
memory: 128M
container_name: dockge
restart: unless-stopped
networks:
- homelab-network
- traefik-network
ports:
- "5001:5001" # Optional: direct access
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/stacks:/opt/stacks # Dockge manages stacks in this directory
- /opt/dockge/data:/app/data
environment:
- DOCKGE_STACKS_DIR=/opt/stacks
- DOCKGE_ENABLE_CONSOLE=true
# Proxy Authentication for SSO integration with Authelia
- DOCKGE_AUTH_PROXY_HEADER=Remote-User
- DOCKGE_AUTH_PROXY_AUTO_CREATE=true
- DOCKGE_AUTH_PROXY_LOGOUT_URL=https://auth.${DOMAIN}/logout
labels:
- "homelab.category=infrastructure"
- "homelab.description=Docker Compose stack manager (PRIMARY)"
- "traefik.enable=true"
- "traefik.http.routers.dockge.rule=Host(`dockge.${DOMAIN}`)"
- "traefik.http.routers.dockge.entrypoints=websecure"
- "traefik.http.routers.dockge.tls=true"
- "traefik.http.routers.dockge.middlewares=authelia@docker"
- "traefik.http.services.dockge.loadbalancer.server.port=5001"
- "x-dockge.url=https://dockge.${DOMAIN}"
- "x-dockge.url=https://dockge.${DOMAIN}"
# Pi-hole - Network-wide ad blocker and DNS server
# Access at: https://pihole.${DOMAIN}
pihole:
image: pihole/pihole:2024.01.0
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
pids: 256
reservations:
cpus: '0.10'
memory: 64M
container_name: pihole
restart: unless-stopped
networks:
- homelab-network
- traefik-network
ports:
- "53:53/tcp" # DNS TCP
- "53:53/udp" # DNS UDP
volumes:
- ./pihole/etc-pihole:/etc/pihole
- ./pihole/etc-dnsmasq.d:/etc/dnsmasq.d
environment:
- TZ=${TZ:-America/New_York}
- WEBPASSWORD=${PIHOLE_PASSWORD:-changeme}
- FTLCONF_LOCAL_IPV4=${SERVER_IP}
dns:
- 127.0.0.1
- 1.1.1.1
cap_add:
- NET_ADMIN
labels:
- "homelab.category=infrastructure"
- "homelab.description=Network-wide ad blocking and DNS"
- "traefik.enable=true"
- "traefik.http.routers.pihole.rule=Host(`pihole.${DOMAIN}`)"
- "traefik.http.routers.pihole.entrypoints=websecure"
- "traefik.http.routers.pihole.tls=true"
- "traefik.http.routers.pihole.tls.certresolver=letsencrypt"
- "traefik.http.routers.pihole.middlewares=authelia@docker"
- "traefik.http.services.pihole.loadbalancer.server.port=80"
# Watchtower - Automatic container updates
# TEMPORARILY DISABLED: Docker API version incompatibility with Docker 29.x
# Watchtower versions have API compatibility issues:
# - v1.7.1: Uses API v1.25 (too old for Docker 29.x which requires min v1.44)
# - v1.7.2+/latest: Has issues with API negotiation
# Issue tracked for resolution in future release
# To enable: Uncomment service below and run: docker compose up -d watchtower
#
# Watchtower - Automatic container updates
# Monitors and updates Docker containers to latest versions
# Runs daily at 4 AM
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
networks:
- homelab-network
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DOCKER_API_VERSION=1.52
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- WATCHTOWER_SCHEDULE=0 0 4 * * * # 4 AM daily
- WATCHTOWER_NOTIFICATIONS=shoutrrr
- WATCHTOWER_NOTIFICATION_URL=${WATCHTOWER_NOTIFICATION_URL:-}
labels:
- "homelab.category=infrastructure"
- "homelab.description=Automatic Docker container updates"
# Dozzle - Real-time Docker log viewer
# Access at: https://dozzle.${DOMAIN}
dozzle:
image: amir20/dozzle:latest
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
pids: 512
reservations:
cpus: '0.25'
memory: 128M
container_name: dozzle
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- DOZZLE_LEVEL=info
- DOZZLE_TAILSIZE=300
- DOZZLE_FILTER=status=running
labels:
- "homelab.category=infrastructure"
- "homelab.description=Real-time Docker log viewer"
- "traefik.enable=true"
- "traefik.http.routers.dozzle.rule=Host(`dozzle.${DOMAIN}`)"
- "traefik.http.routers.dozzle.entrypoints=websecure"
- "traefik.http.routers.dozzle.tls=true"
- "traefik.http.routers.dozzle.middlewares=authelia@docker"
- "traefik.http.services.dozzle.loadbalancer.server.port=8080"
# Docker Proxy - Socket proxy for security
# Used by services that need Docker socket access
dockerproxy:
image: tecnativa/docker-socket-proxy:latest
container_name: dockerproxy
restart: unless-stopped
networks:
- dockerproxy-network
ports:
- "127.0.0.1:2375:2375"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- NETWORKS=1
- NODES=1
labels:
- "homelab.category=infrastructure"
- "homelab.description=Docker socket proxy for security"
# Glances - System monitoring
# Access at: https://glances.${DOMAIN}
glances:
image: nicolargo/glances:latest-full
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
pids: 512
reservations:
cpus: '0.25'
memory: 128M
container_name: glances
restart: unless-stopped
networks:
- homelab-network
- traefik-network
pid: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./glances/config:/glances/conf
environment:
- GLANCES_OPT=-w
labels:
- "homelab.category=infrastructure"
- "homelab.description=System and Docker monitoring"
- "traefik.enable=true"
- "traefik.http.routers.glances.rule=Host(`glances.${DOMAIN}`)"
- "traefik.http.routers.glances.entrypoints=websecure"
- "traefik.http.routers.glances.tls=true"
- "traefik.http.routers.glances.middlewares=authelia@docker"
- "traefik.http.services.glances.loadbalancer.server.port=61208"
# Code Server - VS Code in browser
# Access at: https://code.${DOMAIN}
code-server:
image: lscr.io/linuxserver/code-server:latest
deploy:
resources:
limits:
cpus: '1.5'
memory: 1G
pids: 2048
reservations:
cpus: '0.75'
memory: 512M
container_name: code-server
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./code-server/config:/config
- /opt/stacks:/opt/stacks # Access to all stacks
- /mnt:/mnt:ro # Read-only access to data
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- PASSWORD=${CODE_SERVER_PASSWORD}
- SUDO_PASSWORD=${CODE_SERVER_SUDO_PASSWORD}
labels:
- "homelab.category=infrastructure"
- "homelab.description=VS Code in browser"
- "traefik.enable=true"
- "traefik.http.routers.code-server.rule=Host(`code.${DOMAIN}`)"
- "traefik.http.routers.code-server.entrypoints=websecure"
- "traefik.http.routers.code-server.tls.certresolver=letsencrypt"
- "traefik.http.routers.code-server.middlewares=authelia@docker"
- "traefik.http.services.code-server.loadbalancer.server.port=8443"
networks:
homelab-network:
external: true
traefik-network:
external: true
dockerproxy-network:
external: true

View File

@@ -1,341 +0,0 @@
# Media Management Services
# Content automation and library management (*arr apps, transcoders, etc.)
# Place in /opt/stacks/media-management/docker-compose.yml
# Service Access URLs:
# - Sonarr: https://sonarr.${DOMAIN}
# - Radarr: https://radarr.${DOMAIN}
# - Prowlarr: https://prowlarr.${DOMAIN}
# - Readarr: https://readarr.${DOMAIN}
# - Lidarr: https://lidarr.${DOMAIN}
# - LazyLibrarian: https://lazylibrarian.${DOMAIN}
# - Mylar3: https://mylar.${DOMAIN}
# - Jellyseerr: https://jellyseerr.${DOMAIN}
# - Tdarr: https://tdarr.${DOMAIN}
# - Unmanic: https://unmanic.${DOMAIN}
services:
# Sonarr - TV show automation
# Access at: https://sonarr.yourdomain.duckdns.org
sonarr:
image: linuxserver/sonarr:4.0.0
container_name: sonarr
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./sonarr/config:/config
- /mnt/media:/media
- /mnt/downloads:/downloads # Large downloads on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
labels:
- homelab.category=media
- homelab.description=TV show management and automation
# Traefik labels with Authelia
- traefik.enable=true
- traefik.http.routers.sonarr.rule=Host(`sonarr.${DOMAIN}`)
- traefik.http.routers.sonarr.entrypoints=websecure
- traefik.http.routers.sonarr.tls.certresolver=letsencrypt
- traefik.http.routers.sonarr.middlewares=authelia@docker
- traefik.http.services.sonarr.loadbalancer.server.port=8989
# Radarr - Movie automation
# Access at: https://radarr.yourdomain.duckdns.org
radarr:
image: linuxserver/radarr:5.2.6
container_name: radarr
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./radarr/config:/config
- /mnt/media:/media
- /mnt/downloads:/downloads # Large downloads on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
labels:
- homelab.category=media
- homelab.description=Movie management and automation
# Traefik labels with Authelia
- traefik.enable=true
- traefik.http.routers.radarr.rule=Host(`radarr.${DOMAIN}`)
- traefik.http.routers.radarr.entrypoints=websecure
- traefik.http.routers.radarr.tls.certresolver=letsencrypt
- traefik.http.routers.radarr.middlewares=authelia@docker
- traefik.http.services.radarr.loadbalancer.server.port=7878
# Prowlarr - Indexer manager
# Access at: https://prowlarr.yourdomain.duckdns.org
prowlarr:
image: linuxserver/prowlarr:1.11.4
container_name: prowlarr
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./prowlarr/config:/config
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
labels:
- homelab.category=media
- homelab.description=Indexer manager for Sonarr/Radarr
# Traefik labels with Authelia
- traefik.enable=true
- traefik.http.routers.prowlarr.rule=Host(`prowlarr.${DOMAIN}`)
- traefik.http.routers.prowlarr.entrypoints=websecure
- traefik.http.routers.prowlarr.tls.certresolver=letsencrypt
- traefik.http.routers.prowlarr.middlewares=authelia@docker
- traefik.http.services.prowlarr.loadbalancer.server.port=9696
# Readarr - Ebook and audiobook management
# Access at: https://readarr.${DOMAIN}
readarr:
image: linuxserver/readarr:0.4.19-nightly
container_name: readarr
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./readarr/config:/config
- /mnt/media/books:/books
- /mnt/downloads:/downloads
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
labels:
- homelab.category=media
- homelab.description=Ebook and audiobook management
- traefik.enable=true
- traefik.http.routers.readarr.rule=Host(`readarr.${DOMAIN}`)
- traefik.http.routers.readarr.entrypoints=websecure
- traefik.http.routers.readarr.tls.certresolver=letsencrypt
- traefik.http.routers.readarr.middlewares=authelia@docker
- traefik.http.services.readarr.loadbalancer.server.port=8787
# Lidarr - Music collection manager
# Access at: https://lidarr.${DOMAIN}
lidarr:
image: linuxserver/lidarr:2.0.7
container_name: lidarr
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./lidarr/config:/config
- /mnt/media/music:/music
- /mnt/downloads:/downloads
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
labels:
- homelab.category=media
- homelab.description=Music collection manager
- traefik.enable=true
- traefik.http.routers.lidarr.rule=Host(`lidarr.${DOMAIN}`)
- traefik.http.routers.lidarr.entrypoints=websecure
- traefik.http.routers.lidarr.tls.certresolver=letsencrypt
- traefik.http.routers.lidarr.middlewares=authelia@docker
- traefik.http.services.lidarr.loadbalancer.server.port=8686
# Lazy Librarian - Book manager
# Access at: https://lazylibrarian.${DOMAIN}
lazylibrarian:
image: linuxserver/lazylibrarian:latest
container_name: lazylibrarian
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./lazylibrarian/config:/config
- /mnt/media/books:/books
- /mnt/downloads:/downloads
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- DOCKER_MODS=linuxserver/mods:lazylibrarian-ffmpeg
labels:
- homelab.category=media
- homelab.description=Book download automation
- traefik.enable=true
- traefik.http.routers.lazylibrarian.rule=Host(`lazylibrarian.${DOMAIN}`)
- traefik.http.routers.lazylibrarian.entrypoints=websecure
- traefik.http.routers.lazylibrarian.tls.certresolver=letsencrypt
- traefik.http.routers.lazylibrarian.middlewares=authelia@docker
- traefik.http.services.lazylibrarian.loadbalancer.server.port=5299
# Mylar3 - Comic book manager
# Access at: https://mylar.${DOMAIN}
mylar3:
image: linuxserver/mylar3:latest
container_name: mylar3
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./mylar3/config:/config
- /mnt/media/comics:/comics
- /mnt/downloads:/downloads
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
labels:
- homelab.category=media
- homelab.description=Comic book collection manager
- traefik.enable=true
- traefik.http.routers.mylar.rule=Host(`mylar.${DOMAIN}`)
- traefik.http.routers.mylar.entrypoints=websecure
- traefik.http.routers.mylar.tls.certresolver=letsencrypt
- traefik.http.routers.mylar.middlewares=authelia@docker
- traefik.http.services.mylar.loadbalancer.server.port=8090
# Jellyseerr - Request management for Jellyfin/Plex
# Access at: https://jellyseerr.${DOMAIN}
jellyseerr:
image: fallenbagel/jellyseerr:latest
container_name: jellyseerr
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./jellyseerr/config:/app/config
environment:
- LOG_LEVEL=info
- TZ=${TZ}
labels:
- homelab.category=media
- homelab.description=Media request management
- traefik.enable=true
- traefik.http.routers.jellyseerr.rule=Host(`jellyseerr.${DOMAIN}`)
- traefik.http.routers.jellyseerr.entrypoints=websecure
- traefik.http.routers.jellyseerr.tls.certresolver=letsencrypt
- traefik.http.routers.jellyseerr.middlewares=authelia@docker
- traefik.http.services.jellyseerr.loadbalancer.server.port=5055
- "x-dockge.url=https://jellyseerr.${DOMAIN}"
- "x-dockge.url=https://jellyseerr.${DOMAIN}"
# FlareSolverr - Cloudflare bypass for Prowlarr
# No web UI - used by Prowlarr
flaresolverr:
image: flaresolverr/flaresolverr:latest
container_name: flaresolverr
restart: unless-stopped
networks:
- media-network
environment:
- LOG_LEVEL=info
- TZ=${TZ}
labels:
- homelab.category=media
- homelab.description=Cloudflare bypass for indexers
# Tdarr Server - Distributed transcoding server
# Access at: https://tdarr.${DOMAIN}
tdarr-server:
image: ghcr.io/haveagitgat/tdarr:latest
container_name: tdarr-server
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
ports:
- 8266:8266 # Server port
volumes:
- ./tdarr/server:/app/server
- ./tdarr/configs:/app/configs
- ./tdarr/logs:/app/logs
- /mnt/media:/media
- /mnt/tdarr-transcode:/temp # Transcode cache on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- serverIP=0.0.0.0
- serverPort=8266
- webUIPort=8265
labels:
- homelab.category=media
- homelab.description=Distributed transcoding server
- traefik.enable=true
- traefik.http.routers.tdarr.rule=Host(`tdarr.${DOMAIN}`)
- traefik.http.routers.tdarr.entrypoints=websecure
- traefik.http.routers.tdarr.tls.certresolver=letsencrypt
- traefik.http.routers.tdarr.middlewares=authelia@docker
- traefik.http.services.tdarr.loadbalancer.server.port=8265
# Tdarr Node - Transcoding worker
# No web UI - controlled by server
tdarr-node:
image: ghcr.io/haveagitgat/tdarr_node:latest
container_name: tdarr-node
restart: unless-stopped
networks:
- media-network
volumes:
- ./tdarr/configs:/app/configs
- ./tdarr/logs:/app/logs
- /mnt/media:/media
- /mnt/tdarr-transcode:/temp
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- nodeID=MainNode
- nodeIP=0.0.0.0
- nodePort=8267
- serverIP=tdarr-server
- serverPort=8266
labels:
- homelab.category=media
- homelab.description=Tdarr transcoding worker node
# Unmanic - Another transcoding option
# Access at: https://unmanic.${DOMAIN}
unmanic:
image: josh5/unmanic:latest
container_name: unmanic
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./unmanic/config:/config
- /mnt/media:/library
- /mnt/unmanic-cache:/tmp/unmanic # Transcode cache on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
labels:
- homelab.category=media
- homelab.description=Library optimization and transcoding
- traefik.enable=true
- traefik.http.routers.unmanic.rule=Host(`unmanic.${DOMAIN}`)
- traefik.http.routers.unmanic.entrypoints=websecure
- traefik.http.routers.unmanic.tls.certresolver=letsencrypt
- traefik.http.routers.unmanic.middlewares=authelia@docker
- traefik.http.services.unmanic.loadbalancer.server.port=8888
networks:
media-network:
external: true
homelab-network:
external: true
traefik-network:
external: true

View File

@@ -1,81 +0,0 @@
# Media Services
# Default Services for media management and streaming
# Place in /opt/stacks/media/docker-compose.yml
# Service Access URLs:
# - Jellyfin: https://jellyfin.${DOMAIN} (no SSO - app access)
# - Plex: https://plex.${DOMAIN} (no SSO - app access)
# - qBittorrent: https://qbit.${DOMAIN} (routed through Gluetun VPN)
services:
# Jellyfin - Open-source media streaming server
# Access at: https://jellyfin.yourdomain.duckdns.org
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
jellyfin:
image: jellyfin/jellyfin:10.8.13
container_name: jellyfin
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /mnt/media:/media:ro # Large media files on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
# Uncomment for hardware transcoding
# devices:
# - /dev/dri:/dev/dri
labels:
- "homelab.category=media"
- "homelab.description=Open-source media streaming server"
# Traefik labels - NO Authelia for app access
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.${DOMAIN}`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls=true"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
- "x-dockge.url=https://jellyfin.${DOMAIN}"
- "x-dockge.url=https://jellyfin.${DOMAIN}"
# Calibre-Web - Ebook reader and server
# Access at: https://calibre.${DOMAIN}
calibre-web:
image: lscr.io/linuxserver/calibre-web:latest
container_name: calibre-web
restart: unless-stopped
networks:
- media-network
- homelab-network
- traefik-network
volumes:
- ./calibre-web/config:/config
- /mnt/media/books:/books
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
- DOCKER_MODS=linuxserver/mods:universal-calibre
labels:
- "homelab.category=media"
- "homelab.description=Ebook reader and library management"
- "traefik.enable=true"
- "traefik.http.routers.calibre.rule=Host(`calibre.${DOMAIN}`)"
- "traefik.http.routers.calibre.entrypoints=websecure"
- "traefik.http.routers.calibre.tls.certresolver=letsencrypt"
- "traefik.http.routers.calibre.middlewares=authelia@docker"
- "traefik.http.services.calibre.loadbalancer.server.port=8083"
networks:
media-network:
driver: bridge
homelab-network:
external: true
traefik-network:
external: true

View File

@@ -1,227 +0,0 @@
# Monitoring and Observability Services
# Services for monitoring your homelab infrastructure
# Place in /opt/stacks/monitoring/docker-compose.yml
# Service Access URLs:
# - Prometheus: http://server-ip:9090 (or configure Traefik)
# - Grafana: http://server-ip:3000 (or configure Traefik)
# - Uptime Kuma: https://status.${DOMAIN}
# - Node Exporter: http://server-ip:9100/metrics
# - cAdvisor: http://server-ip:8082
# - Loki: http://server-ip:3100
# NOTE: Prometheus, Grafana, Loki use ports because they need to be accessible to other services
# Add Traefik labels if you want https://prometheus.${DOMAIN} access
services:
# Prometheus - Metrics collection and storage
# Access at: http://server-ip:9090
prometheus:
image: prom/prometheus:v2.48.1
container_name: prometheus
restart: unless-stopped
networks:
- monitoring-network
- homelab-network
- traefik-network
ports:
- "9090:9090"
volumes:
- ./config/prometheus:/etc/prometheus
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
user: "${PUID:-1000}:${PGID:-1000}"
labels:
- "homelab.category=monitoring"
- "homelab.description=Metrics collection and time-series database"
- "traefik.enable=true"
- "traefik.http.routers.prometheus.rule=Host(`prometheus.${DOMAIN}`)"
- "traefik.http.routers.prometheus.entrypoints=websecure"
- "traefik.http.routers.prometheus.tls=true"
- "traefik.http.routers.prometheus.tls.certresolver=letsencrypt"
- "traefik.http.routers.prometheus.middlewares=authelia@docker"
- "traefik.http.services.prometheus.loadbalancer.server.port=9090"
# Grafana - Metrics visualization
# Access at: http://server-ip:3000
# Default credentials: admin / admin (change on first login)
grafana:
image: grafana/grafana:10.2.3
container_name: grafana
restart: unless-stopped
networks:
- monitoring-network
- homelab-network
- traefik-network
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./config/grafana/provisioning:/etc/grafana/provisioning
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=https://grafana.${DOMAIN}
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource,grafana-piechart-panel
user: "${PUID:-1000}:${PGID:-1000}"
depends_on:
- prometheus
labels:
- "homelab.category=monitoring"
- "homelab.description=Metrics visualization and dashboards"
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.${DOMAIN}`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
- "traefik.http.routers.grafana.tls=true"
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
- "traefik.http.routers.grafana.middlewares=authelia@docker"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
# Node Exporter - Host metrics exporter
# Metrics at: http://server-ip:9100/metrics
node-exporter:
image: prom/node-exporter:v1.7.0
container_name: node-exporter
restart: unless-stopped
networks:
- monitoring-network
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
labels:
- "homelab.category=monitoring"
- "homelab.description=Hardware and OS metrics exporter"
# cAdvisor - Container metrics exporter
# Access at: http://server-ip:8082
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.47.2
container_name: cadvisor
restart: unless-stopped
networks:
- monitoring-network
- homelab-network
- traefik-network
ports:
- "8082:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
privileged: true
devices:
- /dev/kmsg
labels:
- "homelab.category=monitoring"
- "homelab.description=Container metrics and performance monitoring"
- "traefik.enable=true"
- "traefik.http.routers.cadvisor.rule=Host(`cadvisor.${DOMAIN}`)"
- "traefik.http.routers.cadvisor.entrypoints=websecure"
- "traefik.http.routers.cadvisor.tls=true"
- "traefik.http.routers.cadvisor.tls.certresolver=letsencrypt"
- "traefik.http.routers.cadvisor.middlewares=authelia@docker"
- "traefik.http.services.cadvisor.loadbalancer.server.port=8080"
# Uptime Kuma - Uptime monitoring
# Access at: https://uptime-kuma.${DOMAIN}
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
networks:
- monitoring-network
- homelab-network
- traefik-network
volumes:
- uptime-kuma-data:/app/data
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
- "homelab.category=monitoring"
- "homelab.description=Service uptime monitoring and alerts"
- "traefik.enable=true"
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime-kuma.${DOMAIN}`)"
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma.tls=true"
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
- "traefik.http.routers.uptime-kuma.middlewares=authelia@docker"
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
# Loki - Log aggregation
# Access at: http://server-ip:3100
loki:
image: grafana/loki:2.9.3
container_name: loki
restart: unless-stopped
networks:
- monitoring-network
- homelab-network
- traefik-network
ports:
- "3100:3100"
volumes:
- ./config/loki:/etc/loki
- loki-data:/loki
command: -config.file=/etc/loki/loki-config.yml
user: "${PUID:-1000}:${PGID:-1000}"
labels:
- "homelab.category=monitoring"
- "homelab.description=Log aggregation system"
- "traefik.enable=true"
- "traefik.http.routers.loki.rule=Host(`loki.${DOMAIN}`)"
- "traefik.http.routers.loki.entrypoints=websecure"
- "traefik.http.routers.loki.tls=true"
- "traefik.http.routers.loki.tls.certresolver=letsencrypt"
- "traefik.http.routers.loki.middlewares=authelia@docker"
- "traefik.http.services.loki.loadbalancer.server.port=3100"
# Promtail - Log shipper for Loki
# Ships Docker container logs to Loki
promtail:
image: grafana/promtail:2.9.3
container_name: promtail
restart: unless-stopped
networks:
- monitoring-network
volumes:
- ./config/promtail:/etc/promtail
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
command: -config.file=/etc/promtail/promtail-config.yml
depends_on:
- loki
labels:
- "homelab.category=monitoring"
- "homelab.description=Log collector for Loki"
volumes:
prometheus-data:
driver: local
grafana-data:
driver: local
uptime-kuma-data:
driver: local
loki-data:
driver: local
networks:
monitoring-network:
driver: bridge
homelab-network:
external: true
traefik-network:
external: true

View File

@@ -1,337 +0,0 @@
# Productivity and Content Management Services
# Place in /opt/stacks/productivity/docker-compose.yml
# Service Access URLs:
# - Nextcloud: https://nextcloud.${DOMAIN}
# - Mealie: https://mealie.${DOMAIN}
# - WordPress: https://blog.${DOMAIN}
# - Gitea: https://git.${DOMAIN}
# - DokuWiki: https://wiki.${DOMAIN}
# - BookStack: https://docs.${DOMAIN}
# - MediaWiki: https://mediawiki.${DOMAIN}
services:
# Nextcloud - File sync and collaboration
# Access at: https://nextcloud.${DOMAIN}
nextcloud:
image: nextcloud:28
container_name: nextcloud
restart: unless-stopped
networks:
- homelab-network
- traefik-network
- nextcloud-network
volumes:
- ./nextcloud/html:/var/www/html
- /mnt/nextcloud-data:/var/www/html/data # Large data on separate drive
environment:
- MYSQL_HOST=nextcloud-db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER:-admin}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.${DOMAIN}
- TRUSTED_PROXIES=172.18.0.0/16
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST=nextcloud.${DOMAIN}
depends_on:
- nextcloud-db
labels:
- "homelab.category=productivity"
- "homelab.description=File sync and collaboration"
- "traefik.enable=true"
- "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${DOMAIN}`)"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
- "traefik.http.routers.nextcloud.middlewares=authelia@docker"
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
nextcloud-db:
image: mariadb:10.11
container_name: nextcloud-db
restart: unless-stopped
networks:
- nextcloud-network
volumes:
- nextcloud-db-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${NEXTCLOUD_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
labels:
- "homelab.category=productivity"
- "homelab.description=Nextcloud database"
# Mealie - Recipe manager
# Access at: https://mealie.${DOMAIN}
mealie:
image: ghcr.io/mealie-recipes/mealie:latest
container_name: mealie
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./mealie/data:/app/data
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- BASE_URL=https://mealie.${DOMAIN}
- DB_ENGINE=sqlite
labels:
- "homelab.category=productivity"
- "homelab.description=Recipe manager and meal planner"
- "traefik.enable=true"
- "traefik.http.routers.mealie.rule=Host(`mealie.${DOMAIN}`)"
- "traefik.http.routers.mealie.entrypoints=websecure"
- "traefik.http.routers.mealie.tls.certresolver=letsencrypt"
- "traefik.http.services.mealie.loadbalancer.server.port=9000"
# No Authelia - family members should access easily
# WordPress - Blog/website platform
# Access at: https://blog.${DOMAIN}
wordpress:
image: wordpress:latest
container_name: wordpress
restart: unless-stopped
networks:
- homelab-network
- traefik-network
- wordpress-network
volumes:
- ./wordpress/html:/var/www/html
environment:
- WORDPRESS_DB_HOST=wordpress-db
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=${WORDPRESS_DB_PASSWORD}
- WORDPRESS_DB_NAME=wordpress
depends_on:
- wordpress-db
labels:
- "homelab.category=productivity"
- "homelab.description=Blog and website platform"
- "traefik.enable=true"
- "traefik.http.routers.wordpress.rule=Host(`wordpress.${DOMAIN}`)"
- "traefik.http.routers.wordpress.entrypoints=websecure"
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
- "traefik.http.services.wordpress.loadbalancer.server.port=80"
# No Authelia - public blog
wordpress-db:
image: mariadb:10.11
container_name: wordpress-db
restart: unless-stopped
networks:
- wordpress-network
volumes:
- wordpress-db-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${WORDPRESS_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=${WORDPRESS_DB_PASSWORD}
labels:
- "homelab.category=productivity"
- "homelab.description=WordPress database"
# Gitea - Self-hosted Git service
# Access at: https://git.${DOMAIN}
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
networks:
- homelab-network
- traefik-network
- gitea-network
volumes:
- ./gitea/data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID=${PUID:-1000}
- USER_GID=${PGID:-1000}
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea-db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
depends_on:
- gitea-db
labels:
- "homelab.category=productivity"
- "homelab.description=Self-hosted Git service"
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`gitea.${DOMAIN}`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
- "traefik.http.routers.gitea.middlewares=authelia@docker"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
gitea-db:
image: postgres:14-alpine
container_name: gitea-db
restart: unless-stopped
networks:
- gitea-network
volumes:
- gitea-db-data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${GITEA_DB_PASSWORD}
- POSTGRES_DB=gitea
labels:
- "homelab.category=productivity"
- "homelab.description=Gitea database"
# DokuWiki - Wiki without database
# Access at: https://wiki.${DOMAIN}
dokuwiki:
image: lscr.io/linuxserver/dokuwiki:latest
container_name: dokuwiki
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./dokuwiki/config:/config
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
labels:
- "homelab.category=productivity"
- "homelab.description=File-based wiki"
- "traefik.enable=true"
- "traefik.http.routers.dokuwiki.rule=Host(`dokuwiki.${DOMAIN}`)"
- "traefik.http.routers.dokuwiki.entrypoints=websecure"
- "traefik.http.routers.dokuwiki.tls.certresolver=letsencrypt"
- "traefik.http.routers.dokuwiki.middlewares=authelia@docker"
- "traefik.http.services.dokuwiki.loadbalancer.server.port=80"
# BookStack - Documentation platform
# Access at: https://docs.${DOMAIN}
bookstack:
image: lscr.io/linuxserver/bookstack:latest
container_name: bookstack
restart: unless-stopped
networks:
- homelab-network
- traefik-network
- bookstack-network
volumes:
- ./bookstack/config:/config
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- APP_URL=https://bookstack.${DOMAIN}
- DB_HOST=bookstack-db
- DB_PORT=3306
- DB_DATABASE=bookstack
- DB_USERNAME=bookstack
- DB_PASSWORD=${BOOKSTACK_DB_PASSWORD}
- APP_KEY=base64:NsYD8+8MAvtBhK8xw9p8pxQDy4x8aOQi/78M3CsseAw=
depends_on:
- bookstack-db
labels:
- "homelab.category=productivity"
- "homelab.description=Documentation and wiki platform"
- "traefik.enable=true"
- "traefik.http.routers.bookstack.rule=Host(`bookstack.${DOMAIN}`)"
- "traefik.http.routers.bookstack.entrypoints=websecure"
- "traefik.http.routers.bookstack.tls.certresolver=letsencrypt"
- "traefik.http.routers.bookstack.middlewares=authelia@docker"
- "traefik.http.services.bookstack.loadbalancer.server.port=80"
- "x-dockge.url=https://bookstack.${DOMAIN}"
- "x-dockge.url=https://bookstack.${DOMAIN}"
bookstack-db:
image: mariadb:10.11
container_name: bookstack-db
restart: unless-stopped
networks:
- bookstack-network
volumes:
- bookstack-db-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${BOOKSTACK_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=${BOOKSTACK_DB_PASSWORD}
labels:
- "homelab.category=productivity"
- "homelab.description=BookStack database"
# MediaWiki - Wiki platform
# Access at: https://mediawiki.${DOMAIN}
mediawiki:
image: mediawiki:latest
container_name: mediawiki
restart: unless-stopped
networks:
- homelab-network
- traefik-network
- mediawiki-network
volumes:
- ./mediawiki/images:/var/www/html/images
- ./mediawiki/LocalSettings.php:/var/www/html/LocalSettings.php
environment:
- MEDIAWIKI_DB_HOST=mediawiki-db
- MEDIAWIKI_DB_NAME=mediawiki
- MEDIAWIKI_DB_USER=mediawiki
- MEDIAWIKI_DB_PASSWORD=${MEDIAWIKI_DB_PASSWORD}
depends_on:
- mediawiki-db
labels:
- "homelab.category=productivity"
- "homelab.description=MediaWiki platform"
- "traefik.enable=true"
- "traefik.http.routers.mediawiki.rule=Host(`mediawiki.${DOMAIN}`)"
- "traefik.http.routers.mediawiki.entrypoints=websecure"
- "traefik.http.routers.mediawiki.tls.certresolver=letsencrypt"
- "traefik.http.routers.mediawiki.middlewares=authelia@docker"
- "traefik.http.services.mediawiki.loadbalancer.server.port=80"
mediawiki-db:
image: mariadb:10.11
container_name: mediawiki-db
restart: unless-stopped
networks:
- mediawiki-network
volumes:
- mediawiki-db-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${MEDIAWIKI_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=mediawiki
- MYSQL_USER=mediawiki
- MYSQL_PASSWORD=${MEDIAWIKI_DB_PASSWORD}
labels:
- "homelab.category=productivity"
- "homelab.description=MediaWiki database"
volumes:
nextcloud-db-data:
wordpress-db-data:
gitea-db-data:
bookstack-db-data:
mediawiki-db-data:
networks:
homelab-network:
external: true
traefik-network:
external: true
nextcloud-network:
driver: bridge
wordpress-network:
driver: bridge
gitea-network:
driver: bridge
bookstack-network:
driver: bridge
mediawiki-network:
driver: bridge

View File

@@ -1,170 +0,0 @@
# Backup and Utility Services
# Place in /opt/stacks/utilities/docker-compose.yml
# Service Access URLs:
# - Backrest: https://backrest.${DOMAIN}
# - Duplicati: https://duplicati.${DOMAIN}
# - Form.io: https://forms.${DOMAIN}
# - Vaultwarden (Bitwarden): https://vault.${DOMAIN}
services:
# Backrest - Backup solution for restic
# Access at: https://backrest.${DOMAIN}
backrest:
image: garethgeorge/backrest:latest
container_name: backrest
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./backrest/data:/data
- ./backrest/config:/config
- /opt/stacks:/opt/stacks:ro # Backup source
- /mnt:/mnt:ro # Backup additional drives
- backrest-cache:/cache
environment:
- BACKREST_DATA=/data
- BACKREST_CONFIG=/config/config.json
- TZ=${TZ}
labels:
- "homelab.category=utilities"
- "homelab.description=Backup management with restic"
- "traefik.enable=true"
- "traefik.http.routers.backrest.rule=Host(`backrest.${DOMAIN}`)"
- "traefik.http.routers.backrest.entrypoints=websecure"
- "traefik.http.routers.backrest.tls.certresolver=letsencrypt"
- "traefik.http.routers.backrest.middlewares=authelia@docker"
- "traefik.http.services.backrest.loadbalancer.server.port=9898"
- "x-dockge.url=https://backrest.${DOMAIN}"
- "x-dockge.url=https://backrest.${DOMAIN}"
# Duplicati - Backup solution
# Access at: https://duplicati.${DOMAIN}
duplicati:
image: lscr.io/linuxserver/duplicati:2.0.7
container_name: duplicati
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./duplicati/config:/config
- /opt/stacks:/source/stacks:ro
- /mnt:/source/mnt:ro
- /mnt/backups:/backups
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
labels:
- "homelab.category=utilities"
- "homelab.description=Backup software with encryption"
- "traefik.enable=true"
- "traefik.http.routers.duplicati.rule=Host(`duplicati.${DOMAIN}`)"
- "traefik.http.routers.duplicati.entrypoints=websecure"
- "traefik.http.routers.duplicati.tls.certresolver=letsencrypt"
- "traefik.http.routers.duplicati.middlewares=authelia@docker"
- "traefik.http.services.duplicati.loadbalancer.server.port=8200"
# Form.io - Form builder (DISABLED - image not available)
# Uncomment and configure if formio/formio image becomes available
# formio:
# image: formio/formio:latest
# container_name: formio
# restart: unless-stopped
# networks:
# - homelab-network
# - traefik-network
# - formio-network
# environment:
# - MONGO_URL=mongodb://formio-mongo:27017/formio
# - JWT_SECRET=${FORMIO_JWT_SECRET}
# - DB_SECRET=${FORMIO_DB_SECRET}
# depends_on:
# - formio-mongo
# labels:
# - "homelab.category=utilities"
# - "homelab.description=Form builder platform"
# - "traefik.enable=true"
# - "traefik.http.routers.formio.rule=Host(`forms.${DOMAIN}`)"
# - "traefik.http.routers.formio.entrypoints=websecure"
# - "traefik.http.routers.formio.tls.certresolver=letsencrypt"
# - "traefik.http.routers.formio.middlewares=authelia@docker"
# - "traefik.http.services.formio.loadbalancer.server.port=3000"
# formio-mongo:
# image: mongo:6.0
# container_name: formio-mongo
# restart: unless-stopped
# networks:
# - formio-network
# volumes:
# - formio-mongo-data:/data/db
# labels:
# - "homelab.category=utilities"
# - "homelab.description=Form.io database"
# Bitwarden (Vaultwarden) - Password manager
# Access at: https://vault.${DOMAIN}
# Note: SSO disabled for browser extension and mobile app compatibility
vaultwarden:
image: vaultwarden/server:1.30.1
container_name: vaultwarden
restart: unless-stopped
networks:
- homelab-network
- traefik-network
volumes:
- ./vaultwarden/data:/data
environment:
- DOMAIN=https://vault.${DOMAIN}
- SIGNUPS_ALLOWED=${BITWARDEN_SIGNUPS_ALLOWED:-true}
- INVITATIONS_ALLOWED=${BITWARDEN_INVITATIONS_ALLOWED:-true}
- ADMIN_TOKEN=${BITWARDEN_ADMIN_TOKEN}
# SMTP disabled - uncomment and configure to enable email
# - SMTP_HOST=${SMTP_HOST}
# - SMTP_FROM=${SMTP_FROM}
# - SMTP_PORT=${SMTP_PORT:-587}
# - SMTP_SECURITY=${SMTP_SECURITY:-starttls}
# - SMTP_USERNAME=${SMTP_USERNAME}
# - SMTP_PASSWORD=${SMTP_PASSWORD}
labels:
- "homelab.category=utilities"
- "homelab.description=Self-hosted password manager (Bitwarden)"
- "traefik.enable=true"
- "traefik.http.routers.vaultwarden.rule=Host(`vault.${DOMAIN}`)"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.routers.vaultwarden.tls=true"
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
# SSO disabled for browser extension and mobile app compatibility
# - "traefik.http.routers.vaultwarden.middlewares=authelia@docker"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
# Authelia Redis - Session storage for Authelia
# No web UI - backend service
authelia-redis:
image: redis:7-alpine
container_name: authelia-redis
restart: unless-stopped
networks:
- homelab-network
volumes:
- authelia-redis-data:/data
command: redis-server --save 60 1 --loglevel warning
labels:
- "homelab.category=utilities"
- "homelab.description=Session storage for Authelia"
volumes:
backrest-cache:
formio-mongo-data:
authelia-redis-data:
networks:
homelab-network:
external: true
traefik-network:
external: true
formio-network:
driver: bridge

View File

@@ -114,7 +114,7 @@ users:
```bash
# Deploy core infrastructure
sudo mkdir -p /opt/stacks/core
cp docker-compose/core.yml /opt/stacks/core/docker-compose.yml
cp docker-compose/core/docker-compose.yml /opt/stacks/core/docker-compose.yml
cp -r config-templates/traefik /opt/stacks/core/
cp .env /opt/stacks/core/
@@ -129,7 +129,7 @@ docker compose up -d
```bash
sudo mkdir -p /opt/stacks/infrastructure
cp docker-compose/infrastructure.yml /opt/stacks/infrastructure/docker-compose.yml
cp docker-compose/infrastructure/docker-compose.yml /opt/stacks/infrastructure/docker-compose.yml
cp .env /opt/stacks/infrastructure/
cd /opt/stacks/infrastructure
docker compose up -d
@@ -139,7 +139,7 @@ docker compose up -d
```bash
sudo mkdir -p /opt/stacks/dashboards
cp docker-compose/dashboards.yml /opt/stacks/dashboards/docker-compose.yml
cp docker-compose/dashboards/docker-compose.yml /opt/stacks/dashboards/docker-compose.yml
cp -r config-templates/homepage /opt/stacks/dashboards/
cp .env /opt/stacks/dashboards/

View File

@@ -495,7 +495,7 @@ deploy:
### Create a new stack
1. Create directory: `mkdir /opt/stacks/new-stack`
2. Copy compose file: `cp docker-compose/template.yml /opt/stacks/new-stack/docker-compose.yml`
2. Copy compose file: `cp docker-compose/core/docker-compose.yml /opt/stacks/new-stack/docker-compose.yml`
3. Copy env: `cp .env /opt/stacks/new-stack/`
4. Edit configuration
5. Deploy: `cd /opt/stacks/new-stack && docker compose up -d`
@@ -542,7 +542,7 @@ nano .env
# Deploy core only
mkdir -p /opt/stacks/core
cp docker-compose/core.yml /opt/stacks/core/docker-compose.yml
cp docker-compose/core/docker-compose.yml /opt/stacks/core/docker-compose.yml
cp -r config-templates/traefik /opt/stacks/core/
cp -r config-templates/authelia /opt/stacks/core/
cp .env /opt/stacks/core/
@@ -554,9 +554,9 @@ cd /opt/stacks/core && docker compose up -d
# After core is running, deploy all stacks
# Use Dockge UI at https://dockge.yourdomain.duckdns.org
# Or deploy manually:
docker compose -f docker-compose/infrastructure.yml up -d
docker compose -f docker-compose/dashboards.yml up -d
docker compose -f docker-compose/media.yml up -d
cd /opt/stacks/infrastructure && docker compose up -d
cd /opt/stacks/dashboards && docker compose up -d
cd /opt/stacks/media && docker compose up -d
# etc.
```

View File

@@ -111,7 +111,7 @@ if [ -f "/opt/stacks/core/docker-compose.yml" ]; then
log_info "Creating backup: docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)"
cp /opt/stacks/core/docker-compose.yml /opt/stacks/core/docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)
fi
cp "$REPO_DIR/docker-compose/core.yml" /opt/stacks/core/docker-compose.yml
cp "$REPO_DIR/docker-compose/core/docker-compose.yml" /opt/stacks/core/docker-compose.yml
if [ -d "/opt/stacks/core/traefik" ]; then
log_warning "Traefik configuration already exists in /opt/stacks/core/"
@@ -178,7 +178,7 @@ log_info " - Docker Proxy (Security)"
echo ""
# Copy infrastructure stack
cp "$REPO_DIR/docker-compose/infrastructure.yml" /opt/stacks/infrastructure/docker-compose.yml
cp "$REPO_DIR/docker-compose/infrastructure/docker-compose.yml" /opt/stacks/infrastructure/docker-compose.yml
cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env
# Deploy infrastructure stack