Implement Dockge structure with Traefik, Authelia, DuckDNS, and Gluetun VPN

- Update AI copilot instructions for /opt/stacks structure and automated config management
- Replace Nginx Proxy Manager with Traefik (file-based configuration for AI)
- Add Authelia for SSO with bypass rules for Jellyfin/Plex apps
- Add DuckDNS for dynamic DNS with Let's Encrypt integration
- Add Gluetun VPN with Surfshark (WireGuard) for secure downloads
- Update all services to use /opt/stacks paths instead of local directories
- Add Traefik labels to all services for automatic routing
- Configure qBittorrent to route through Gluetun VPN
- Update .env.example with all new required variables
- Create configuration templates for Traefik and Authelia
- Add comprehensive Dockge deployment guide

Co-authored-by: kelinfoxy <67766943+kelinfoxy@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-12 00:13:55 +00:00
parent 6083da7036
commit f9a34fe9c7
13 changed files with 1082 additions and 112 deletions

View File

@@ -12,12 +12,41 @@ TZ=America/New_York
# Server IP address
SERVER_IP=192.168.1.100
# Domain Configuration
DOMAIN=yourdomain.duckdns.org # Your DuckDNS domain
# Directory Paths
USERDIR=/home/username/homelab
MEDIADIR=/mnt/media
DOWNLOADDIR=/mnt/downloads
USERDIR=/opt/stacks
MEDIADIR=/mnt/media # Large media files on separate drive
DOWNLOADDIR=/mnt/downloads # Downloads on separate drive
PROJECTDIR=/home/username/projects
# DuckDNS Configuration
DUCKDNS_TOKEN=your-duckdns-token
DUCKDNS_SUBDOMAINS=yourdomain # Without .duckdns.org
# Let's Encrypt / ACME
ACME_EMAIL=your-email@example.com
# Authelia Secrets (generate with: openssl rand -hex 64)
AUTHELIA_JWT_SECRET=your-jwt-secret-here-64-chars
AUTHELIA_SESSION_SECRET=your-session-secret-here-64-chars
AUTHELIA_STORAGE_ENCRYPTION_KEY=your-encryption-key-here-64-chars
# SMTP for Authelia Notifications (optional)
SMTP_USERNAME=your-email@example.com
SMTP_PASSWORD=your-smtp-password
# VPN Configuration (Surfshark)
# 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
@@ -45,4 +74,7 @@ PIHOLE_PASSWORD=changeme
# Watchtower Notifications (optional)
# WATCHTOWER_NOTIFICATION_URL=
# Cloudflare API (optional, for DNS challenge)
# CF_DNS_API_TOKEN=your-cloudflare-api-token
# Add your own variables below

View File

