Add comprehensive service stacks: Dockge, Homepage, Home Assistant, and all user services
- Add Dockge to infrastructure (primary over Portainer) - Create dashboards.yml with Homepage and Homarr (AI-configurable) - Create homeassistant.yml with HA, ESPHome, TasmoAdmin, Node-RED, Mosquitto, Zigbee2MQTT, MotionEye - Create media-extended.yml with Readarr, Lidarr, Lazy Librarian, Mylar3, Calibre-Web, Jellyseerr, FlareSolverr, Tdarr, Unmanic - Create productivity.yml with Nextcloud, Mealie, WordPress, Gitea, DokuWiki, BookStack, MediaWiki (all with databases) - Create utilities.yml with Backrest, Duplicati, Uptime Kuma, Code Server, Form.io, Authelia Redis - Add Homepage configuration templates (services.yaml, docker.yaml, settings.yaml, widgets.yaml) - All services include container names for Docker integration - Widgets configured for services that support them (Sonarr, Radarr, Plex, Jellyfin, etc.) - Organized by category with proper layouts - Create docs/proxying-external-hosts.md - comprehensive guide for proxying Raspberry Pi and other external hosts via Traefik - Update .env.example with all new service credentials and Homepage API keys - Update infrastructure.yml to prioritize Dockge, add Dozzle, Glances, Docker Proxy - All services configured with /opt/stacks paths, Traefik labels, and appropriate Authelia middleware Co-authored-by: kelinfoxy <67766943+kelinfoxy@users.noreply.github.com>
This commit is contained in:
96
.env.example
96
.env.example
@@ -47,6 +47,102 @@ VPN_COUNTRY=Netherlands # Preferred VPN server location
|
|||||||
# SURFSHARK_USERNAME=your-surfshark-username
|
# SURFSHARK_USERNAME=your-surfshark-username
|
||||||
# SURFSHARK_PASSWORD=your-surfshark-password
|
# 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 Configuration
|
||||||
PLEX_CLAIM=claim-xxxxxxxxxx
|
PLEX_CLAIM=claim-xxxxxxxxxx
|
||||||
|
|
||||||
|
|||||||
13
config-templates/homepage/docker.yaml
Normal file
13
config-templates/homepage/docker.yaml
Normal file
@@ -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
|
||||||
373
config-templates/homepage/services.yaml
Normal file
373
config-templates/homepage/services.yaml
Normal file
@@ -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
|
||||||
63
config-templates/homepage/settings.yaml
Normal file
63
config-templates/homepage/settings.yaml
Normal file
@@ -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}}
|
||||||
49
config-templates/homepage/widgets.yaml
Normal file
49
config-templates/homepage/widgets.yaml
Normal file
@@ -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}}
|
||||||
66
docker-compose/dashboards.yml
Normal file
66
docker-compose/dashboards.yml
Normal file
@@ -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
|
||||||
173
docker-compose/homeassistant.yml
Normal file
173
docker-compose/homeassistant.yml
Normal file
@@ -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
|
||||||
@@ -1,11 +1,63 @@
|
|||||||
# Infrastructure Services
|
# Infrastructure Services
|
||||||
# Core services that other services depend on
|
# Core services that other services depend on
|
||||||
# NOTE: Traefik, Authelia, DuckDNS, and Gluetun have their own compose files
|
# Place in /opt/stacks/infrastructure/docker-compose.yml
|
||||||
# See traefik.yml, authelia.yml, duckdns.yml, and gluetun.yml
|
# NOTE: Traefik, Authelia, DuckDNS, and Gluetun have their own separate stacks
|
||||||
|
# See /opt/stacks/traefik/, /opt/stacks/authelia/, etc.
|
||||||
|
|
||||||
services:
|
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
|
# 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:
|
pihole:
|
||||||
image: pihole/pihole:2024.01.0
|
image: pihole/pihole:2024.01.0
|
||||||
container_name: pihole
|
container_name: pihole
|
||||||
@@ -16,7 +68,6 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "53:53/tcp" # DNS TCP
|
- "53:53/tcp" # DNS TCP
|
||||||
- "53:53/udp" # DNS UDP
|
- "53:53/udp" # DNS UDP
|
||||||
- "8082:80/tcp" # Web interface (changed from 8080 to avoid conflicts)
|
|
||||||
volumes:
|
volumes:
|
||||||
- /opt/stacks/pihole/etc-pihole:/etc/pihole
|
- /opt/stacks/pihole/etc-pihole:/etc/pihole
|
||||||
- /opt/stacks/pihole/etc-dnsmasq.d:/etc/dnsmasq.d
|
- /opt/stacks/pihole/etc-dnsmasq.d:/etc/dnsmasq.d
|
||||||
@@ -32,57 +83,12 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=infrastructure"
|
- "homelab.category=infrastructure"
|
||||||
- "homelab.description=Network-wide ad blocking and DNS"
|
- "homelab.description=Network-wide ad blocking and DNS"
|
||||||
# Traefik labels
|
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.pihole.rule=Host(`pihole.${DOMAIN}`)"
|
- "traefik.http.routers.pihole.rule=Host(`pihole.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.pihole.entrypoints=websecure"
|
- "traefik.http.routers.pihole.entrypoints=websecure"
|
||||||
- "traefik.http.routers.pihole.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.pihole.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.pihole.middlewares=authelia@docker"
|
- "traefik.http.routers.pihole.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.pihole.loadbalancer.server.port=80"
|
- "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
|
# Watchtower - Automatic container updates
|
||||||
# Runs silently in background, no UI
|
# Runs silently in background, no UI
|
||||||
@@ -104,6 +110,78 @@ services:
|
|||||||
- "homelab.category=infrastructure"
|
- "homelab.category=infrastructure"
|
||||||
- "homelab.description=Automatic Docker container updates"
|
- "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:
|
volumes:
|
||||||
portainer-data:
|
portainer-data:
|
||||||
driver: local
|
driver: local
|
||||||
@@ -113,3 +191,5 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
traefik-network:
|
traefik-network:
|
||||||
external: true
|
external: true
|
||||||
|
dockerproxy-network:
|
||||||
|
driver: bridge
|
||||||
|
|||||||
282
docker-compose/media-extended.yml
Normal file
282
docker-compose/media-extended.yml
Normal file
@@ -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
|
||||||
324
docker-compose/productivity.yml
Normal file
324
docker-compose/productivity.yml
Normal file
@@ -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
|
||||||
177
docker-compose/utilities.yml
Normal file
177
docker-compose/utilities.yml
Normal file
@@ -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
|
||||||
379
docs/proxying-external-hosts.md
Normal file
379
docs/proxying-external-hosts.md
Normal file
@@ -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. 🎉
|
||||||
Reference in New Issue
Block a user