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:
38
.env.example
38
.env.example
@@ -12,12 +12,41 @@ TZ=America/New_York
|
|||||||
# Server IP address
|
# Server IP address
|
||||||
SERVER_IP=192.168.1.100
|
SERVER_IP=192.168.1.100
|
||||||
|
|
||||||
|
# Domain Configuration
|
||||||
|
DOMAIN=yourdomain.duckdns.org # Your DuckDNS domain
|
||||||
|
|
||||||
# Directory Paths
|
# Directory Paths
|
||||||
USERDIR=/home/username/homelab
|
USERDIR=/opt/stacks
|
||||||
MEDIADIR=/mnt/media
|
MEDIADIR=/mnt/media # Large media files on separate drive
|
||||||
DOWNLOADDIR=/mnt/downloads
|
DOWNLOADDIR=/mnt/downloads # Downloads on separate drive
|
||||||
PROJECTDIR=/home/username/projects
|
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 Configuration
|
||||||
PLEX_CLAIM=claim-xxxxxxxxxx
|
PLEX_CLAIM=claim-xxxxxxxxxx
|
||||||
|
|
||||||
@@ -45,4 +74,7 @@ PIHOLE_PASSWORD=changeme
|
|||||||
# Watchtower Notifications (optional)
|
# Watchtower Notifications (optional)
|
||||||
# WATCHTOWER_NOTIFICATION_URL=
|
# WATCHTOWER_NOTIFICATION_URL=
|
||||||
|
|
||||||
|
# Cloudflare API (optional, for DNS challenge)
|
||||||
|
# CF_DNS_API_TOKEN=your-cloudflare-api-token
|
||||||
|
|
||||||
# Add your own variables below
|
# Add your own variables below
|
||||||
|
|||||||
255
.github/copilot-instructions.md
vendored
255
.github/copilot-instructions.md
vendored
@@ -1,26 +1,45 @@
|
|||||||
# AI Homelab Management Assistant
|
# 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
|
## Core Principles
|
||||||
|
|
||||||
### 1. Docker Compose First
|
### 1. Dockge and Docker Compose First
|
||||||
- **ALWAYS** use Docker Compose stacks for persistent services
|
- **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)
|
- 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
|
- Keep consistent naming conventions across all compose files
|
||||||
- Use the same network naming patterns
|
- Use the same network naming patterns
|
||||||
- Maintain uniform volume mount structures
|
- Maintain uniform volume mount structures
|
||||||
- Apply consistent environment variable patterns
|
- Apply consistent environment variable patterns
|
||||||
|
|
||||||
### 3. Stack-Aware Changes
|
### 4. Stack-Aware Changes
|
||||||
- Before making changes, consider the impact on the entire server stack
|
- Before making changes, consider the impact on the entire server stack
|
||||||
- Check for service dependencies (networks, volumes, other services)
|
- Check for service dependencies (networks, volumes, other services)
|
||||||
- Ensure changes don't break existing integrations
|
- Ensure changes don't break existing integrations
|
||||||
- Validate that port assignments don't conflict
|
- 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
|
## Creating a New Docker Service
|
||||||
|
|
||||||
When creating a new service, follow these steps:
|
When creating a new service, follow these steps:
|
||||||
@@ -46,10 +65,12 @@ When creating a new service, follow these steps:
|
|||||||
networks:
|
networks:
|
||||||
- homelab-network # Use shared networks
|
- homelab-network # Use shared networks
|
||||||
ports:
|
ports:
|
||||||
- "host_port:container_port" # Document port purpose
|
- "host_port:container_port" # Document port purpose (if not using Traefik)
|
||||||
volumes:
|
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
|
- service-data:/data # Named volumes for persistent data
|
||||||
|
# For large data, use separate mount:
|
||||||
|
# - /mnt/media:/media # Large media files on separate drive
|
||||||
environment:
|
environment:
|
||||||
- PUID=1000 # Standard user/group IDs
|
- PUID=1000 # Standard user/group IDs
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
@@ -57,6 +78,13 @@ When creating a new service, follow these steps:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=category-name" # For organization
|
- "homelab.category=category-name" # For organization
|
||||||
- "homelab.description=Service description"
|
- "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:
|
volumes:
|
||||||
service-data:
|
service-data:
|
||||||
@@ -213,41 +241,220 @@ environment:
|
|||||||
## File Organization
|
## File Organization
|
||||||
|
|
||||||
```
|
```
|
||||||
/home/user/homelab/
|
/opt/stacks/
|
||||||
├── docker-compose/
|
├── stack-name/
|
||||||
│ ├── media.yml # Media server services
|
│ ├── docker-compose.yml # Stack definition
|
||||||
│ ├── monitoring.yml # Monitoring stack
|
│ ├── config/ # Service configurations
|
||||||
│ ├── development.yml # Dev tools
|
│ ├── .env # Stack-specific secrets
|
||||||
│ └── infrastructure.yml # Core services
|
│ └── README.md # Stack documentation
|
||||||
├── config/
|
├── traefik/
|
||||||
│ ├── service1/
|
│ ├── docker-compose.yml
|
||||||
│ ├── service2/
|
│ ├── traefik.yml # Traefik static config
|
||||||
│ └── ...
|
│ ├── dynamic/ # Dynamic configuration
|
||||||
├── data/ # Bind mount data
|
│ │ └── routes.yml # Route definitions
|
||||||
│ └── ...
|
│ ├── acme.json # Let's Encrypt certificates
|
||||||
├── .env # Global secrets (gitignored)
|
│ └── .env
|
||||||
└── README.md # Stack documentation
|
├── 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
|
## Safety Checks
|
||||||
|
|
||||||
Before deploying any changes:
|
Before deploying any changes:
|
||||||
- [ ] YAML syntax is valid
|
- [ ] YAML syntax is valid
|
||||||
- [ ] Ports don't conflict with existing services
|
- [ ] Ports don't conflict with existing services
|
||||||
- [ ] Networks exist or are defined
|
- [ ] 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
|
- [ ] Environment variables are set
|
||||||
- [ ] No secrets in compose files
|
- [ ] No secrets in compose files
|
||||||
- [ ] Service dependencies are met
|
- [ ] Service dependencies are met
|
||||||
- [ ] Backup of current configuration exists
|
- [ ] Backup of current configuration exists
|
||||||
|
- [ ] Traefik labels are correct for routing
|
||||||
|
- [ ] Authelia middleware applied appropriately
|
||||||
|
- [ ] VPN routing configured if needed
|
||||||
|
|
||||||
## Remember
|
## Remember
|
||||||
|
|
||||||
- **Think before you act**: Consider the entire stack
|
- **Think before you act**: Consider the entire stack
|
||||||
- **Be consistent**: Follow established patterns
|
- **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
|
- **Document everything**: Future you will thank you
|
||||||
- **Test safely**: Use temporary containers first
|
- **Test safely**: Use temporary containers first
|
||||||
- **Back up first**: Always have a rollback plan
|
- **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.
|
||||||
|
|||||||
93
config-templates/authelia/configuration.yml
Normal file
93
config-templates/authelia/configuration.yml
Normal 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
|
||||||
20
config-templates/authelia/users_database.yml
Normal file
20
config-templates/authelia/users_database.yml
Normal 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
|
||||||
31
config-templates/traefik/dynamic/routes.yml
Normal file
31
config-templates/traefik/dynamic/routes.yml
Normal 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
|
||||||
58
config-templates/traefik/traefik.yml
Normal file
58
config-templates/traefik/traefik.yml
Normal 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
|
||||||
324
docker-compose/README-dockge.md
Normal file
324
docker-compose/README-dockge.md
Normal 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
|
||||||
39
docker-compose/authelia.yml
Normal file
39
docker-compose/authelia.yml
Normal 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
|
||||||
21
docker-compose/duckdns.yml
Normal file
21
docker-compose/duckdns.yml
Normal 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"
|
||||||
81
docker-compose/gluetun.yml
Normal file
81
docker-compose/gluetun.yml
Normal 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
|
||||||
@@ -1,43 +1,44 @@
|
|||||||
# 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
|
||||||
|
# See traefik.yml, authelia.yml, duckdns.yml, and gluetun.yml
|
||||||
|
|
||||||
services:
|
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
|
# 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:
|
pihole:
|
||||||
image: pihole/pihole:2024.01.0
|
image: pihole/pihole:2024.01.0
|
||||||
container_name: pihole
|
container_name: pihole
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- homelab-network
|
- 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:
|
ports:
|
||||||
- "53:53/tcp" # DNS TCP
|
- "53:53/tcp" # DNS TCP
|
||||||
- "53:53/udp" # DNS UDP
|
- "53:53/udp" # DNS UDP
|
||||||
@@ -59,16 +60,14 @@ services:
|
|||||||
- "homelab.description=Network-wide ad blocking and DNS"
|
- "homelab.description=Network-wide ad blocking and DNS"
|
||||||
|
|
||||||
# Portainer - Docker management UI
|
# Portainer - Docker management UI
|
||||||
# Access at: http://server-ip:9000
|
# Access at: https://portainer.yourdomain.duckdns.org
|
||||||
portainer:
|
portainer:
|
||||||
image: portainer/portainer-ce:2.19.4
|
image: portainer/portainer-ce:2.19.4
|
||||||
container_name: portainer
|
container_name: portainer
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- homelab-network
|
- homelab-network
|
||||||
ports:
|
- traefik-network
|
||||||
- "9000:9000"
|
|
||||||
- "9443:9443"
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- portainer-data:/data
|
- portainer-data:/data
|
||||||
@@ -77,6 +76,13 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=infrastructure"
|
- "homelab.category=infrastructure"
|
||||||
- "homelab.description=Docker container management UI"
|
- "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
|
||||||
@@ -105,3 +111,5 @@ volumes:
|
|||||||
networks:
|
networks:
|
||||||
homelab-network:
|
homelab-network:
|
||||||
external: true
|
external: true
|
||||||
|
traefik-network:
|
||||||
|
external: true
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
# Media Services
|
# Media Services
|
||||||
# Services for media management and streaming
|
# 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:
|
services:
|
||||||
# Plex Media Server - Media streaming platform
|
# 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:
|
plex:
|
||||||
image: plexinc/pms-docker:1.40.0.7998-f68041501
|
image: plexinc/pms-docker:1.40.0.7998-f68041501
|
||||||
container_name: plex
|
container_name: plex
|
||||||
@@ -11,18 +14,16 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- media-network
|
- media-network
|
||||||
- homelab-network
|
- homelab-network
|
||||||
ports:
|
- traefik-network
|
||||||
- "32400:32400" # Plex web UI
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/plex:/config
|
- /opt/stacks/plex/config:/config
|
||||||
- ${MEDIADIR:-/media}:/media:ro
|
- /mnt/media:/media:ro # Large media files on separate drive
|
||||||
- plex-transcode:/transcode
|
- plex-transcode:/transcode
|
||||||
environment:
|
environment:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
- TZ=${TZ:-America/New_York}
|
- TZ=${TZ:-America/New_York}
|
||||||
- PLEX_CLAIM=${PLEX_CLAIM}
|
- PLEX_CLAIM=${PLEX_CLAIM}
|
||||||
- ADVERTISE_IP=http://${SERVER_IP}:32400/
|
|
||||||
# Hardware transcoding support
|
# Hardware transcoding support
|
||||||
# Uncomment ONE of the following options:
|
# Uncomment ONE of the following options:
|
||||||
|
|
||||||
@@ -44,9 +45,16 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=media"
|
- "homelab.category=media"
|
||||||
- "homelab.description=Plex media streaming server"
|
- "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
|
# 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:
|
jellyfin:
|
||||||
image: jellyfin/jellyfin:10.8.13
|
image: jellyfin/jellyfin:10.8.13
|
||||||
container_name: jellyfin
|
container_name: jellyfin
|
||||||
@@ -54,12 +62,11 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- media-network
|
- media-network
|
||||||
- homelab-network
|
- homelab-network
|
||||||
ports:
|
- traefik-network
|
||||||
- "8096:8096"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/jellyfin:/config
|
- /opt/stacks/jellyfin/config:/config
|
||||||
- ./config/jellyfin/cache:/cache
|
- /opt/stacks/jellyfin/cache:/cache
|
||||||
- ${MEDIADIR:-/media}:/media:ro
|
- /mnt/media:/media:ro # Large media files on separate drive
|
||||||
environment:
|
environment:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
@@ -70,9 +77,15 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=media"
|
- "homelab.category=media"
|
||||||
- "homelab.description=Open-source media streaming server"
|
- "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
|
# Sonarr - TV show automation
|
||||||
# Access at: http://server-ip:8989
|
# Access at: https://sonarr.yourdomain.duckdns.org
|
||||||
sonarr:
|
sonarr:
|
||||||
image: lscr.io/linuxserver/sonarr:4.0.0
|
image: lscr.io/linuxserver/sonarr:4.0.0
|
||||||
container_name: sonarr
|
container_name: sonarr
|
||||||
@@ -80,12 +93,11 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- media-network
|
- media-network
|
||||||
- homelab-network
|
- homelab-network
|
||||||
ports:
|
- traefik-network
|
||||||
- "8989:8989"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/sonarr:/config
|
- /opt/stacks/sonarr/config:/config
|
||||||
- ${MEDIADIR:-/media}:/media
|
- /mnt/media:/media
|
||||||
- ${DOWNLOADDIR:-/downloads}:/downloads
|
- /mnt/downloads:/downloads # Large downloads on separate drive
|
||||||
environment:
|
environment:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
@@ -93,9 +105,16 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=media"
|
- "homelab.category=media"
|
||||||
- "homelab.description=TV show management and automation"
|
- "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
|
# Radarr - Movie automation
|
||||||
# Access at: http://server-ip:7878
|
# Access at: https://radarr.yourdomain.duckdns.org
|
||||||
radarr:
|
radarr:
|
||||||
image: lscr.io/linuxserver/radarr:5.2.6
|
image: lscr.io/linuxserver/radarr:5.2.6
|
||||||
container_name: radarr
|
container_name: radarr
|
||||||
@@ -103,12 +122,11 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- media-network
|
- media-network
|
||||||
- homelab-network
|
- homelab-network
|
||||||
ports:
|
- traefik-network
|
||||||
- "7878:7878"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/radarr:/config
|
- /opt/stacks/radarr/config:/config
|
||||||
- ${MEDIADIR:-/media}:/media
|
- /mnt/media:/media
|
||||||
- ${DOWNLOADDIR:-/downloads}:/downloads
|
- /mnt/downloads:/downloads # Large downloads on separate drive
|
||||||
environment:
|
environment:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
@@ -116,9 +134,16 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=media"
|
- "homelab.category=media"
|
||||||
- "homelab.description=Movie management and automation"
|
- "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
|
# Prowlarr - Indexer manager
|
||||||
# Access at: http://server-ip:9696
|
# Access at: https://prowlarr.yourdomain.duckdns.org
|
||||||
prowlarr:
|
prowlarr:
|
||||||
image: lscr.io/linuxserver/prowlarr:1.11.4
|
image: lscr.io/linuxserver/prowlarr:1.11.4
|
||||||
container_name: prowlarr
|
container_name: prowlarr
|
||||||
@@ -126,10 +151,9 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- media-network
|
- media-network
|
||||||
- homelab-network
|
- homelab-network
|
||||||
ports:
|
- traefik-network
|
||||||
- "9696:9696"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/prowlarr:/config
|
- /opt/stacks/prowlarr/config:/config
|
||||||
environment:
|
environment:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
@@ -137,32 +161,19 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "homelab.category=media"
|
- "homelab.category=media"
|
||||||
- "homelab.description=Indexer manager for Sonarr/Radarr"
|
- "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
|
# qBittorrent - Torrent client
|
||||||
# Access at: http://server-ip:8081
|
# Access at: https://qbit.yourdomain.duckdns.org
|
||||||
# Default credentials: admin / adminadmin
|
# Routes through Gluetun VPN - configure in gluetun.yml
|
||||||
qbittorrent:
|
# NOTE: This is a placeholder. Configure qBittorrent in gluetun.yml with network_mode: "service:gluetun"
|
||||||
image: lscr.io/linuxserver/qbittorrent:4.6.2
|
# See gluetun.yml for the actual qBittorrent configuration
|
||||||
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"
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
plex-transcode:
|
plex-transcode:
|
||||||
@@ -173,3 +184,5 @@ networks:
|
|||||||
driver: bridge
|
driver: bridge
|
||||||
homelab-network:
|
homelab-network:
|
||||||
external: true
|
external: true
|
||||||
|
traefik-network:
|
||||||
|
external: true
|
||||||
|
|||||||
43
docker-compose/traefik.yml
Normal file
43
docker-compose/traefik.yml
Normal 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
|
||||||
Reference in New Issue
Block a user