@@ -1,26 +1,45 @@
# AI Homelab Management Assistant
You are an AI assistant specialized in managing Docker-based homelab infrastructure. Your role is to help users create, modify, and manage Docker services while maintaining consistency across the entire server stack.
You are an AI assistant specialized in managing Docker-based homelab infrastructure using Dockge. Your role is to help users create, modify, and manage Docker services while maintaining consistency across the entire server stack.
## Core Principles
### 1. Docker Compose First
### 1. Dockge and Docker Compose First
- **ALWAYS** use Docker Compose stacks for persistent services
- Store all compose files in `/opt/stacks/stack-name/` directories
- Only use `docker run` for temporary containers (e.g., testing nvidia-container-toolkit functionality)
- Maintain all services in organized docker-compose.yml files
- Maintain all services in organized docker-compose.yml files within their stack folders
### 2. Consistency is Key
### 2. File Structure and Storage
- **Base Path**: All stacks are stored in `/opt/stacks/`
- **Bind Mounts**: Default to `/opt/stacks/stack-name/` for configuration files
- **Large Data**: Suggest using separate mounted drives for:
- Media files (movies, TV shows, music) - typically `/mnt/media`
- Downloads - typically `/mnt/downloads`
- Database data files that grow large
- Backup storage
- Any data that may exceed 50GB or grow continuously
- **Named Volumes**: Use Docker named volumes for smaller application data
### 3. Consistency is Key
- Keep consistent naming conventions across all compose files
- Use the same network naming patterns
- Maintain uniform volume mount structures
- Apply consistent environment variable patterns
### 3. Stack-Aware Changes
### 4. Stack-Aware Changes
- Before making changes, consider the impact on the entire server stack
- Check for service dependencies (networks, volumes, other services)
- Ensure changes don't break existing integrations
- Validate that port assignments don't conflict
### 5. Automated Configuration Management
- Configure all services via configuration files, not web UIs
- Traefik routes configured via Docker labels
- Authelia rules configured via YAML files
- Enable AI to manage and update configurations automatically
- Maintain homelab functionality through code, not manual UI clicks
## Creating a New Docker Service
When creating a new service, follow these steps:
@@ -46,10 +65,12 @@ When creating a new service, follow these steps:
networks:
- homelab-network # Use shared networks
ports:
- "host_port:container_port" # Document port purpose
- "host_port:container_port" # Document port purpose (if not using Traefik)
volumes:
- ./config/service-name:/config # Config in local directory
- /opt/stacks/stack-name/config:/config # Config in stack directory
- service-data:/data # Named volumes for persistent data
# For large data, use separate mount:
# - /mnt/media:/media # Large media files on separate drive
environment:
- PUID=1000 # Standard user/group IDs
- PGID=1000
@@ -57,6 +78,13 @@ When creating a new service, follow these steps:
labels:
- "homelab.category=category-name" # For organization
- "homelab.description=Service description"
# Traefik labels (if using Traefik):
# - "traefik.enable=true"
# - "traefik.http.routers.service-name.rule=Host(`service.domain.com`)"
# - "traefik.http.routers.service-name.entrypoints=websecure"
# - "traefik.http.routers.service-name.tls.certresolver=letsencrypt"
# Authelia middleware (if SSO required):
# - "traefik.http.routers.service-name.middlewares=authelia@docker"
volumes:
service-data:
@@ -213,41 +241,220 @@ environment:
## File Organization
```
/home/user/homelab/
├── docker-compose/
│ ├── media.yml # Media server services
│ ├── monitoring.yml # Monitoring stack
│ ├── development.yml # Dev tools
│ └── infrastructure.yml # Core services
├── config/
│ ├── service1/
│ ├── service2/
── ...
├── data/ # Bind mount data
── ...
── .env # Global secrets (gitignored)
── README.md # Stack documentation
/opt/stacks/
├── stack-name/
│ ├── docker-compose.yml # Stack definition
│ ├── config/ # Service configurations
│ ├── .env # Stack-specific secrets
│ └── README.md # Stack documentation
├── traefik/
│ ├── docker-compose.yml
│ ├── traefik.yml # Traefik static config
── dynamic/ # Dynamic configuration
└── routes.yml # Route definitions
── acme.json # Let's Encrypt certificates
│ └── .env
── authelia/
│ ├── docker-compose.yml
│ ├── configuration.yml # Authelia config
│ ├── users_database.yml # User definitions
│ └── .env
├── gluetun/
│ ├── docker-compose.yml
│ └── .env # VPN credentials
└── duckdns/
├── docker-compose.yml
└── .env # DuckDNS token
```
## VPN Integration with Gluetun
### When to Use VPN
- Download clients (qBittorrent, SABnzbd, etc.)
- Services that need to hide their origin IP
- Services accessing geo-restricted content
### Gluetun Configuration
- **Default VPN**: Surfshark
- Services connect through Gluetun's network namespace
- Use `network_mode: "service:gluetun"` for VPN routing
- Access via Gluetun's ports: map ports in Gluetun service
**Example:**
```yaml
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=surfshark
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${SURFSHARK_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${SURFSHARK_ADDRESSES}
- SERVER_COUNTRIES=Netherlands
ports:
- 8080:8080 # qBittorrent web UI
- 6881:6881 # qBittorrent ports
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun" # Route through VPN
depends_on:
- gluetun
volumes:
- /opt/stacks/qbittorrent/config:/config
- /mnt/downloads:/downloads
```
## SSO with Authelia
### Authentication Strategy
- **Protected Services**: Most web UIs (require SSO login)
- **Bypass Services**: Apps that need direct access (Jellyfin, Plex, mobile apps)
- **API Endpoints**: Configure bypass rules for API access
### Authelia Configuration
- Users defined in `users_database.yml`
- Access rules in `configuration.yml`
- Integrate with Traefik via middleware
### Services Requiring Authelia
- Monitoring dashboards (Grafana, Prometheus, etc.)
- Admin panels (Portainer, etc.)
- Download clients web UIs
- Development tools
- Any service with sensitive data
### Services Bypassing Authelia
- Jellyfin (for app access - Roku, Fire TV, mobile apps)
- Plex (for app access)
- Home Assistant (has its own auth)
- Services with API-only access
- Public-facing services (if any)
**Example Traefik Labels with Authelia:**
```yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.sonarr.rule=Host(`sonarr.${DOMAIN}`)"
- "traefik.http.routers.sonarr.entrypoints=websecure"
- "traefik.http.routers.sonarr.tls.certresolver=letsencrypt"
- "traefik.http.routers.sonarr.middlewares=authelia@docker" # SSO enabled
```
**Example Bypassing Authelia (Jellyfin):**
```yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.${DOMAIN}`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
# No authelia middleware - direct access for apps
```
## Traefik Reverse Proxy
### Why Traefik Instead of Nginx Proxy Manager
- **File-based configuration**: AI can modify YAML files
- **Docker label integration**: Automatic service discovery
- **No web UI dependency**: Fully automated management
- **Let's Encrypt automation**: Automatic SSL certificate management
- **Dynamic configuration**: Changes without restarts
### Traefik Configuration Pattern
1. **Static config** (`traefik.yml`): Core settings, entry points, certificate resolvers
2. **Dynamic config** (Docker labels): Per-service routing rules
3. **File provider**: Additional route definitions in `dynamic/` directory
### Managing Routes via AI
- Traefik routes defined in Docker labels
- AI can read compose files and add/modify labels
- Automatic service discovery when containers start
- Update routes by modifying compose files and redeploying
**Example Service with Traefik:**
```yaml
services:
service-name:
image: service:latest
container_name: service-name
networks:
- traefik-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.service-name.rule=Host(`service.${DOMAIN}`)"
- "traefik.http.routers.service-name.entrypoints=websecure"
- "traefik.http.routers.service-name.tls.certresolver=letsencrypt"
- "traefik.http.routers.service-name.middlewares=authelia@docker"
- "traefik.http.services.service-name.loadbalancer.server.port=8080"
```
## DuckDNS for Dynamic DNS
### Purpose
- Provides dynamic DNS for home IP addresses
- Integrates with Let's Encrypt for SSL certificates
- Updates automatically when IP changes
### Configuration
- Single container updates your domain periodically
- Works with Traefik's Let's Encrypt resolver
- Set up once and forget
## Automated Homelab Management
### AI's Role in Maintenance
1. **Service Addition**: Create compose files with proper Traefik labels
2. **Route Management**: Update labels to modify proxy routes
3. **SSL Certificates**: Traefik handles automatically via Let's Encrypt
4. **SSO Configuration**: Add/remove authelia middleware as needed
5. **VPN Routing**: Configure services to use Gluetun when required
6. **Monitoring**: Ensure all services are properly configured
### Configuration Files AI Can Manage
- `docker-compose.yml` files for all stacks
- `traefik/dynamic/routes.yml` for custom routes
- `authelia/configuration.yml` for access rules
- Environment variables in `.env` files
- Service-specific config files in `/opt/stacks/stack-name/config/`
### What AI Should Monitor
- Port conflicts
- Network connectivity
- Certificate expiration (Traefik handles renewal)
- Service health
- VPN connection status
- Authentication bypass requirements
## Safety Checks
Before deploying any changes:
- [ ] YAML syntax is valid
- [ ] Ports don't conflict with existing services
- [ ] Networks exist or are defined
- [ ] Volume paths are correct
- [ ] Volume paths are correct (use /opt/stacks/ or /mnt/ for large data)
- [ ] Environment variables are set
- [ ] No secrets in compose files
- [ ] Service dependencies are met
- [ ] Backup of current configuration exists
- [ ] Traefik labels are correct for routing
- [ ] Authelia middleware applied appropriately
- [ ] VPN routing configured if needed
## Remember
- **Think before you act**: Consider the entire stack
- **Be consistent**: Follow established patterns
- **Use /opt/stacks/**: All compose files go in stack directories
- **Large data on /mnt/**: Media and downloads go on separate drives
- **Configure via files**: Traefik labels, Authelia YAML, not web UIs
- **Document everything**: Future you will thank you
- **Test safely**: Use temporary containers first
- **Back up first**: Always have a rollback plan
- **Security matters**: Keep secrets secret, update regularly
- **Security matters**: Use Authelia SSO, keep secrets in .env files
- **VPN when needed**: Route download clients through Gluetun
When a user asks you to create or modify a Docker service, follow these guidelines carefully, ask clarifying questions if needed, and always prioritize the stability and consistency of the entire homelab infrastructure.
When a user asks you to create or modify a Docker service, follow these guidelines carefully, ask clarifying questions if needed, and always prioritize the stability, security, and consistency of the entire homelab infrastructure.

View File

@@ -0,0 +1,93 @@
# Authelia Configuration
# Copy to /opt/stacks/authelia/configuration.yml
server:
host: 0.0.0.0
port: 9091
log:
level: info
theme: dark
jwt_secret: ${AUTHELIA_JWT_SECRET}
default_redirection_url: https://auth.${DOMAIN}
totp:
issuer: ${DOMAIN}
period: 30
skew: 1
authentication_backend:
file:
path: /config/users_database.yml
password:
algorithm: argon2id
iterations: 1
key_length: 32
salt_length: 16
memory: 1024
parallelism: 8
access_control:
default_policy: deny
rules:
# Bypass Authelia for Jellyfin (allow app access)
- domain: jellyfin.${DOMAIN}
policy: bypass
# Bypass for Plex (allow app access)
- domain: plex.${DOMAIN}
policy: bypass
# Bypass for Home Assistant (has its own auth)
- domain: ha.${DOMAIN}
policy: bypass
# Protected: All other services require authentication
- domain: "*.${DOMAIN}"
policy: one_factor
# Two-factor for admin services (optional)
# - domain:
# - "admin.${DOMAIN}"
# - "portainer.${DOMAIN}"
# policy: two_factor
session:
name: authelia_session
secret: ${AUTHELIA_SESSION_SECRET}
expiration: 1h
inactivity: 5m
remember_me_duration: 1M
domain: ${DOMAIN}
regulation:
max_retries: 3
find_time: 2m
ban_time: 5m
storage:
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
local:
path: /config/db.sqlite3
notifier:
# File-based notifications (for testing)
filesystem:
filename: /config/notification.txt
# SMTP notifications (recommended for production)
# smtp:
# host: smtp.gmail.com
# port: 587
# username: ${SMTP_USERNAME}
# password: ${AUTHELIA_NOTIFIER_SMTP_PASSWORD}
# sender: authelia@${DOMAIN}
# identifier: localhost
# subject: "[Authelia] {title}"
# startup_check_address: test@authelia.com
# disable_require_tls: false
# disable_html_emails: false

View File

@@ -0,0 +1,20 @@
# Authelia Users Database
# Copy to /opt/stacks/authelia/users_database.yml
# Generate password hashes with: docker run authelia/authelia:latest authelia crypto hash generate argon2 --password 'yourpassword'
users:
admin:
displayname: "Admin User"
password: "$argon2id$v=19$m=65536,t=3,p=4$CHANGEME" # Replace with your hashed password
email: admin@example.com
groups:
- admins
- users
# Example: Additional user
# user1:
# displayname: "User One"
# password: "$argon2id$v=19$m=65536,t=3,p=4$CHANGEME"
# email: user1@example.com
# groups:
# - users

View File

@@ -0,0 +1,31 @@
# Traefik Dynamic Configuration
# Copy to /opt/stacks/traefik/dynamic/routes.yml
# Add custom routes here that aren't defined via Docker labels
http:
routers:
# Example custom route
# custom-service:
# rule: "Host(`custom.example.com`)"
# entryPoints:
# - websecure
# middlewares:
# - authelia@docker
# tls:
# certResolver: letsencrypt
# service: custom-service
services:
# Example custom service
# custom-service:
# loadBalancer:
# servers:
# - url: "http://192.168.1.100:8080"
middlewares:
# Additional middlewares can be defined here
# Example: Rate limiting
# rate-limit:
# rateLimit:
# average: 100
# burst: 50

View File

@@ -0,0 +1,58 @@
# Traefik Static Configuration
# Copy to /opt/stacks/traefik/traefik.yml
global:
checkNewVersion: true
sendAnonymousUsage: false
api:
dashboard: true
insecure: false # Dashboard accessible via Traefik route with Authelia
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: ${ACME_EMAIL}
storage: /acme.json
# Use HTTP challenge (port 80 must be accessible)
httpChallenge:
entryPoint: web
# Or use DNS challenge (requires API token):
# dnsChallenge:
# provider: duckdns
# resolvers:
# - "1.1.1.1:53"
# - "8.8.8.8:53"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false # Only expose services with traefik.enable=true
network: traefik-network
file:
directory: /dynamic
watch: true
log:
level: INFO # DEBUG, INFO, WARN, ERROR
filePath: /var/log/traefik/traefik.log
accessLog:
filePath: /var/log/traefik/access.log
bufferingSize: 100

View File

@@ -0,0 +1,324 @@
# Docker Compose Stacks - Dockge Structure
This directory contains Docker Compose files designed for use with Dockge. Each stack should be placed in `/opt/stacks/stack-name/` on your server.
## Structure
```
/opt/stacks/
├── traefik/
│ ├── docker-compose.yml # Copy from traefik.yml
│ ├── traefik.yml # Static configuration
│ ├── dynamic/ # Dynamic routes
│ ├── acme.json # SSL certificates (chmod 600)
│ └── .env
├── authelia/
│ ├── docker-compose.yml # Copy from authelia.yml
│ ├── configuration.yml # Authelia config
│ ├── users_database.yml # User definitions
│ └── .env
├── duckdns/
│ ├── docker-compose.yml # Copy from duckdns.yml
│ └── .env
├── gluetun/
│ ├── docker-compose.yml # Copy from gluetun.yml (includes qBittorrent)
│ └── .env
├── infrastructure/
│ ├── docker-compose.yml # Copy from infrastructure.yml
│ └── .env
├── media/
│ ├── docker-compose.yml # Copy from media.yml
│ └── .env
├── monitoring/
│ ├── docker-compose.yml # Copy from monitoring.yml
│ └── .env
└── development/
├── docker-compose.yml # Copy from development.yml
└── .env
```
## Core Infrastructure Stacks
### 1. Traefik (REQUIRED - Deploy First)
**File**: `traefik.yml`
**Location**: `/opt/stacks/traefik/`
Reverse proxy with automatic SSL certificates via Let's Encrypt.
**Features**:
- Automatic HTTPS with Let's Encrypt
- Docker service discovery via labels
- File-based configuration for AI management
- HTTP to HTTPS redirect
**Setup**:
```bash
mkdir -p /opt/stacks/traefik/dynamic
cd /opt/stacks/traefik
# Copy traefik.yml to docker-compose.yml
# Copy config templates from config-templates/traefik/
# Create acme.json and set permissions
touch acme.json && chmod 600 acme.json
# Edit .env with your domain and email
docker compose up -d
```
### 2. Authelia (REQUIRED - Deploy Second)
**File**: `authelia.yml`
**Location**: `/opt/stacks/authelia/`
Single Sign-On (SSO) authentication for all services.
**Features**:
- Protects services with authentication
- Bypass rules for apps (Jellyfin, Plex)
- Integrates with Traefik via middleware
- TOTP 2FA support
**Setup**:
```bash
mkdir -p /opt/stacks/authelia
cd /opt/stacks/authelia
# Copy authelia.yml to docker-compose.yml
# Copy config templates from config-templates/authelia/
# Generate secrets: openssl rand -hex 64
# Edit configuration.yml and users_database.yml
# Hash password: docker run authelia/authelia:latest authelia crypto hash generate argon2 --password 'yourpassword'
docker compose up -d
```
### 3. DuckDNS (RECOMMENDED)
**File**: `duckdns.yml`
**Location**: `/opt/stacks/duckdns/`
Dynamic DNS updater for your domain.
**Setup**:
```bash
mkdir -p /opt/stacks/duckdns
cd /opt/stacks/duckdns
# Copy duckdns.yml to docker-compose.yml
# Add DUCKDNS_TOKEN and DUCKDNS_SUBDOMAINS to .env
docker compose up -d
```
### 4. Gluetun VPN (REQUIRED for torrenting)
**File**: `gluetun.yml`
**Location**: `/opt/stacks/gluetun/`
VPN client (Surfshark) for routing download clients securely.
**Includes**: qBittorrent configured to route through VPN
**Setup**:
```bash
mkdir -p /opt/stacks/gluetun
mkdir -p /opt/stacks/qbittorrent
cd /opt/stacks/gluetun
# Copy gluetun.yml to docker-compose.yml
# Add Surfshark WireGuard credentials to .env
# Get WireGuard config from Surfshark dashboard
docker compose up -d
```
## Application Stacks
### Infrastructure
**File**: `infrastructure.yml`
**Location**: `/opt/stacks/infrastructure/`
- Pi-hole: Network-wide ad blocking
- Portainer: Docker management UI
- Watchtower: Automatic container updates
### Media
**File**: `media.yml`
**Location**: `/opt/stacks/media/`
- Plex: Media streaming (NO Authelia - app access)
- Jellyfin: Open-source streaming (NO Authelia - app access)
- Sonarr: TV show automation (WITH Authelia)
- Radarr: Movie automation (WITH Authelia)
- Prowlarr: Indexer manager (WITH Authelia)
**Note**: qBittorrent is in gluetun.yml (VPN routing)
### Monitoring
**File**: `monitoring.yml`
**Location**: `/opt/stacks/monitoring/`
- Prometheus: Metrics collection
- Grafana: Visualization
- Node Exporter: System metrics
- cAdvisor: Container metrics
- Uptime Kuma: Service monitoring
- Loki: Log aggregation
- Promtail: Log shipping
### Development
**File**: `development.yml`
**Location**: `/opt/stacks/development/`
- Code Server: VS Code in browser
- GitLab: Git repository manager
- PostgreSQL: Database
- Redis: In-memory store
- pgAdmin: Database UI
- Jupyter Lab: Data science notebooks
- Node-RED: Automation
## Networks
Create these networks before deploying stacks:
```bash
docker network create traefik-network
docker network create homelab-network
```
## Environment Variables
Each stack needs a `.env` file. Use `/home/runner/work/AI-Homelab/AI-Homelab/.env.example` as a template.
**Required variables**:
- `DOMAIN`: Your DuckDNS domain (e.g., `yourdomain.duckdns.org`)
- `DUCKDNS_TOKEN`: Your DuckDNS token
- `ACME_EMAIL`: Email for Let's Encrypt
- `AUTHELIA_JWT_SECRET`: Generate with `openssl rand -hex 64`
- `AUTHELIA_SESSION_SECRET`: Generate with `openssl rand -hex 64`
- `AUTHELIA_STORAGE_ENCRYPTION_KEY`: Generate with `openssl rand -hex 64`
- `SURFSHARK_PRIVATE_KEY`: From Surfshark WireGuard config
- `SURFSHARK_ADDRESSES`: From Surfshark WireGuard config
## Deployment Order
1. **Create networks**:
```bash
docker network create traefik-network
docker network create homelab-network
```
2. **Deploy Traefik** (reverse proxy):
```bash
cd /opt/stacks/traefik
docker compose up -d
```
3. **Deploy Authelia** (SSO):
```bash
cd /opt/stacks/authelia
docker compose up -d
```
4. **Deploy DuckDNS** (optional but recommended):
```bash
cd /opt/stacks/duckdns
docker compose up -d
```
5. **Deploy Gluetun** (VPN for downloads):
```bash
cd /opt/stacks/gluetun
docker compose up -d
```
6. **Deploy other stacks** as needed:
```bash
cd /opt/stacks/infrastructure
docker compose up -d
cd /opt/stacks/media
docker compose up -d
```
## Accessing Services
All services are accessible via your domain:
- **With Authelia (SSO required)**:
- `https://traefik.yourdomain.duckdns.org` - Traefik dashboard
- `https://portainer.yourdomain.duckdns.org` - Portainer
- `https://sonarr.yourdomain.duckdns.org` - Sonarr
- `https://radarr.yourdomain.duckdns.org` - Radarr
- `https://prowlarr.yourdomain.duckdns.org` - Prowlarr
- `https://qbit.yourdomain.duckdns.org` - qBittorrent
- `https://grafana.yourdomain.duckdns.org` - Grafana
- And more...
- **Without Authelia (direct app access)**:
- `https://plex.yourdomain.duckdns.org` - Plex
- `https://jellyfin.yourdomain.duckdns.org` - Jellyfin
- **Authentication page**:
- `https://auth.yourdomain.duckdns.org` - Authelia login
## AI Management
The AI assistant (GitHub Copilot) can:
1. **Add new services**: Creates compose files with proper Traefik labels and Authelia middleware
2. **Modify routes**: Updates Docker labels to change proxy routing
3. **Manage SSO**: Adds or removes Authelia middleware as needed
4. **Configure VPN**: Sets up services to route through Gluetun
5. **Update configurations**: Modifies config files in `/opt/stacks/*/config/`
All configuration is file-based, allowing the AI to manage everything without web UI dependencies.
## Storage Strategy
- **Config files**: `/opt/stacks/stack-name/config/` (on system drive)
- **Small data**: Docker named volumes
- **Large data**:
- Media: `/mnt/media` (separate drive)
- Downloads: `/mnt/downloads` (separate drive)
- Backups: `/mnt/backups` (separate drive)
## Troubleshooting
### Service won't start
```bash
cd /opt/stacks/stack-name
docker compose logs -f
```
### Check Traefik routing
```bash
docker logs traefik
# Or visit: https://traefik.yourdomain.duckdns.org
```
### Test VPN connection
```bash
docker exec gluetun sh -c "curl ifconfig.me"
# Should show VPN IP, not your home IP
```
### Authelia issues
```bash
cd /opt/stacks/authelia
docker compose logs -f authelia
# Check configuration.yml for syntax errors
```
## Backup Important Files
Regular backups of:
- `/opt/stacks/` (all compose files and configs)
- `/opt/stacks/traefik/acme.json` (SSL certificates)
- `/opt/stacks/authelia/users_database.yml` (user accounts)
- Environment files (`.env` - store securely, not in git!)
## Security Notes
1. **Secrets**: Never commit `.env` files or `acme.json` to git
2. **Authelia**: Use strong passwords, hash them properly
3. **VPN**: Always route download clients through Gluetun
4. **Updates**: Watchtower keeps containers updated automatically
5. **Firewall**: Only expose ports 80 and 443 to the internet
## Getting Help
- Check the main [README.md](../README.md)
- Review [Docker Guidelines](../docs/docker-guidelines.md)
- Use GitHub Copilot in VS Code for AI assistance
- Check service-specific logs

