diff --git a/.env.example b/.env.example index 9f171bb..7519bb9 100644 --- a/.env.example +++ b/.env.example @@ -47,6 +47,102 @@ VPN_COUNTRY=Netherlands # Preferred VPN server location # SURFSHARK_USERNAME=your-surfshark-username # SURFSHARK_PASSWORD=your-surfshark-password +# Media Services +PLEX_CLAIM=claim-xxxxxxxxxx + +# Monitoring & Dashboards +GRAFANA_ADMIN_PASSWORD=changeme + +# Development Tools +CODE_SERVER_PASSWORD=changeme +CODE_SERVER_SUDO_PASSWORD=changeme + +# Databases - General +POSTGRES_USER=postgres +POSTGRES_PASSWORD=changeme +POSTGRES_DB=homelab + +PGADMIN_EMAIL=admin@example.com +PGADMIN_PASSWORD=changeme + +# Infrastructure +PIHOLE_PASSWORD=changeme +WATCHTOWER_NOTIFICATION_URL= + +# Productivity Services - Nextcloud +NEXTCLOUD_ADMIN_USER=admin +NEXTCLOUD_ADMIN_PASSWORD=changeme +NEXTCLOUD_DB_PASSWORD=changeme +NEXTCLOUD_DB_ROOT_PASSWORD=changeme + +# Productivity Services - Gitea +GITEA_DB_PASSWORD=changeme + +# Productivity Services - WordPress +WORDPRESS_DB_PASSWORD=changeme +WORDPRESS_DB_ROOT_PASSWORD=changeme + +# Productivity Services - BookStack +BOOKSTACK_DB_PASSWORD=changeme +BOOKSTACK_DB_ROOT_PASSWORD=changeme + +# Productivity Services - MediaWiki +MEDIAWIKI_DB_PASSWORD=changeme +MEDIAWIKI_DB_ROOT_PASSWORD=changeme + +# Utilities - Form.io +FORMIO_JWT_SECRET=changeme +FORMIO_DB_SECRET=changeme + +# Development - Jupyter +JUPYTER_TOKEN=changeme + +# Cloudflare API (optional, for DNS challenge) +# CF_DNS_API_TOKEN=your-cloudflare-api-token + +# qBittorrent +QBITTORRENT_USER=admin +QBITTORRENT_PASS=changeme + +# Homepage Dashboard - API Keys and Tokens +# Generate these from each service's settings page +HOMEPAGE_VAR_DOMAIN=${DOMAIN} +HOMEPAGE_VAR_SERVER_IP=${SERVER_IP} +HOMEPAGE_VAR_PORTAINER_KEY=your-portainer-api-key +HOMEPAGE_VAR_PIHOLE_KEY=your-pihole-api-key +HOMEPAGE_VAR_PLEX_KEY=your-plex-token +HOMEPAGE_VAR_JELLYFIN_KEY=your-jellyfin-api-key +HOMEPAGE_VAR_SONARR_KEY=your-sonarr-api-key +HOMEPAGE_VAR_RADARR_KEY=your-radarr-api-key +HOMEPAGE_VAR_LIDARR_KEY=your-lidarr-api-key +HOMEPAGE_VAR_READARR_KEY=your-readarr-api-key +HOMEPAGE_VAR_PROWLARR_KEY=your-prowlarr-api-key +HOMEPAGE_VAR_JELLYSEERR_KEY=your-jellyseerr-api-key +HOMEPAGE_VAR_QBITTORRENT_USER=${QBITTORRENT_USER} +HOMEPAGE_VAR_QBITTORRENT_PASS=${QBITTORRENT_PASS} +HOMEPAGE_VAR_HA_KEY=your-home-assistant-long-lived-token +HOMEPAGE_VAR_NEXTCLOUD_USER=${NEXTCLOUD_ADMIN_USER} +HOMEPAGE_VAR_NEXTCLOUD_PASS=${NEXTCLOUD_ADMIN_PASSWORD} +HOMEPAGE_VAR_GRAFANA_USER=admin +HOMEPAGE_VAR_GRAFANA_PASS=${GRAFANA_ADMIN_PASSWORD} +HOMEPAGE_VAR_BOOKSTACK_KEY=your-bookstack-api-token +HOMEPAGE_VAR_UPTIMEKUMA_SLUG=your-uptime-kuma-slug +HOMEPAGE_VAR_OPENWEATHER_KEY=your-openweather-api-key +HOMEPAGE_VAR_WEATHERAPI_KEY=your-weatherapi-key +HOMEPAGE_VAR_UNIFI_USER=your-unifi-username +HOMEPAGE_VAR_UNIFI_PASS=your-unifi-password + +# Add your own variables below + +# Get WireGuard details from Surfshark dashboard +SURFSHARK_PRIVATE_KEY=your-wireguard-private-key +SURFSHARK_ADDRESSES=10.14.0.2/16 +VPN_COUNTRY=Netherlands # Preferred VPN server location + +# Alternative: OpenVPN credentials (if not using WireGuard) +# SURFSHARK_USERNAME=your-surfshark-username +# SURFSHARK_PASSWORD=your-surfshark-password + # Plex Configuration PLEX_CLAIM=claim-xxxxxxxxxx diff --git a/config-templates/homepage/docker.yaml b/config-templates/homepage/docker.yaml new file mode 100644 index 0000000..57ca23a --- /dev/null +++ b/config-templates/homepage/docker.yaml @@ -0,0 +1,13 @@ +# Homepage Configuration - Docker Integration +# Copy to /opt/stacks/homepage/config/docker.yaml +# Enables auto-discovery of containers and status monitoring + +--- +# Docker socket (via proxy for security) +my-docker: + socket: /var/run/docker.sock + +# Or use Docker socket proxy (recommended for production) +# my-docker: +# host: dockerproxy +# port: 2375 diff --git a/config-templates/homepage/services.yaml b/config-templates/homepage/services.yaml new file mode 100644 index 0000000..47a5e4c --- /dev/null +++ b/config-templates/homepage/services.yaml @@ -0,0 +1,373 @@ +# Homepage Configuration - Services +# Copy to /opt/stacks/homepage/config/services.yaml +# This file is AI-configurable - Homepage will auto-discover services via Docker labels + +--- +# Infrastructure Services +- Infrastructure: + - Dockge: + icon: dockge.png + href: https://dockge.{{HOMEPAGE_VAR_DOMAIN}} + description: Docker Compose Stack Manager (PRIMARY) + container: dockge + widget: + type: dockge + url: http://dockge:5001 + + - Traefik: + icon: traefik.png + href: https://traefik.{{HOMEPAGE_VAR_DOMAIN}} + description: Reverse Proxy & SSL + container: traefik + widget: + type: traefik + url: http://traefik:8080 + + - Authelia: + icon: authelia.png + href: https://auth.{{HOMEPAGE_VAR_DOMAIN}} + description: Single Sign-On + container: authelia + widget: + type: authelia + url: http://authelia:9091 + + - Portainer: + icon: portainer.png + href: https://portainer.{{HOMEPAGE_VAR_DOMAIN}} + description: Docker Management (Secondary) + container: portainer + widget: + type: portainer + url: http://portainer:9000 + env: 1 + key: {{HOMEPAGE_VAR_PORTAINER_KEY}} + + - Pi-hole: + icon: pi-hole.png + href: https://pihole.{{HOMEPAGE_VAR_DOMAIN}} + description: Network-wide Ad Blocking + container: pihole + widget: + type: pihole + url: http://pihole + key: {{HOMEPAGE_VAR_PIHOLE_KEY}} + + - Dozzle: + icon: dozzle.png + href: https://dozzle.{{HOMEPAGE_VAR_DOMAIN}} + description: Real-time Docker Logs + container: dozzle + + - Glances: + icon: glances.png + href: https://glances.{{HOMEPAGE_VAR_DOMAIN}} + description: System Monitoring + container: glances + widget: + type: glances + url: http://glances:61208 + metric: cpu + +# Dashboards +- Dashboards: + - Homarr: + icon: homarr.png + href: https://homarr.{{HOMEPAGE_VAR_DOMAIN}} + description: Alternative Dashboard + container: homarr + + - Uptime Kuma: + icon: uptime-kuma.png + href: https://status.{{HOMEPAGE_VAR_DOMAIN}} + description: Uptime Monitoring + container: uptime-kuma + widget: + type: uptimekuma + url: http://uptime-kuma:3001 + slug: {{HOMEPAGE_VAR_UPTIMEKUMA_SLUG}} + +# Media - Streaming +- Media Streaming: + - Plex: + icon: plex.png + href: https://plex.{{HOMEPAGE_VAR_DOMAIN}} + description: Media Server + container: plex + widget: + type: plex + url: http://plex:32400 + key: {{HOMEPAGE_VAR_PLEX_KEY}} + + - Jellyfin: + icon: jellyfin.png + href: https://jellyfin.{{HOMEPAGE_VAR_DOMAIN}} + description: Open Source Media Server + container: jellyfin + widget: + type: jellyfin + url: http://jellyfin:8096 + key: {{HOMEPAGE_VAR_JELLYFIN_KEY}} + + - Jellyseerr: + icon: jellyseerr.png + href: https://jellyseerr.{{HOMEPAGE_VAR_DOMAIN}} + description: Media Requests + container: jellyseerr + widget: + type: jellyseerr + url: http://jellyseerr:5055 + key: {{HOMEPAGE_VAR_JELLYSEERR_KEY}} + +# Media - Management +- Media Management: + - Sonarr: + icon: sonarr.png + href: https://sonarr.{{HOMEPAGE_VAR_DOMAIN}} + description: TV Show Management + container: sonarr + widget: + type: sonarr + url: http://sonarr:8989 + key: {{HOMEPAGE_VAR_SONARR_KEY}} + + - Radarr: + icon: radarr.png + href: https://radarr.{{HOMEPAGE_VAR_DOMAIN}} + description: Movie Management + container: radarr + widget: + type: radarr + url: http://radarr:7878 + key: {{HOMEPAGE_VAR_RADARR_KEY}} + + - Lidarr: + icon: lidarr.png + href: https://lidarr.{{HOMEPAGE_VAR_DOMAIN}} + description: Music Management + container: lidarr + widget: + type: lidarr + url: http://lidarr:8686 + key: {{HOMEPAGE_VAR_LIDARR_KEY}} + + - Readarr: + icon: readarr.png + href: https://readarr.{{HOMEPAGE_VAR_DOMAIN}} + description: Book Management + container: readarr + widget: + type: readarr + url: http://readarr:8787 + key: {{HOMEPAGE_VAR_READARR_KEY}} + + - Prowlarr: + icon: prowlarr.png + href: https://prowlarr.{{HOMEPAGE_VAR_DOMAIN}} + description: Indexer Manager + container: prowlarr + widget: + type: prowlarr + url: http://prowlarr:9696 + key: {{HOMEPAGE_VAR_PROWLARR_KEY}} + +# Downloads +- Downloads: + - qBittorrent: + icon: qbittorrent.png + href: https://qbit.{{HOMEPAGE_VAR_DOMAIN}} + description: Torrent Client (via VPN) + container: qbittorrent + widget: + type: qbittorrent + url: http://gluetun:8080 + username: {{HOMEPAGE_VAR_QBITTORRENT_USER}} + password: {{HOMEPAGE_VAR_QBITTORRENT_PASS}} + + - Gluetun: + icon: gluetun.png + href: http://gluetun:8000 + description: VPN Client (Surfshark) + container: gluetun + +# Books & Comics +- Books & Comics: + - Calibre-Web: + icon: calibre-web.png + href: https://calibre.{{HOMEPAGE_VAR_DOMAIN}} + description: Ebook Library + container: calibre-web + + - Lazy Librarian: + icon: lazylibrarian.png + href: https://lazylibrarian.{{HOMEPAGE_VAR_DOMAIN}} + description: Book Manager + container: lazylibrarian + + - Mylar3: + icon: mylar3.png + href: https://mylar.{{HOMEPAGE_VAR_DOMAIN}} + description: Comic Book Manager + container: mylar3 + +# Transcoding +- Transcoding: + - Tdarr: + icon: tdarr.png + href: https://tdarr.{{HOMEPAGE_VAR_DOMAIN}} + description: Distributed Transcoding + container: tdarr-server + widget: + type: tdarr + url: http://tdarr-server:8265 + + - Unmanic: + icon: unmanic.png + href: https://unmanic.{{HOMEPAGE_VAR_DOMAIN}} + description: Library Optimizer + container: unmanic + +# Home Automation +- Home Automation: + - Home Assistant: + icon: home-assistant.png + href: https://ha.{{HOMEPAGE_VAR_DOMAIN}} + description: Home Automation Hub + # Note: Uses host network, configure manually + widget: + type: homeassistant + url: http://{{HOMEPAGE_VAR_SERVER_IP}}:8123 + key: {{HOMEPAGE_VAR_HA_KEY}} + + - ESPHome: + icon: esphome.png + href: https://esphome.{{HOMEPAGE_VAR_DOMAIN}} + description: ESP Device Manager + container: esphome + + - Node-RED: + icon: node-red.png + href: https://nodered.{{HOMEPAGE_VAR_DOMAIN}} + description: Flow Automation + container: nodered + + - TasmoAdmin: + icon: tasmota.png + href: https://tasmoadmin.{{HOMEPAGE_VAR_DOMAIN}} + description: Tasmota Device Manager + container: tasmoadmin + + - Zigbee2MQTT: + icon: zigbee2mqtt.png + href: https://zigbee2mqtt.{{HOMEPAGE_VAR_DOMAIN}} + description: Zigbee Bridge + container: zigbee2mqtt + + - MotionEye: + icon: motioneye.png + href: https://motioneye.{{HOMEPAGE_VAR_DOMAIN}} + description: Video Surveillance + container: motioneye + +# Productivity +- Productivity: + - Nextcloud: + icon: nextcloud.png + href: https://nextcloud.{{HOMEPAGE_VAR_DOMAIN}} + description: File Sync & Share + container: nextcloud + widget: + type: nextcloud + url: http://nextcloud + username: {{HOMEPAGE_VAR_NEXTCLOUD_USER}} + password: {{HOMEPAGE_VAR_NEXTCLOUD_PASS}} + + - Mealie: + icon: mealie.png + href: https://mealie.{{HOMEPAGE_VAR_DOMAIN}} + description: Recipe Manager + container: mealie + + - Gitea: + icon: gitea.png + href: https://git.{{HOMEPAGE_VAR_DOMAIN}} + description: Git Service + container: gitea + + - Code Server: + icon: vscode.png + href: https://code.{{HOMEPAGE_VAR_DOMAIN}} + description: VS Code in Browser + container: code-server + +# Documentation +- Documentation: + - BookStack: + icon: bookstack.png + href: https://docs.{{HOMEPAGE_VAR_DOMAIN}} + description: Documentation Platform + container: bookstack + widget: + type: bookstack + url: http://bookstack + key: {{HOMEPAGE_VAR_BOOKSTACK_KEY}} + + - DokuWiki: + icon: dokuwiki.png + href: https://wiki.{{HOMEPAGE_VAR_DOMAIN}} + description: File-based Wiki + container: dokuwiki + + - MediaWiki: + icon: mediawiki.png + href: https://mediawiki.{{HOMEPAGE_VAR_DOMAIN}} + description: Wiki Platform + container: mediawiki + + - WordPress: + icon: wordpress.png + href: https://blog.{{HOMEPAGE_VAR_DOMAIN}} + description: Blog Platform + container: wordpress + +# Backups & Monitoring +- Backups & Tools: + - Backrest: + icon: backrest.png + href: https://backrest.{{HOMEPAGE_VAR_DOMAIN}} + description: Backup Manager (Restic) + container: backrest + + - Duplicati: + icon: duplicati.png + href: https://duplicati.{{HOMEPAGE_VAR_DOMAIN}} + description: Backup Software + container: duplicati + +# Monitoring Stack +- Monitoring: + - Grafana: + icon: grafana.png + href: https://grafana.{{HOMEPAGE_VAR_DOMAIN}} + description: Metrics Visualization + container: grafana + widget: + type: grafana + url: http://grafana:3000 + username: {{HOMEPAGE_VAR_GRAFANA_USER}} + password: {{HOMEPAGE_VAR_GRAFANA_PASS}} + + - Prometheus: + icon: prometheus.png + href: https://prometheus.{{HOMEPAGE_VAR_DOMAIN}} + description: Metrics Collection + container: prometheus + widget: + type: prometheus + url: http://prometheus:9090 + + - Uptime Kuma: + icon: uptime-kuma.png + href: https://status.{{HOMEPAGE_VAR_DOMAIN}} + description: Status Page + container: uptime-kuma diff --git a/config-templates/homepage/settings.yaml b/config-templates/homepage/settings.yaml new file mode 100644 index 0000000..6fc2f89 --- /dev/null +++ b/config-templates/homepage/settings.yaml @@ -0,0 +1,63 @@ +# Homepage Configuration - Settings +# Copy to /opt/stacks/homepage/config/settings.yaml + +--- +title: Homelab Dashboard +background: https://images.unsplash.com/photo-1558591710-4b4a1ae0f04d +backgroundOpacity: 0.2 +theme: dark +color: slate +headerStyle: boxed +hideVersion: true +hideErrors: false +showStats: true +target: _self # Open links in same tab + +# Layout configuration +layout: + Infrastructure: + style: row + columns: 4 + Dashboards: + style: row + columns: 2 + Media Streaming: + style: row + columns: 3 + Media Management: + style: row + columns: 4 + Downloads: + style: row + columns: 2 + Books & Comics: + style: row + columns: 3 + Home Automation: + style: row + columns: 4 + Productivity: + style: row + columns: 4 + Documentation: + style: row + columns: 4 + Backups & Tools: + style: row + columns: 2 + Monitoring: + style: row + columns: 3 + +# Quick search +quicklaunch: + searchDescriptions: true + hideInternetSearch: false + showSearchSuggestions: true + +# Providers for additional functionality +providers: + longhorn: + url: http://longhorn:9500 + openweathermap: {{HOMEPAGE_VAR_OPENWEATHER_KEY}} + weatherapi: {{HOMEPAGE_VAR_WEATHERAPI_KEY}} diff --git a/config-templates/homepage/widgets.yaml b/config-templates/homepage/widgets.yaml new file mode 100644 index 0000000..48ad40d --- /dev/null +++ b/config-templates/homepage/widgets.yaml @@ -0,0 +1,49 @@ +# Homepage Configuration - Widgets +# Copy to /opt/stacks/homepage/config/widgets.yaml +# Displays system resources and other information + +--- +- logo: + icon: https://avatars.githubusercontent.com/u/... # Your logo here + +- search: + provider: google + target: _blank + +- datetime: + text_size: xl + format: + dateStyle: long + timeStyle: short + hourCycle: h23 + +- resources: + label: System + cpu: true + memory: true + disk: / + cputemp: true + uptime: true + units: metric + +- resources: + label: Storage + disk: /mnt/media + expanded: true + +- resources: + label: Downloads + disk: /mnt/downloads + expanded: true + +- openmeteo: + label: Weather + latitude: 40.7128 + longitude: -74.0060 + units: metric + cache: 5 + +- unifi_console: + url: http://unifi:8443 + username: {{HOMEPAGE_VAR_UNIFI_USER}} + password: {{HOMEPAGE_VAR_UNIFI_PASS}} diff --git a/docker-compose/dashboards.yml b/docker-compose/dashboards.yml new file mode 100644 index 0000000..2e83333 --- /dev/null +++ b/docker-compose/dashboards.yml @@ -0,0 +1,66 @@ +# Dashboard Services +# Homepage and Homarr for homelab dashboards +# Place in /opt/stacks/dashboards/docker-compose.yml + +services: + # Homepage - Application dashboard (AI-configurable via YAML) + # Access at: https://home.${DOMAIN} + homepage: + image: ghcr.io/gethomepage/homepage:latest + container_name: homepage + restart: unless-stopped + networks: + - homelab-network + - traefik-network + - dockerproxy-network + volumes: + - /opt/stacks/homepage/config:/app/config + - /var/run/docker.sock:/var/run/docker.sock:ro # For Docker integration + - /opt/stacks:/opt/stacks:ro # To discover other stacks + environment: + - PUID=${PUID:-1000} + - PGID=${PGID:-1000} + - TZ=${TZ} + 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.certresolver=letsencrypt" + - "traefik.http.services.homepage.loadbalancer.server.port=3000" + # No Authelia - make it the default landing page + + # Homarr - Modern dashboard + # Access at: https://homarr.${DOMAIN} + homarr: + image: ghcr.io/ajnart/homarr:latest + container_name: homarr + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/homarr/configs:/app/data/configs + - /opt/stacks/homarr/data:/data + - /opt/stacks/homarr/icons:/app/public/icons + - /var/run/docker.sock:/var/run/docker.sock:ro + 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.certresolver=letsencrypt" + - "traefik.http.services.homarr.loadbalancer.server.port=7575" + # No Authelia - dashboard should be accessible + +networks: + homelab-network: + external: true + traefik-network: + external: true + dockerproxy-network: + external: true diff --git a/docker-compose/homeassistant.yml b/docker-compose/homeassistant.yml new file mode 100644 index 0000000..199330e --- /dev/null +++ b/docker-compose/homeassistant.yml @@ -0,0 +1,173 @@ +# Home Assistant and IoT Services +# Home automation platform and related tools +# Place in /opt/stacks/homeassistant/docker-compose.yml + +services: + # Home Assistant - Home automation platform + # Access at: https://ha.${DOMAIN} + # NOTE: No Authelia - HA has its own authentication + homeassistant: + image: ghcr.io/home-assistant/home-assistant:2024.1 + container_name: homeassistant + restart: unless-stopped + network_mode: host # Required for device discovery + volumes: + - /opt/stacks/homeassistant/config:/config + - /etc/localtime:/etc/localtime:ro + environment: + - TZ=${TZ} + privileged: true + labels: + - "homelab.category=iot" + - "homelab.description=Home automation platform" + # Note: network_mode: host means Traefik can't proxy this directly + # Use Traefik's file provider or external host routing + + # ESPHome - ESP8266/ESP32 firmware manager + # Access at: https://esphome.${DOMAIN} + esphome: + image: ghcr.io/esphome/esphome:latest + container_name: esphome + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/esphome/config:/config + - /etc/localtime:/etc/localtime:ro + environment: + - TZ=${TZ} + - ESPHOME_DASHBOARD_USE_PING=true + privileged: true # For USB device access + labels: + - "homelab.category=iot" + - "homelab.description=ESP8266/ESP32 firmware manager" + - "traefik.enable=true" + - "traefik.http.routers.esphome.rule=Host(`esphome.${DOMAIN}`)" + - "traefik.http.routers.esphome.entrypoints=websecure" + - "traefik.http.routers.esphome.tls.certresolver=letsencrypt" + - "traefik.http.routers.esphome.middlewares=authelia@docker" + - "traefik.http.services.esphome.loadbalancer.server.port=6052" + + # TasmoAdmin - Tasmota device manager + # Access at: https://tasmoadmin.${DOMAIN} + tasmoadmin: + image: ghcr.io/tasmoadmin/tasmoadmin:latest + container_name: tasmoadmin + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/tasmoadmin/data:/data + environment: + - TZ=${TZ} + labels: + - "homelab.category=iot" + - "homelab.description=Tasmota device management" + - "traefik.enable=true" + - "traefik.http.routers.tasmoadmin.rule=Host(`tasmoadmin.${DOMAIN}`)" + - "traefik.http.routers.tasmoadmin.entrypoints=websecure" + - "traefik.http.routers.tasmoadmin.tls.certresolver=letsencrypt" + - "traefik.http.routers.tasmoadmin.middlewares=authelia@docker" + - "traefik.http.services.tasmoadmin.loadbalancer.server.port=80" + + # MotionEye - Video surveillance + # Access at: https://motioneye.${DOMAIN} + motioneye: + image: ccrisan/motioneye:master-amd64 + container_name: motioneye + restart: unless-stopped + networks: + - homelab-network + - traefik-network + ports: + - "8765:8765" # Optional: direct access + volumes: + - /opt/stacks/motioneye/config:/etc/motioneye + - /mnt/surveillance:/var/lib/motioneye # Large video files on separate drive + environment: + - TZ=${TZ} + labels: + - "homelab.category=iot" + - "homelab.description=Video surveillance system" + - "traefik.enable=true" + - "traefik.http.routers.motioneye.rule=Host(`motioneye.${DOMAIN}`)" + - "traefik.http.routers.motioneye.entrypoints=websecure" + - "traefik.http.routers.motioneye.tls.certresolver=letsencrypt" + - "traefik.http.routers.motioneye.middlewares=authelia@docker" + - "traefik.http.services.motioneye.loadbalancer.server.port=8765" + + # Node-RED - Flow-based automation (Home Assistant addon alternative) + # Access at: https://nodered.${DOMAIN} + nodered: + image: nodered/node-red:latest + container_name: nodered + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/nodered/data:/data + environment: + - TZ=${TZ} + labels: + - "homelab.category=iot" + - "homelab.description=Flow-based automation programming" + - "traefik.enable=true" + - "traefik.http.routers.nodered.rule=Host(`nodered.${DOMAIN}`)" + - "traefik.http.routers.nodered.entrypoints=websecure" + - "traefik.http.routers.nodered.tls.certresolver=letsencrypt" + - "traefik.http.routers.nodered.middlewares=authelia@docker" + - "traefik.http.services.nodered.loadbalancer.server.port=1880" + + # Mosquitto - MQTT broker (Home Assistant addon alternative) + # Used by: Home Assistant, ESPHome, Tasmota devices + mosquitto: + image: eclipse-mosquitto:latest + container_name: mosquitto + restart: unless-stopped + networks: + - homelab-network + ports: + - "1883:1883" # MQTT + - "9001:9001" # Websockets + volumes: + - /opt/stacks/mosquitto/config:/mosquitto/config + - /opt/stacks/mosquitto/data:/mosquitto/data + - /opt/stacks/mosquitto/log:/mosquitto/log + labels: + - "homelab.category=iot" + - "homelab.description=MQTT message broker" + + # Zigbee2MQTT - Zigbee to MQTT bridge (Home Assistant addon alternative) + # Access at: https://zigbee2mqtt.${DOMAIN} + zigbee2mqtt: + image: koenkk/zigbee2mqtt:latest + container_name: zigbee2mqtt + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/zigbee2mqtt/data:/app/data + - /run/udev:/run/udev:ro + devices: + - /dev/ttyACM0:/dev/ttyACM0 # Zigbee adapter - adjust as needed + environment: + - TZ=${TZ} + labels: + - "homelab.category=iot" + - "homelab.description=Zigbee to MQTT bridge" + - "traefik.enable=true" + - "traefik.http.routers.zigbee2mqtt.rule=Host(`zigbee2mqtt.${DOMAIN}`)" + - "traefik.http.routers.zigbee2mqtt.entrypoints=websecure" + - "traefik.http.routers.zigbee2mqtt.tls.certresolver=letsencrypt" + - "traefik.http.routers.zigbee2mqtt.middlewares=authelia@docker" + - "traefik.http.services.zigbee2mqtt.loadbalancer.server.port=8080" + +networks: + homelab-network: + external: true + traefik-network: + external: true diff --git a/docker-compose/infrastructure.yml b/docker-compose/infrastructure.yml index 2e628d5..8a7d752 100644 --- a/docker-compose/infrastructure.yml +++ b/docker-compose/infrastructure.yml @@ -1,11 +1,63 @@ # Infrastructure Services # Core services that other services depend on -# NOTE: Traefik, Authelia, DuckDNS, and Gluetun have their own compose files -# See traefik.yml, authelia.yml, duckdns.yml, and gluetun.yml +# 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. services: + # Dockge - Docker Compose Stack Manager (PRIMARY - preferred over Portainer) + # Access at: https://dockge.${DOMAIN} + dockge: + image: louislam/dockge:1 + 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 + 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.certresolver=letsencrypt" + - "traefik.http.routers.dockge.middlewares=authelia@docker" + - "traefik.http.services.dockge.loadbalancer.server.port=5001" + + # Portainer - Docker management UI (SECONDARY - use Dockge instead) + # Access at: https://portainer.${DOMAIN} + portainer: + image: portainer/portainer-ce:2.19.4 + container_name: portainer + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - portainer-data:/data + security_opt: + - no-new-privileges:true + labels: + - "homelab.category=infrastructure" + - "homelab.description=Docker container management UI (SECONDARY)" + - "traefik.enable=true" + - "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)" + - "traefik.http.routers.portainer.entrypoints=websecure" + - "traefik.http.routers.portainer.tls.certresolver=letsencrypt" + - "traefik.http.routers.portainer.middlewares=authelia@docker" + - "traefik.http.services.portainer.loadbalancer.server.port=9000" + # Pi-hole - Network-wide ad blocker and DNS server - # Access at: http://server-ip:8080/admin or https://pihole.yourdomain.duckdns.org + # Access at: https://pihole.${DOMAIN} pihole: image: pihole/pihole:2024.01.0 container_name: pihole @@ -16,7 +68,6 @@ services: ports: - "53:53/tcp" # DNS TCP - "53:53/udp" # DNS UDP - - "8082:80/tcp" # Web interface (changed from 8080 to avoid conflicts) volumes: - /opt/stacks/pihole/etc-pihole:/etc/pihole - /opt/stacks/pihole/etc-dnsmasq.d:/etc/dnsmasq.d @@ -32,57 +83,12 @@ services: labels: - "homelab.category=infrastructure" - "homelab.description=Network-wide ad blocking and DNS" - # Traefik labels - "traefik.enable=true" - "traefik.http.routers.pihole.rule=Host(`pihole.${DOMAIN}`)" - "traefik.http.routers.pihole.entrypoints=websecure" - "traefik.http.routers.pihole.tls.certresolver=letsencrypt" - "traefik.http.routers.pihole.middlewares=authelia@docker" - "traefik.http.services.pihole.loadbalancer.server.port=80" - ports: - - "53:53/tcp" # DNS TCP - - "53:53/udp" # DNS UDP - - "8080:80/tcp" # Web interface - volumes: - - ./config/pihole/etc-pihole:/etc/pihole - - ./config/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" - - # Portainer - Docker management UI - # Access at: https://portainer.yourdomain.duckdns.org - portainer: - image: portainer/portainer-ce:2.19.4 - container_name: portainer - restart: unless-stopped - networks: - - homelab-network - - traefik-network - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - portainer-data:/data - security_opt: - - no-new-privileges:true - labels: - - "homelab.category=infrastructure" - - "homelab.description=Docker container management UI" - # Traefik labels - - "traefik.enable=true" - - "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)" - - "traefik.http.routers.portainer.entrypoints=websecure" - - "traefik.http.routers.portainer.tls.certresolver=letsencrypt" - - "traefik.http.routers.portainer.middlewares=authelia@docker" - - "traefik.http.services.portainer.loadbalancer.server.port=9000" # Watchtower - Automatic container updates # Runs silently in background, no UI @@ -104,6 +110,78 @@ services: - "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 + 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.certresolver=letsencrypt" + - "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 + container_name: glances + restart: unless-stopped + networks: + - homelab-network + - traefik-network + pid: host + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /opt/stacks/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.certresolver=letsencrypt" + - "traefik.http.routers.glances.middlewares=authelia@docker" + - "traefik.http.services.glances.loadbalancer.server.port=61208" + volumes: portainer-data: driver: local @@ -113,3 +191,5 @@ networks: external: true traefik-network: external: true + dockerproxy-network: + driver: bridge diff --git a/docker-compose/media-extended.yml b/docker-compose/media-extended.yml new file mode 100644 index 0000000..78997b6 --- /dev/null +++ b/docker-compose/media-extended.yml @@ -0,0 +1,282 @@ +# Extended Media Services +# Additional media management tools +# Place in /opt/stacks/media-extended/docker-compose.yml + +services: + # Readarr - Ebook and audiobook management + # Access at: https://readarr.${DOMAIN} + readarr: + image: lscr.io/linuxserver/readarr:develop + container_name: readarr + restart: unless-stopped + networks: + - media-network + - homelab-network + - traefik-network + volumes: + - /opt/stacks/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: lscr.io/linuxserver/lidarr:latest + container_name: lidarr + restart: unless-stopped + networks: + - media-network + - homelab-network + - traefik-network + volumes: + - /opt/stacks/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: lscr.io/linuxserver/lazylibrarian:latest + container_name: lazylibrarian + restart: unless-stopped + networks: + - media-network + - homelab-network + - traefik-network + volumes: + - /opt/stacks/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: lscr.io/linuxserver/mylar3:latest + container_name: mylar3 + restart: unless-stopped + networks: + - media-network + - homelab-network + - traefik-network + volumes: + - /opt/stacks/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" + + # 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: + - /opt/stacks/calibre-web/config:/config + - /mnt/media/books:/books + environment: + - PUID=${PUID:-1000} + - PGID=${PGID:-1000} + - TZ=${TZ} + - 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" + + # 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: + - /opt/stacks/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.services.jellyseerr.loadbalancer.server.port=5055" + # No Authelia - users should be able to request easily + + # FlareSolverr - Cloudflare bypass for Prowlarr + # No web UI - used by Prowlarr + flaresolverr: + image: ghcr.io/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: + - /opt/stacks/tdarr/server:/app/server + - /opt/stacks/tdarr/configs:/app/configs + - /opt/stacks/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: + - /opt/stacks/tdarr/configs:/app/configs + - /opt/stacks/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: + - /opt/stacks/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 diff --git a/docker-compose/productivity.yml b/docker-compose/productivity.yml new file mode 100644 index 0000000..5d85fe3 --- /dev/null +++ b/docker-compose/productivity.yml @@ -0,0 +1,324 @@ +# Productivity and Content Management Services +# Place in /opt/stacks/productivity/docker-compose.yml + +services: + # Nextcloud - File sync and collaboration + # Access at: https://nextcloud.${DOMAIN} + nextcloud: + image: nextcloud:latest + container_name: nextcloud + restart: unless-stopped + networks: + - homelab-network + - traefik-network + - nextcloud-network + volumes: + - /opt/stacks/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=${DOMAIN} + - TRUSTED_PROXIES=172.18.0.0/16 + - OVERWRITEPROTOCOL=https + 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: + - /opt/stacks/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: + - /opt/stacks/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(`blog.${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: + - /opt/stacks/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(`git.${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: + - /opt/stacks/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(`wiki.${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: + - /opt/stacks/bookstack/config:/config + environment: + - PUID=${PUID:-1000} + - PGID=${PGID:-1000} + - APP_URL=https://docs.${DOMAIN} + - DB_HOST=bookstack-db + - DB_PORT=3306 + - DB_DATABASE=bookstack + - DB_USERNAME=bookstack + - DB_PASSWORD=${BOOKSTACK_DB_PASSWORD} + depends_on: + - bookstack-db + labels: + - "homelab.category=productivity" + - "homelab.description=Documentation and wiki platform" + - "traefik.enable=true" + - "traefik.http.routers.bookstack.rule=Host(`docs.${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" + + 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: + - /opt/stacks/mediawiki/images:/var/www/html/images + - /opt/stacks/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 diff --git a/docker-compose/utilities.yml b/docker-compose/utilities.yml new file mode 100644 index 0000000..95003ee --- /dev/null +++ b/docker-compose/utilities.yml @@ -0,0 +1,177 @@ +# Backup and Utility Services +# Place in /opt/stacks/utilities/docker-compose.yml + +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: + - /opt/stacks/backrest/data:/data + - /opt/stacks/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" + + # Duplicati - Backup solution + # Access at: https://duplicati.${DOMAIN} + duplicati: + image: lscr.io/linuxserver/duplicati:latest + container_name: duplicati + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/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" + + # Uptime Kuma - Status monitoring + # Access at: https://status.${DOMAIN} + uptime-kuma: + image: louislam/uptime-kuma:1 + container_name: uptime-kuma + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/uptime-kuma/data:/app/data + - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + - "homelab.category=utilities" + - "homelab.description=Uptime monitoring and status page" + - "traefik.enable=true" + - "traefik.http.routers.uptime-kuma.rule=Host(`status.${DOMAIN}`)" + - "traefik.http.routers.uptime-kuma.entrypoints=websecure" + - "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt" + - "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001" + # No Authelia - public status page + + # Code Server - VS Code in browser + # Access at: https://code.${DOMAIN} + code-server: + image: lscr.io/linuxserver/code-server:latest + container_name: code-server + restart: unless-stopped + networks: + - homelab-network + - traefik-network + volumes: + - /opt/stacks/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=utilities" + - "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" + + # Form.io - Form builder (if needed) + # Access at: https://forms.${DOMAIN} + 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 + 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" + + # Authelia Redis - Session storage for Authelia + # No web UI - backend service + authelia-redis: + image: redis: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 diff --git a/docs/proxying-external-hosts.md b/docs/proxying-external-hosts.md new file mode 100644 index 0000000..5e5d3d0 --- /dev/null +++ b/docs/proxying-external-hosts.md @@ -0,0 +1,379 @@ +# Proxying External Hosts with Traefik and Authelia + +This guide explains how to use Traefik and Authelia to proxy external services (like a Raspberry Pi running Home Assistant) through your domain with HTTPS and optional SSO protection. + +## Overview + +Traefik can proxy services that aren't running in Docker, such as: +- Home Assistant on a Raspberry Pi +- Other physical servers on your network +- Services running on different machines +- Any HTTP/HTTPS service accessible via IP:PORT + +## Method 1: Using Traefik File Provider (Recommended) + +### Step 1: Create External Service Configuration + +Create a file in `/opt/stacks/traefik/dynamic/external-hosts.yml`: + +```yaml +http: + routers: + # Home Assistant on Raspberry Pi + homeassistant-external: + rule: "Host(`ha.yourdomain.duckdns.org`)" + entryPoints: + - websecure + service: homeassistant-external + tls: + certResolver: letsencrypt + # Uncomment to add Authelia protection: + # middlewares: + # - authelia@docker + + services: + homeassistant-external: + loadBalancer: + servers: + - url: "http://192.168.1.50:8123" # Replace with your Pi's IP and port + passHostHeader: true + + middlewares: + # Optional: Add headers for WebSocket support + homeassistant-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + customResponseHeaders: + X-Frame-Options: "SAMEORIGIN" +``` + +### Step 2: Reload Traefik + +Traefik watches the `/opt/stacks/traefik/dynamic/` directory automatically and reloads configurations: + +```bash +# Verify configuration is loaded +docker logs traefik | grep external-hosts + +# If needed, restart Traefik +cd /opt/stacks/traefik +docker compose restart +``` + +### Step 3: Test Access + +Visit `https://ha.yourdomain.duckdns.org` - Traefik will: +1. Accept the HTTPS connection +2. Proxy the request to `http://192.168.1.50:8123` +3. Return the response with proper SSL +4. (Optionally) Require Authelia login if middleware is configured + +## Method 2: Using Docker Labels (Dummy Container) + +If you prefer managing routes via Docker labels (so the AI can modify them), create a dummy container: + +### Create a Label Container + +In `/opt/stacks/external-proxies/docker-compose.yml`: + +```yaml +services: + # Dummy container for Raspberry Pi Home Assistant + homeassistant-proxy-labels: + image: alpine:latest + container_name: homeassistant-proxy-labels + command: tail -f /dev/null # Keep container running + restart: unless-stopped + networks: + - traefik-network + labels: + - "traefik.enable=true" + - "traefik.http.routers.ha-external.rule=Host(`ha.${DOMAIN}`)" + - "traefik.http.routers.ha-external.entrypoints=websecure" + - "traefik.http.routers.ha-external.tls.certresolver=letsencrypt" + # Point to external service + - "traefik.http.services.ha-external.loadbalancer.server.url=http://192.168.1.50:8123" + # Optional: Add Authelia (usually not for HA) + # - "traefik.http.routers.ha-external.middlewares=authelia@docker" + +networks: + traefik-network: + external: true +``` + +Deploy: +```bash +cd /opt/stacks/external-proxies +docker compose up -d +``` + +## Method 3: Hybrid Approach (File + Docker Discovery) + +Combine both methods for maximum flexibility: +- Use file provider for static external hosts +- Use Docker labels for frequently changing services +- AI can manage both! + +## Common External Services to Proxy + +### Home Assistant (Raspberry Pi) +```yaml +homeassistant-pi: + rule: "Host(`ha.yourdomain.duckdns.org`)" + service: http://192.168.1.50:8123 + # No Authelia - HA has its own auth +``` + +### Router/Firewall Admin Panel +```yaml +router-admin: + rule: "Host(`router.yourdomain.duckdns.org`)" + service: http://192.168.1.1:80 + middlewares: + - authelia@docker # Add SSO protection +``` + +### Proxmox Server +```yaml +proxmox: + rule: "Host(`proxmox.yourdomain.duckdns.org`)" + service: https://192.168.1.100:8006 + middlewares: + - authelia@docker + # Note: Use https:// if backend uses HTTPS +``` + +### TrueNAS/FreeNAS +```yaml +truenas: + rule: "Host(`nas.yourdomain.duckdns.org`)" + service: http://192.168.1.200:80 + middlewares: + - authelia@docker +``` + +### Security Camera NVR +```yaml +nvr: + rule: "Host(`cameras.yourdomain.duckdns.org`)" + service: http://192.168.1.10:80 + middlewares: + - authelia@docker # Definitely protect cameras! +``` + +## Advanced Configuration + +### WebSocket Support + +Some services (like Home Assistant) need WebSocket support: + +```yaml +http: + middlewares: + websocket-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + Connection: "upgrade" + Upgrade: "websocket" + + routers: + homeassistant-external: + middlewares: + - websocket-headers +``` + +### HTTPS Backend + +If your external service already uses HTTPS: + +```yaml +http: + services: + https-backend: + loadBalancer: + servers: + - url: "https://192.168.1.50:8123" + serversTransport: insecureTransport + + serversTransports: + insecureTransport: + insecureSkipVerify: true # Only if using self-signed cert +``` + +### IP Whitelist + +Restrict access to specific IPs: + +```yaml +http: + middlewares: + local-only: + ipWhiteList: + sourceRange: + - "192.168.1.0/24" + - "10.0.0.0/8" + + routers: + sensitive-service: + middlewares: + - local-only + - authelia@docker +``` + +## Authelia Bypass Rules + +Configure Authelia to bypass authentication for specific external hosts. + +Edit `/opt/stacks/authelia/configuration.yml`: + +```yaml +access_control: + rules: + # Bypass for Home Assistant (app access) + - domain: ha.yourdomain.duckdns.org + policy: bypass + + # Require auth for router admin + - domain: router.yourdomain.duckdns.org + policy: one_factor + + # Two-factor for critical services + - domain: proxmox.yourdomain.duckdns.org + policy: two_factor +``` + +## DNS Configuration + +Ensure your DuckDNS domain points to your public IP: + +1. DuckDNS container automatically updates your IP +2. Port forward 80 and 443 to your Traefik server +3. All subdomains (`*.yourdomain.duckdns.org`) point to same IP +4. Traefik routes based on Host header + +## Troubleshooting + +### Check Traefik Routing +```bash +# View active routes +docker logs traefik | grep "Creating router" + +# Check if external host route is loaded +docker logs traefik | grep homeassistant + +# View Traefik dashboard +# Visit: https://traefik.yourdomain.duckdns.org +``` + +### Test Without SSL +```bash +# Temporarily test direct connection +curl -H "Host: ha.yourdomain.duckdns.org" http://localhost/ +``` + +### Check Authelia Logs +```bash +cd /opt/stacks/authelia +docker compose logs -f authelia +``` + +### Verify External Service +```bash +# Test that external service is reachable +curl http://192.168.1.50:8123 +``` + +## AI Management + +The AI can manage external host proxying by: + +1. **Reading existing configurations**: Parse `/opt/stacks/traefik/dynamic/*.yml` +2. **Adding new routes**: Create/update YAML files in dynamic directory +3. **Modifying Docker labels**: Update dummy container labels +4. **Configuring Authelia rules**: Edit `configuration.yml` for bypass/require auth +5. **Testing connectivity**: Suggest verification steps + +Example AI prompt: +> "Add proxying for my Unifi Controller at 192.168.1.5:8443 with Authelia protection" + +AI will: +1. Create route configuration file +2. Add HTTPS backend support (Unifi uses HTTPS) +3. Configure Authelia middleware +4. Add to Homepage dashboard +5. Provide testing instructions + +## Security Best Practices + +1. **Always use Authelia** for admin interfaces (routers, NAS, etc.) +2. **Bypass Authelia** only for services with their own auth (HA, Plex) +3. **Use IP whitelist** for highly sensitive services +4. **Enable two-factor** for critical infrastructure +5. **Monitor access logs** in Traefik and Authelia +6. **Keep services updated** - Traefik, Authelia, and external services + +## Example: Complete External Host Setup + +Let's proxy a Raspberry Pi Home Assistant: + +1. **Traefik configuration** (`/opt/stacks/traefik/dynamic/raspberry-pi.yml`): +```yaml +http: + routers: + ha-pi: + rule: "Host(`ha.yourdomain.duckdns.org`)" + entryPoints: + - websecure + service: ha-pi + tls: + certResolver: letsencrypt + middlewares: + - ha-headers + + services: + ha-pi: + loadBalancer: + servers: + - url: "http://192.168.1.50:8123" + + middlewares: + ha-headers: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" +``` + +2. **Authelia bypass** (in `/opt/stacks/authelia/configuration.yml`): +```yaml +access_control: + rules: + - domain: ha.yourdomain.duckdns.org + policy: bypass +``` + +3. **Homepage entry** (in `/opt/stacks/homepage/config/services.yaml`): +```yaml +- Home Automation: + - Home Assistant (Pi): + icon: home-assistant.png + href: https://ha.yourdomain.duckdns.org + description: HA on Raspberry Pi + ping: 192.168.1.50 + widget: + type: homeassistant + url: http://192.168.1.50:8123 + key: your-long-lived-token +``` + +4. **Test**: +```bash +# Reload Traefik (automatic, but verify) +docker logs traefik | grep ha-pi + +# Visit +https://ha.yourdomain.duckdns.org +``` + +Done! Your Raspberry Pi Home Assistant is now accessible via your domain with HTTPS. 🎉