View File

@@ -0,0 +1,39 @@
# Authelia SSO Stack
# Single Sign-On authentication for all services
# Place in /opt/stacks/authelia/docker-compose.yml
services:
authelia:
image: authelia/authelia:4.37
container_name: authelia
restart: unless-stopped
networks:
- traefik-network
volumes:
- /opt/stacks/authelia/configuration.yml:/config/configuration.yml:ro
- /opt/stacks/authelia/users_database.yml:/config/users_database.yml
- authelia-data:/config
environment:
- TZ=${TZ}
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
- AUTHELIA_NOTIFIER_SMTP_PASSWORD=${SMTP_PASSWORD} # If using email notifications
labels:
- "traefik.enable=true"
- "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN}`)"
- "traefik.http.routers.authelia.entrypoints=websecure"
- "traefik.http.routers.authelia.tls.certresolver=letsencrypt"
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
# Authelia middleware for other services
- "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.${DOMAIN}"
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"
volumes:
authelia-data:
driver: local
networks:
traefik-network:
external: true

View File

@@ -0,0 +1,21 @@
# DuckDNS Dynamic DNS Stack
# Updates your DuckDNS domain with current public IP
# Place in /opt/stacks/duckdns/docker-compose.yml
services:
duckdns:
image: lscr.io/linuxserver/duckdns:latest
container_name: duckdns
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- SUBDOMAINS=${DUCKDNS_SUBDOMAINS} # Your subdomain(s), comma separated
- TOKEN=${DUCKDNS_TOKEN} # Your DuckDNS token
- UPDATE_IP=ipv4 # or ipv6, or both
volumes:
- /opt/stacks/duckdns/config:/config
labels:
- "homelab.category=infrastructure"
- "homelab.description=Dynamic DNS updater"

View File

@@ -0,0 +1,81 @@
# Gluetun VPN Stack
# VPN client for routing services through Surfshark (or other VPN providers)
# Place in /opt/stacks/gluetun/docker-compose.yml
# Services that need VPN use: network_mode: "service:gluetun"
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
networks:
- gluetun-network
- traefik-network
ports:
# qBittorrent ports (service runs through Gluetun)
- "8080:8080" # qBittorrent WebUI
- "6881:6881" # qBittorrent TCP
- "6881:6881/udp" # qBittorrent UDP
environment:
- VPN_SERVICE_PROVIDER=surfshark
- VPN_TYPE=wireguard # or openvpn
- WIREGUARD_PRIVATE_KEY=${SURFSHARK_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${SURFSHARK_ADDRESSES}
- SERVER_COUNTRIES=${VPN_COUNTRY:-Netherlands} # Preferred VPN server country
- TZ=${TZ}
# For OpenVPN instead of WireGuard:
# - OPENVPN_USER=${SURFSHARK_USERNAME}
# - OPENVPN_PASSWORD=${SURFSHARK_PASSWORD}
volumes:
- /opt/stacks/gluetun/config:/gluetun
labels:
- "homelab.category=infrastructure"
- "homelab.description=VPN client for secure routing (Surfshark)"
# qBittorrent - Torrent client routing through VPN
# Access at: https://qbit.yourdomain.duckdns.org
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:4.6.2
container_name: qbittorrent
network_mode: "service:gluetun" # Routes all traffic through VPN
depends_on:
- gluetun
volumes:
- /opt/stacks/qbittorrent/config:/config
- /mnt/downloads:/downloads # Large downloads on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ}
- WEBUI_PORT=8080
labels:
- "homelab.category=media"
- "homelab.description=Torrent download client (via VPN)"
# Traefik labels (applied to Gluetun since qBittorrent uses its network)
# Configure these on the Gluetun container instead:
# Traefik routing for qBittorrent (via Gluetun)
# Since qBittorrent uses Gluetun's network, we add a sidecar label container
qbit-labels:
image: alpine:latest
container_name: qbit-labels
command: tail -f /dev/null
networks:
- traefik-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.qbittorrent.rule=Host(`qbit.${DOMAIN}`)"
- "traefik.http.routers.qbittorrent.entrypoints=websecure"
- "traefik.http.routers.qbittorrent.tls.certresolver=letsencrypt"
- "traefik.http.routers.qbittorrent.middlewares=authelia@docker"
- "traefik.http.services.qbittorrent.loadbalancer.server.url=http://gluetun:8080"
networks:
gluetun-network:
driver: bridge
traefik-network:
external: true

View File

@@ -1,43 +1,44 @@
# 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
services:
# Nginx Proxy Manager - Web-based reverse proxy management
# Access at: http://server-ip:81
# Default credentials: admin@example.com / changeme
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:2.10.4
container_name: nginx-proxy-manager
restart: unless-stopped
networks:
- homelab-network
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "81:81" # Admin UI
volumes:
- ./config/nginx-proxy-manager/data:/data
- ./config/nginx-proxy-manager/letsencrypt:/etc/letsencrypt
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:81"]
interval: 30s
timeout: 10s
retries: 3
labels:
- "homelab.category=infrastructure"
- "homelab.description=Reverse proxy with Let's Encrypt support"
# Pi-hole - Network-wide ad blocker and DNS server
# Access at: http://server-ip:8080/admin
# Access at: http://server-ip:8080/admin or https://pihole.yourdomain.duckdns.org
pihole:
image: pihole/pihole:2024.01.0
container_name: pihole
restart: unless-stopped
networks:
- homelab-network
- traefik-network
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
environment:
- TZ=${TZ:-America/New_York}
- WEBPASSWORD=${PIHOLE_PASSWORD:-changeme}
- FTLCONF_LOCAL_IPV4=${SERVER_IP}
dns:
- 127.0.0.1
- 1.1.1.1
cap_add:
- NET_ADMIN
labels:
- "homelab.category=infrastructure"
- "homelab.description=Network-wide ad blocking and DNS"
# Traefik 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
@@ -59,16 +60,14 @@ services:
- "homelab.description=Network-wide ad blocking and DNS"
# Portainer - Docker management UI
# Access at: http://server-ip:9000
# Access at: https://portainer.yourdomain.duckdns.org
portainer:
image: portainer/portainer-ce:2.19.4
container_name: portainer
restart: unless-stopped
networks:
- homelab-network
ports:
- "9000:9000"
- "9443:9443"
- traefik-network
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer-data:/data
@@ -77,6 +76,13 @@ services:
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
@@ -105,3 +111,5 @@ volumes:
networks:
homelab-network:
external: true
traefik-network:
external: true

View File

@@ -1,9 +1,12 @@
# Media Services
# Services for media management and streaming
# Place in /opt/stacks/media/docker-compose.yml
# NOTE: qBittorrent is configured to use Gluetun VPN (see gluetun.yml)
services:
# Plex Media Server - Media streaming platform
# Access at: http://server-ip:32400/web
# Access at: https://plex.yourdomain.duckdns.org
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
plex:
image: plexinc/pms-docker:1.40.0.7998-f68041501
container_name: plex
@@ -11,18 +14,16 @@ services:
networks:
- media-network
- homelab-network
ports:
- "32400:32400" # Plex web UI
- traefik-network
volumes:
- ./config/plex:/config
- ${MEDIADIR:-/media}:/media:ro
- /opt/stacks/plex/config:/config
- /mnt/media:/media:ro # Large media files on separate drive
- plex-transcode:/transcode
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
- PLEX_CLAIM=${PLEX_CLAIM}
- ADVERTISE_IP=http://${SERVER_IP}:32400/
# Hardware transcoding support
# Uncomment ONE of the following options:
@@ -44,9 +45,16 @@ services:
labels:
- "homelab.category=media"
- "homelab.description=Plex media streaming server"
# Traefik labels - NO Authelia for app access
- "traefik.enable=true"
- "traefik.http.routers.plex.rule=Host(`plex.${DOMAIN}`)"
- "traefik.http.routers.plex.entrypoints=websecure"
- "traefik.http.routers.plex.tls.certresolver=letsencrypt"
- "traefik.http.services.plex.loadbalancer.server.port=32400"
# Jellyfin - Free alternative to Plex
# Access at: http://server-ip:8096
# Access at: https://jellyfin.yourdomain.duckdns.org
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
jellyfin:
image: jellyfin/jellyfin:10.8.13
container_name: jellyfin
@@ -54,12 +62,11 @@ services:
networks:
- media-network
- homelab-network
ports:
- "8096:8096"
- traefik-network
volumes:
- ./config/jellyfin:/config
- ./config/jellyfin/cache:/cache
- ${MEDIADIR:-/media}:/media:ro
- /opt/stacks/jellyfin/config:/config
- /opt/stacks/jellyfin/cache:/cache
- /mnt/media:/media:ro # Large media files on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
@@ -70,9 +77,15 @@ services:
labels:
- "homelab.category=media"
- "homelab.description=Open-source media streaming server"
# Traefik labels - NO Authelia for app access
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.${DOMAIN}`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
# Sonarr - TV show automation
# Access at: http://server-ip:8989
# Access at: https://sonarr.yourdomain.duckdns.org
sonarr:
image: lscr.io/linuxserver/sonarr:4.0.0
container_name: sonarr
@@ -80,12 +93,11 @@ services:
networks:
- media-network
- homelab-network
ports:
- "8989:8989"
- traefik-network
volumes:
- ./config/sonarr:/config
- ${MEDIADIR:-/media}:/media
- ${DOWNLOADDIR:-/downloads}:/downloads
- /opt/stacks/sonarr/config:/config
- /mnt/media:/media
- /mnt/downloads:/downloads # Large downloads on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
@@ -93,9 +105,16 @@ services:
labels:
- "homelab.category=media"
- "homelab.description=TV show management and automation"
# Traefik labels with Authelia
- "traefik.enable=true"
- "traefik.http.routers.sonarr.rule=Host(`sonarr.${DOMAIN}`)"
- "traefik.http.routers.sonarr.entrypoints=websecure"
- "traefik.http.routers.sonarr.tls.certresolver=letsencrypt"
- "traefik.http.routers.sonarr.middlewares=authelia@docker"
- "traefik.http.services.sonarr.loadbalancer.server.port=8989"
# Radarr - Movie automation
# Access at: http://server-ip:7878
# Access at: https://radarr.yourdomain.duckdns.org
radarr:
image: lscr.io/linuxserver/radarr:5.2.6
container_name: radarr
@@ -103,12 +122,11 @@ services:
networks:
- media-network
- homelab-network
ports:
- "7878:7878"
- traefik-network
volumes:
- ./config/radarr:/config
- ${MEDIADIR:-/media}:/media
- ${DOWNLOADDIR:-/downloads}:/downloads
- /opt/stacks/radarr/config:/config
- /mnt/media:/media
- /mnt/downloads:/downloads # Large downloads on separate drive
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
@@ -116,9 +134,16 @@ services:
labels:
- "homelab.category=media"
- "homelab.description=Movie management and automation"
# Traefik labels with Authelia
- "traefik.enable=true"
- "traefik.http.routers.radarr.rule=Host(`radarr.${DOMAIN}`)"
- "traefik.http.routers.radarr.entrypoints=websecure"
- "traefik.http.routers.radarr.tls.certresolver=letsencrypt"
- "traefik.http.routers.radarr.middlewares=authelia@docker"
- "traefik.http.services.radarr.loadbalancer.server.port=7878"
# Prowlarr - Indexer manager
# Access at: http://server-ip:9696
# Access at: https://prowlarr.yourdomain.duckdns.org
prowlarr:
image: lscr.io/linuxserver/prowlarr:1.11.4
container_name: prowlarr
@@ -126,10 +151,9 @@ services:
networks:
- media-network
- homelab-network
ports:
- "9696:9696"
- traefik-network
volumes:
- ./config/prowlarr:/config
- /opt/stacks/prowlarr/config:/config
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
@@ -137,32 +161,19 @@ services:
labels:
- "homelab.category=media"
- "homelab.description=Indexer manager for Sonarr/Radarr"
# Traefik labels with Authelia
- "traefik.enable=true"
- "traefik.http.routers.prowlarr.rule=Host(`prowlarr.${DOMAIN}`)"
- "traefik.http.routers.prowlarr.entrypoints=websecure"
- "traefik.http.routers.prowlarr.tls.certresolver=letsencrypt"
- "traefik.http.routers.prowlarr.middlewares=authelia@docker"
- "traefik.http.services.prowlarr.loadbalancer.server.port=9696"
# qBittorrent - Torrent client
# Access at: http://server-ip:8081
# Default credentials: admin / adminadmin
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:4.6.2
container_name: qbittorrent
restart: unless-stopped
networks:
- media-network
- homelab-network
ports:
- "8081:8081" # Web UI
- "6881:6881" # Torrent port TCP
- "6881:6881/udp" # Torrent port UDP
volumes:
- ./config/qbittorrent:/config
- ${DOWNLOADDIR:-/downloads}:/downloads
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-America/New_York}
- WEBUI_PORT=8081
labels:
- "homelab.category=media"
- "homelab.description=Torrent download client"
# Access at: https://qbit.yourdomain.duckdns.org
# Routes through Gluetun VPN - configure in gluetun.yml
# NOTE: This is a placeholder. Configure qBittorrent in gluetun.yml with network_mode: "service:gluetun"
# See gluetun.yml for the actual qBittorrent configuration
volumes:
plex-transcode:
@@ -173,3 +184,5 @@ networks:
driver: bridge
homelab-network:
external: true
traefik-network:
external: true

View File

@@ -0,0 +1,43 @@
# Traefik Reverse Proxy Stack
# Main reverse proxy with Let's Encrypt SSL automation
# Place in /opt/stacks/traefik/docker-compose.yml
services:
traefik:
image: traefik:v2.11
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-network
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "8080:8080" # Dashboard (protect with Authelia)
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /opt/stacks/traefik/traefik.yml:/traefik.yml:ro
- /opt/stacks/traefik/dynamic:/dynamic:ro
- /opt/stacks/traefik/acme.json:/acme.json
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # If using Cloudflare DNS challenge
- DUCKDNS_TOKEN=${DUCKDNS_TOKEN} # If using DuckDNS
labels:
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik.middlewares=authelia@docker"
- "traefik.http.routers.traefik.service=api@internal"
# Global HTTP to HTTPS redirect
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
networks:
traefik-network:
external: true