Merge remote updates, accepting remote versions for conflicted files
This commit is contained in:
364
.github/copilot-instructions.md
vendored
364
.github/copilot-instructions.md
vendored
@@ -1,355 +1,71 @@
|
|||||||
# AI Homelab Management Assistant
|
# AI Homelab Management Assistant
|
||||||
|
|
||||||
You are an AI assistant for the **AI-Homelab** project - a production-ready Docker homelab infrastructure managed through GitHub Copilot in VS Code. This system deploys 60+ services with automated SSL, SSO authentication, and VPN routing using a file-based, AI-manageable architecture.
|
You are an AI assistant for managing Docker-based homelab infrastructure using Dockge, Traefik, Authelia, and Gluetun.
|
||||||
|
|
||||||
## Project Architecture
|
## Architecture Overview
|
||||||
|
- **Stacks**: All services in `/opt/stacks/stack-name/docker-compose.yml` managed via Dockge
|
||||||
|
- **Reverse Proxy**: Traefik routes traffic with automatic SSL via Let's Encrypt
|
||||||
|
- **SSO**: Authelia protects admin interfaces (bypass for Plex/Jellyfin apps)
|
||||||
|
- **VPN**: Gluetun (Surfshark WireGuard) for secure downloads
|
||||||
|
- **Networks**: `traefik-network`, `homelab-network`, `media-network` (external)
|
||||||
|
- **Storage**: Bind mounts in `/opt/stacks/` for configs; `/mnt/` for large data (>50GB)
|
||||||
|
|
||||||
### Core Infrastructure (Deploy First)
|
## Core Workflow
|
||||||
The **core stack** (`/opt/stacks/core/`) contains essential services that must run before others:
|
1. **Deploy Core First**: DuckDNS + Traefik + Authelia + Gluetun via `./scripts/deploy-homelab.sh`
|
||||||
- **DuckDNS**: Dynamic DNS with Let's Encrypt DNS challenge for wildcard SSL (`*.yourdomain.duckdns.org`)
|
2. **Add Services**: Create compose files with Traefik labels, deploy via Dockge
|
||||||
- **Traefik**: Reverse proxy with automatic HTTPS termination (labels-based routing, file provider for external hosts)
|
3. **Manage via Files**: No web UIs - all config in YAML files
|
||||||
- **Authelia**: SSO authentication (auto-generated secrets, file-based user database)
|
|
||||||
- **Gluetun**: VPN client (Surfshark WireGuard/OpenVPN) for download services
|
|
||||||
|
|
||||||
### Deployment Model
|
## Service Template
|
||||||
- **Two-script setup**: `setup-homelab.sh` (system prep, Docker install, secrets generation) → `deploy-homelab.sh` (automated deployment)
|
|
||||||
- **Dockge-based management**: All stacks in `/opt/stacks/`, managed via web UI at `dockge.${DOMAIN}`
|
|
||||||
- **Automated workflows**: Scripts create directories, configure networks, deploy stacks, wait for health checks
|
|
||||||
- **Repository location**: `/home/kelin/AI-Homelab/` (templates in `docker-compose/`, docs in `docs/`)
|
|
||||||
|
|
||||||
### File Structure Standards
|
|
||||||
```
|
|
||||||
/opt/stacks/
|
|
||||||
├── core/ # DuckDNS, Traefik, Authelia, Gluetun (deploy FIRST)
|
|
||||||
├── infrastructure/ # Dockge, Pi-hole, monitoring tools
|
|
||||||
├── dashboards/ # Homepage (AI-configured), Homarr
|
|
||||||
├── media/ # Plex, Jellyfin, Calibre-web, qBittorrent
|
|
||||||
├── media-management/ # *arr services (Sonarr, Radarr, etc.)
|
|
||||||
├── homeassistant/ # Home Assistant, Node-RED, Zigbee2MQTT
|
|
||||||
├── productivity/ # Nextcloud, Gitea, Bookstack
|
|
||||||
├── monitoring/ # Grafana, Prometheus, Uptime Kuma
|
|
||||||
└── utilities/ # Duplicati, FreshRSS, Wallabag
|
|
||||||
```
|
|
||||||
|
|
||||||
### Network Architecture
|
|
||||||
- **traefik-network**: Primary network for all services behind Traefik
|
|
||||||
- **Gluetun network mode**: Download clients use `network_mode: "service:gluetun"` for VPN routing
|
|
||||||
- **Port mapping**: Only core services expose ports (80/443 for Traefik); others route via Traefik labels
|
|
||||||
|
|
||||||
## Critical Operational Principles
|
|
||||||
|
|
||||||
### 1. Security-First SSO Strategy
|
|
||||||
- **Default stance**: ALL services start with Authelia middleware enabled
|
|
||||||
- **Bypass exceptions**: Only Plex and Jellyfin (for device/app compatibility)
|
|
||||||
- **Disabling SSO**: Comment (don't delete) the middleware line: `# - "traefik.http.routers.SERVICE.middlewares=authelia@docker"`
|
|
||||||
- **Rationale**: Security by default; users explicitly opt-out for specific services
|
|
||||||
|
|
||||||
### 2. Traefik Label Patterns
|
|
||||||
Standard routing configuration for new services:
|
|
||||||
```yaml
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.SERVICE.rule=Host(`SERVICE.${DOMAIN}`)"
|
|
||||||
- "traefik.http.routers.SERVICE.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.SERVICE.tls.certresolver=letsencrypt" # Uses wildcard cert
|
|
||||||
- "traefik.http.routers.SERVICE.middlewares=authelia@docker" # SSO protection
|
|
||||||
- "traefik.http.services.SERVICE.loadbalancer.server.port=PORT" # If not default
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Storage Strategy
|
|
||||||
- **Configs**: Bind mount `./service/config:/config` relative to stack directory
|
|
||||||
- **Small data**: Named volumes (databases, app data <50GB)
|
|
||||||
- **Large data**: External mounts `/mnt/media`, `/mnt/downloads` (user must configure)
|
|
||||||
- **Secrets**: `.env` files in stack directories (auto-copied from `~/AI-Homelab/.env`)
|
|
||||||
|
|
||||||
### 4. LinuxServer.io Preference
|
|
||||||
- Use `lscr.io/linuxserver/*` images when available (PUID/PGID support for permissions)
|
|
||||||
- Standard environment: `PUID=1000`, `PGID=1000`, `TZ=${TZ}`
|
|
||||||
|
|
||||||
### 5. External Host Proxying
|
|
||||||
Proxy non-Docker services (Raspberry Pi, NAS) via Traefik file provider:
|
|
||||||
- Create routes in `/opt/stacks/core/traefik/dynamic/external.yml`
|
|
||||||
- Example pattern documented in `docs/proxying-external-hosts.md`
|
|
||||||
- AI can manage these YAML files directly
|
|
||||||
|
|
||||||
## Developer Workflows
|
|
||||||
|
|
||||||
### First-Time Deployment
|
|
||||||
```bash
|
|
||||||
cd ~/AI-Homelab
|
|
||||||
sudo ./scripts/setup-homelab.sh # System prep, Docker install, Authelia secrets
|
|
||||||
# Reboot if NVIDIA drivers installed
|
|
||||||
sudo ./scripts/deploy-homelab.sh # Deploy core+infrastructure stacks, open Dockge
|
|
||||||
```
|
|
||||||
|
|
||||||
### Managing Services via Scripts
|
|
||||||
- **setup-homelab.sh**: Idempotent system preparation (skips completed steps, runs on bare Debian)
|
|
||||||
- Steps: Update system → Install Docker → Configure firewall → Generate Authelia secrets → Create directories/networks → NVIDIA driver detection
|
|
||||||
- Auto-generates: JWT secret (64 hex), session secret (64 hex), encryption key (64 hex), admin password hash
|
|
||||||
- Creates `homelab-network` and `traefik-network` Docker networks
|
|
||||||
- **deploy-homelab.sh**: Automated stack deployment (requires `.env` configured first)
|
|
||||||
- Steps: Validate prerequisites → Create directories → Deploy core → Deploy infrastructure → Deploy dashboards → Prepare additional stacks → Wait for Dockge
|
|
||||||
- Copies `.env` to `/opt/stacks/core/.env` and `/opt/stacks/infrastructure/.env`
|
|
||||||
- Waits for service health checks before proceeding
|
|
||||||
|
|
||||||
### Testing Changes
|
|
||||||
```bash
|
|
||||||
# Test in isolation before modifying stacks
|
|
||||||
docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi # GPU test
|
|
||||||
docker compose -f docker-compose.yml config # Validate YAML syntax
|
|
||||||
docker compose -f docker-compose.yml up -d SERVICE # Deploy single service
|
|
||||||
docker compose logs -f SERVICE # Monitor logs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Operations
|
|
||||||
```bash
|
|
||||||
cd /opt/stacks/STACK_NAME
|
|
||||||
docker compose up -d # Start stack
|
|
||||||
docker compose restart SERVICE # Restart service
|
|
||||||
docker compose logs -f SERVICE # Tail logs
|
|
||||||
docker compose pull && docker compose up -d # Update images
|
|
||||||
```
|
|
||||||
|
|
||||||
## Creating a New Docker Service
|
|
||||||
|
|
||||||
## Creating a New Docker Service
|
|
||||||
|
|
||||||
### Service Definition Template
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
service-name:
|
service-name:
|
||||||
image: linuxserver/service:latest # Pin versions in production; prefer LinuxServer.io
|
image: lscr.io/linuxserver/service:latest # Pin versions, prefer LinuxServer
|
||||||
container_name: service-name
|
container_name: service-name
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- traefik-network
|
- homelab-network
|
||||||
volumes:
|
volumes:
|
||||||
- ./service-name/config:/config # Config in stack directory
|
- /opt/stacks/stack-name/config:/config # Configs
|
||||||
- service-data:/data # Named volume for persistent data
|
- /mnt/large-data:/data # Large data on separate drives
|
||||||
# Large data on separate drives:
|
|
||||||
# - /mnt/media:/media
|
|
||||||
# - /mnt/downloads:/downloads
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=1000
|
||||||
- PGID=${PGID:-1000}
|
- PGID=1000
|
||||||
- TZ=${TZ}
|
- TZ=${TZ}
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.service-name.rule=Host(`service.${DOMAIN}`)"
|
- "traefik.http.routers.service-name.rule=Host(`service.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.service-name.entrypoints=websecure"
|
- "traefik.http.routers.service-name.entrypoints=websecure"
|
||||||
- "traefik.http.routers.service-name.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.service-name.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.service-name.middlewares=authelia@docker" # SSO enabled by default
|
- "traefik.http.routers.service-name.middlewares=authelia@docker" # SSO enabled
|
||||||
- "traefik.http.services.service-name.loadbalancer.server.port=8080" # If non-standard port
|
- "traefik.http.services.service-name.loadbalancer.server.port=8080"
|
||||||
- "homelab.category=category-name"
|
|
||||||
- "homelab.description=Service description"
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
service-data:
|
service-data:
|
||||||
driver: local
|
driver: local
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
traefik-network:
|
homelab-network:
|
||||||
external: true
|
external: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### VPN-Routed Service (Downloads)
|
## Key Patterns
|
||||||
Route through Gluetun for VPN protection:
|
- **SSO Bypass**: Comment out `authelia@docker` middleware for Plex/Jellyfin
|
||||||
```yaml
|
- **VPN Routing**: Use `network_mode: "service:gluetun"` for download clients
|
||||||
services:
|
- **Environment**: Secrets in `.env` files, referenced as `${VAR}`
|
||||||
# Gluetun already running in core stack
|
- **Dependencies**: Core stack must deploy first
|
||||||
|
- **Updates**: `docker compose pull && docker compose up -d`
|
||||||
|
|
||||||
qbittorrent:
|
## Critical Files
|
||||||
image: lscr.io/linuxserver/qbittorrent:latest
|
- `docker-compose/core.yml`: Essential infrastructure stack
|
||||||
container_name: qbittorrent
|
- `config-templates/`: Authelia/Traefik configs
|
||||||
network_mode: "service:gluetun" # Routes through VPN
|
- `scripts/deploy-homelab.sh`: Automated deployment
|
||||||
depends_on:
|
- `.env`: All environment variables
|
||||||
- gluetun
|
|
||||||
volumes:
|
|
||||||
- ./qbittorrent/config:/config
|
|
||||||
- /mnt/downloads:/downloads
|
|
||||||
environment:
|
|
||||||
- PUID=1000
|
|
||||||
- PGID=1000
|
|
||||||
- TZ=${TZ}
|
|
||||||
# No ports needed - mapped in Gluetun service
|
|
||||||
# No Traefik labels - access via Gluetun's network
|
|
||||||
```
|
|
||||||
|
|
||||||
Add ports to Gluetun in core stack:
|
## Safety First
|
||||||
```yaml
|
- Always consider stack-wide impacts
|
||||||
gluetun:
|
- Test changes with `docker run` first
|
||||||
ports:
|
- Backup configs before modifications
|
||||||
- 8080:8080 # qBittorrent WebUI
|
- Use LinuxServer images for proper permissions
|
||||||
```
|
- Document non-obvious configurations
|
||||||
|
|
||||||
### Authelia Bypass Example (Jellyfin)
|
When creating/modifying services, prioritize stability, security, and consistency across the homelab.
|
||||||
```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/devices
|
|
||||||
```
|
|
||||||
|
|
||||||
## Modifying Existing Services
|
|
||||||
|
|
||||||
## Modifying Existing Services
|
|
||||||
|
|
||||||
### Safe Modification Process
|
|
||||||
1. **Read entire compose file** - understand dependencies (networks, volumes, depends_on)
|
|
||||||
2. **Check for impacts** - search for service references across other compose files
|
|
||||||
3. **Validate YAML** - `docker compose config` before deploying
|
|
||||||
4. **Test in place** - restart single service: `docker compose up -d SERVICE`
|
|
||||||
5. **Monitor logs** - `docker compose logs -f SERVICE` to verify functionality
|
|
||||||
|
|
||||||
### Common Modifications
|
|
||||||
- **Toggle SSO**: Comment/uncomment `middlewares=authelia@docker` label
|
|
||||||
- **Change port**: Update `loadbalancer.server.port` label
|
|
||||||
- **Add VPN routing**: Change to `network_mode: "service:gluetun"`, map ports in Gluetun
|
|
||||||
- **Update subdomain**: Modify `Host()` rule in Traefik labels
|
|
||||||
- **Environment changes**: Update in `.env`, redeploy: `docker compose up -d`
|
|
||||||
|
|
||||||
## Project-Specific Conventions
|
|
||||||
|
|
||||||
### Why Traefik vs Nginx Proxy Manager
|
|
||||||
- **File-based configuration**: AI can modify labels/YAML directly (no web UI clicks)
|
|
||||||
- **Docker label discovery**: Services auto-register routes when deployed
|
|
||||||
- **Let's Encrypt automation**: Wildcard cert via DuckDNS DNS challenge (single cert for all services)
|
|
||||||
- **Dynamic reloading**: Changes apply without container restarts
|
|
||||||
|
|
||||||
### Authelia Password Generation
|
|
||||||
Secrets auto-generated by `setup-homelab.sh`:
|
|
||||||
- JWT secret: `openssl rand -hex 64`
|
|
||||||
- Session secret: `openssl rand -hex 64`
|
|
||||||
- Encryption key: `openssl rand -hex 64`
|
|
||||||
- Admin password: Hashed with `docker run authelia/authelia:latest authelia crypto hash generate argon2`
|
|
||||||
- Stored in `.env` file, never committed to git
|
|
||||||
|
|
||||||
### DuckDNS Wildcard Certificate
|
|
||||||
- **Single certificate**: `*.yourdomain.duckdns.org` covers all subdomains
|
|
||||||
- **DNS challenge**: Traefik uses DuckDNS token for Let's Encrypt validation
|
|
||||||
- **Certificate storage**: `/opt/stacks/core/traefik/acme.json` (600 permissions)
|
|
||||||
- **Renewal**: Traefik handles automatically (90-day Let's Encrypt certs)
|
|
||||||
- **Usage**: Services use `tls.certresolver=letsencrypt` label (no per-service cert requests)
|
|
||||||
|
|
||||||
### Homepage Dashboard AI Configuration
|
|
||||||
Homepage (`/opt/stacks/dashboards/`) uses dynamic variable replacement:
|
|
||||||
- Services configured in `homepage/config/services.yaml`
|
|
||||||
- URLs use `${DOMAIN}` variable replaced at runtime
|
|
||||||
- AI can add/remove service entries by editing YAML
|
|
||||||
- Bookmarks, widgets configured similarly in separate YAML files
|
|
||||||
|
|
||||||
## Key Documentation References
|
|
||||||
|
|
||||||
- **[Getting Started](../docs/getting-started.md)**: Step-by-step deployment guide
|
|
||||||
- **[Docker Guidelines](../docs/docker-guidelines.md)**: Comprehensive service management patterns (1000+ lines)
|
|
||||||
- **[Services Reference](../docs/services-reference.md)**: All 60+ pre-configured services
|
|
||||||
- **[Proxying External Hosts](../docs/proxying-external-hosts.md)**: Traefik file provider patterns for non-Docker services
|
|
||||||
- **[Quick Reference](../docs/quick-reference.md)**: Command cheat sheet and troubleshooting
|
|
||||||
|
|
||||||
## Pre-Deployment Safety Checks
|
|
||||||
|
|
||||||
Before deploying any service changes:
|
|
||||||
- [ ] YAML syntax valid (`docker compose config`)
|
|
||||||
- [ ] No port conflicts (check `docker ps --format "table {{.Names}}\t{{.Ports}}"`)
|
|
||||||
- [ ] Networks exist (`docker network ls | grep -E 'traefik-network|homelab-network'`)
|
|
||||||
- [ ] Volume paths correct (`/opt/stacks/` for configs, `/mnt/` for large data)
|
|
||||||
- [ ] `.env` variables populated (source stack `.env` and check `echo $DOMAIN`)
|
|
||||||
- [ ] Traefik labels complete (enable, rule, entrypoint, tls, middleware)
|
|
||||||
- [ ] SSO appropriate (default enabled, bypass only for Plex/Jellyfin)
|
|
||||||
- [ ] VPN routing configured if download service (network_mode + Gluetun ports)
|
|
||||||
- [ ] LinuxServer.io image available (check hub.docker.com/u/linuxserver)
|
|
||||||
|
|
||||||
## Troubleshooting Common Issues
|
|
||||||
|
|
||||||
### Service Won't Start
|
|
||||||
```bash
|
|
||||||
docker compose logs SERVICE # Check error messages
|
|
||||||
docker compose config # Validate YAML syntax
|
|
||||||
docker ps -a | grep SERVICE # Check exit code
|
|
||||||
```
|
|
||||||
Common causes: Port conflict, missing `.env` variable, network not found, volume permission denied
|
|
||||||
|
|
||||||
### Traefik Not Routing
|
|
||||||
```bash
|
|
||||||
docker logs traefik | grep SERVICE # Check if route registered
|
|
||||||
curl -k https://traefik.${DOMAIN}/api/http/routers # Inspect routes (if API enabled)
|
|
||||||
```
|
|
||||||
Verify: Service on `traefik-network`, labels correctly formatted, `traefik.enable=true`, Traefik restarted after label changes
|
|
||||||
|
|
||||||
### Authelia SSO Not Prompting
|
|
||||||
Check middleware: `docker compose config | grep -A5 SERVICE | grep authelia`
|
|
||||||
Verify: Authelia container running, middleware label present, no bypass rule in `authelia/configuration.yml`
|
|
||||||
|
|
||||||
### VPN Not Working (Gluetun)
|
|
||||||
```bash
|
|
||||||
docker exec gluetun sh -c "curl -s ifconfig.me" # Check VPN IP
|
|
||||||
docker logs gluetun | grep -i wireguard # Verify connection
|
|
||||||
```
|
|
||||||
Verify: `SURFSHARK_PRIVATE_KEY` set in `.env`, service using `network_mode: "service:gluetun"`, ports mapped in Gluetun
|
|
||||||
|
|
||||||
### Wildcard Certificate Issues
|
|
||||||
```bash
|
|
||||||
docker logs traefik | grep -i certificate
|
|
||||||
ls -lh /opt/stacks/core/traefik/acme.json # Should be 600 permissions
|
|
||||||
```
|
|
||||||
Verify: DuckDNS token valid, `DUCKDNS_TOKEN` in `.env`, DNS propagation (wait 2-5 min), acme.json writable by Traefik
|
|
||||||
|
|
||||||
## AI Management Capabilities
|
|
||||||
|
|
||||||
You can manage this homelab by:
|
|
||||||
- **Creating services**: Generate compose files in `/opt/stacks/` with proper Traefik labels
|
|
||||||
- **Modifying routes**: Edit Traefik labels in compose files
|
|
||||||
- **Managing external hosts**: Update `/opt/stacks/core/traefik/dynamic/external.yml`
|
|
||||||
- **Configuring Homepage**: Edit `services.yaml`, `bookmarks.yaml` in homepage config
|
|
||||||
- **Toggling SSO**: Add/remove Authelia middleware labels
|
|
||||||
- **Adding VPN routing**: Change network_mode and update Gluetun ports
|
|
||||||
- **Environment management**: Update `.env` (but remind users to manually copy to stacks)
|
|
||||||
|
|
||||||
### What NOT to Do
|
|
||||||
- Never commit `.env` files to git (contain secrets)
|
|
||||||
- Don't use `docker run` for persistent services (use compose in `/opt/stacks/`)
|
|
||||||
- Don't manually request Let's Encrypt certs (Traefik handles via wildcard)
|
|
||||||
- Don't edit Authelia/Traefik config via web UI (use YAML files)
|
|
||||||
- Don't expose download clients without VPN (route through Gluetun)
|
|
||||||
|
|
||||||
## Quick Command Reference
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# View all running containers
|
|
||||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
|
||||||
|
|
||||||
# Check service logs
|
|
||||||
cd /opt/stacks/STACK && docker compose logs -f SERVICE
|
|
||||||
|
|
||||||
# Restart specific service
|
|
||||||
cd /opt/stacks/STACK && docker compose restart SERVICE
|
|
||||||
|
|
||||||
# Update images and redeploy
|
|
||||||
cd /opt/stacks/STACK && docker compose pull && docker compose up -d
|
|
||||||
|
|
||||||
# Validate compose file
|
|
||||||
docker compose -f /opt/stacks/STACK/docker-compose.yml config
|
|
||||||
|
|
||||||
# Check Traefik routes
|
|
||||||
docker logs traefik | grep -i "Creating router\|Adding route"
|
|
||||||
|
|
||||||
# Check network connectivity
|
|
||||||
docker exec SERVICE ping -c 2 traefik
|
|
||||||
|
|
||||||
# View environment variables
|
|
||||||
cd /opt/stacks/STACK && docker compose config | grep -A20 "environment:"
|
|
||||||
|
|
||||||
# Test NVIDIA GPU access
|
|
||||||
docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi
|
|
||||||
```
|
|
||||||
|
|
||||||
## User Context Notes
|
|
||||||
|
|
||||||
- **User**: kelin (PUID=1000, PGID=1000)
|
|
||||||
- **Repository**: `/home/kelin/AI-Homelab/`
|
|
||||||
- **Testing Phase**: Round 6+ (focus on reliability, error handling, deployment robustness)
|
|
||||||
- **Recent work**: Script automation, idempotent setup, health checks, automated secret generation
|
|
||||||
|
|
||||||
When interacting with users, prioritize **security** (SSO by default), **consistency** (follow existing patterns), and **stack-awareness** (consider service dependencies). Always explain the "why" behind architectural decisions and reference specific files/paths when describing changes.
|
|
||||||
|
|||||||
@@ -1,4 +1,18 @@
|
|||||||
---
|
---
|
||||||
|
# Currently Installed Services - Grouped by Stack
|
||||||
|
|
||||||
|
- Dashboards:
|
||||||
|
- Homepage:
|
||||||
|
icon: homepage.png
|
||||||
|
href: https://home.kelin-casa.duckdns.org
|
||||||
|
description: This Dashboard
|
||||||
|
container: homepage
|
||||||
|
|
||||||
|
- Homarr:
|
||||||
|
icon: homarr.png
|
||||||
|
href: https://homarr.kelin-casa.duckdns.org
|
||||||
|
description: Alternative Dashboard
|
||||||
|
container: homarr
|
||||||
|
|
||||||
- Core:
|
- Core:
|
||||||
- Dockge:
|
- Dockge:
|
||||||
@@ -19,25 +33,7 @@
|
|||||||
description: Authentication Portal
|
description: Authentication Portal
|
||||||
container: authelia
|
container: authelia
|
||||||
|
|
||||||
- Dashboards:
|
|
||||||
- Homepage:
|
|
||||||
icon: homepage.png
|
|
||||||
href: https://home.kelin-casa.duckdns.org
|
|
||||||
description: This Dashboard
|
|
||||||
container: homepage
|
|
||||||
|
|
||||||
- Homarr:
|
|
||||||
icon: homarr.png
|
|
||||||
href: https://homarr.kelin-casa.duckdns.org
|
|
||||||
description: Alternative Dashboard
|
|
||||||
container: homarr
|
|
||||||
- Infrastructure:
|
- Infrastructure:
|
||||||
- VS Code Server:
|
|
||||||
icon: vscode.png
|
|
||||||
href: https://code.kelin-casa.duckdns.org
|
|
||||||
description: Browser-based IDE
|
|
||||||
container: code-server
|
|
||||||
|
|
||||||
- Dozzle:
|
- Dozzle:
|
||||||
icon: dozzle.png
|
icon: dozzle.png
|
||||||
href: https://dozzle.kelin-casa.duckdns.org
|
href: https://dozzle.kelin-casa.duckdns.org
|
||||||
@@ -56,36 +52,7 @@
|
|||||||
description: Network-wide Ad Blocking
|
description: Network-wide Ad Blocking
|
||||||
container: pihole
|
container: pihole
|
||||||
|
|
||||||
- Monitoring:
|
# Available to Install - Grouped by Stack
|
||||||
- Grafana:
|
|
||||||
icon: grafana.png
|
|
||||||
href: https://grafana.kelin-casa.duckdns.org
|
|
||||||
description: Metrics Dashboard
|
|
||||||
container: grafana
|
|
||||||
|
|
||||||
- Prometheus:
|
|
||||||
icon: prometheus.png
|
|
||||||
href: https://prometheus.kelin-casa.duckdns.org
|
|
||||||
description: Metrics Collection
|
|
||||||
container: prometheus
|
|
||||||
|
|
||||||
- Uptime Kuma:
|
|
||||||
icon: uptime-kuma.png
|
|
||||||
href: https://uptime-kuma.kelin-casa.duckdns.org
|
|
||||||
description: Uptime Monitoring
|
|
||||||
container: uptime-kuma
|
|
||||||
|
|
||||||
- Loki:
|
|
||||||
icon: loki.png
|
|
||||||
href: https://loki.kelin-casa.duckdns.org
|
|
||||||
description: Log Aggregation
|
|
||||||
container: loki
|
|
||||||
|
|
||||||
- cAdvisor:
|
|
||||||
icon: cadvisor.png
|
|
||||||
href: https://cadvisor.kelin-casa.duckdns.org
|
|
||||||
description: Container Metrics
|
|
||||||
container: cadvisor
|
|
||||||
|
|
||||||
- Media:
|
- Media:
|
||||||
- Jellyfin:
|
- Jellyfin:
|
||||||
@@ -107,80 +74,53 @@
|
|||||||
container: calibre-web
|
container: calibre-web
|
||||||
|
|
||||||
- Media Management:
|
- Media Management:
|
||||||
- Lidarr:
|
|
||||||
icon: lidarr.png
|
|
||||||
href: https://lidarr.kelin-casa.duckdns.org
|
|
||||||
description: Music Automation
|
|
||||||
container: lidarr
|
|
||||||
- Readarr:
|
|
||||||
icon: readarr.png
|
|
||||||
href: https://readarr.kelin-casa.duckdns.org
|
|
||||||
description: Books Automation
|
|
||||||
container: readarr
|
|
||||||
- Radarr:
|
|
||||||
icon: radarr.png
|
|
||||||
href: https://radarr.kelin-casa.duckdns.org
|
|
||||||
description: Movies Automation
|
|
||||||
container: radarr
|
|
||||||
- Sonarr:
|
- Sonarr:
|
||||||
icon: sonarr.png
|
icon: sonarr.png
|
||||||
href: https://sonarr.kelin-casa.duckdns.org
|
href: https://sonarr.kelin-casa.duckdns.org
|
||||||
description: TV Shows Automation
|
description: TV Shows Automation
|
||||||
container: sonarr
|
container: sonarr
|
||||||
|
|
||||||
|
- Radarr:
|
||||||
|
icon: radarr.png
|
||||||
|
href: https://radarr.kelin-casa.duckdns.org
|
||||||
|
description: Movies Automation
|
||||||
|
container: radarr
|
||||||
|
|
||||||
- Prowlarr:
|
- Prowlarr:
|
||||||
icon: prowlarr.png
|
icon: prowlarr.png
|
||||||
href: https://prowlarr.kelin-casa.duckdns.org
|
href: https://prowlarr.kelin-casa.duckdns.org
|
||||||
description: Indexer Manager
|
description: Indexer Manager
|
||||||
container: prowlarr
|
container: prowlarr
|
||||||
|
|
||||||
|
- Readarr:
|
||||||
|
icon: readarr.png
|
||||||
|
href: https://readarr.kelin-casa.duckdns.org
|
||||||
|
description: Books Automation
|
||||||
|
container: readarr
|
||||||
|
|
||||||
|
- Lidarr:
|
||||||
|
icon: lidarr.png
|
||||||
|
href: https://lidarr.kelin-casa.duckdns.org
|
||||||
|
description: Music Automation
|
||||||
|
container: lidarr
|
||||||
|
|
||||||
- Mylar3:
|
- Mylar3:
|
||||||
icon: mylar.png
|
icon: mylar.png
|
||||||
href: https://mylar.kelin-casa.duckdns.org
|
href: https://mylar.kelin-casa.duckdns.org
|
||||||
description: Comics Manager
|
description: Comics Manager
|
||||||
container: mylar3
|
container: mylar3
|
||||||
|
|
||||||
- Productivity:
|
- qBittorrent:
|
||||||
- Nextcloud:
|
icon: qbittorrent.png
|
||||||
icon: nextcloud.png
|
href: https://qbit.kelin-casa.duckdns.org
|
||||||
href: https://nextcloud.kelin-casa.duckdns.org
|
description: Torrent Client
|
||||||
description: Cloud Storage & Collaboration
|
container: qbittorrent
|
||||||
container: nextcloud
|
|
||||||
|
|
||||||
- Mealie:
|
- Tdarr:
|
||||||
icon: mealie.png
|
icon: tdarr.png
|
||||||
href: https://mealie.kelin-casa.duckdns.org
|
href: https://tdarr.kelin-casa.duckdns.org
|
||||||
description: Recipe Manager
|
description: Media Transcoding
|
||||||
container: mealie
|
container: tdarr
|
||||||
|
|
||||||
- WordPress:
|
|
||||||
icon: wordpress.png
|
|
||||||
href: https://wordpress.kelin-casa.duckdns.org
|
|
||||||
description: CMS Platform
|
|
||||||
container: wordpress
|
|
||||||
|
|
||||||
- Jupyter:
|
|
||||||
icon: jupyter.png
|
|
||||||
href: https://jupyter.kelin-casa.duckdns.org
|
|
||||||
description: Data Science Notebooks
|
|
||||||
container: jupyter
|
|
||||||
|
|
||||||
- Gitea:
|
|
||||||
icon: gitea.png
|
|
||||||
href: https://gitea.kelin-casa.duckdns.org
|
|
||||||
description: Git Repository
|
|
||||||
container: gitea
|
|
||||||
|
|
||||||
- Wiki:
|
|
||||||
- BookStack:
|
|
||||||
icon: bookstack.png
|
|
||||||
href: https://bookstack.kelin-casa.duckdns.org
|
|
||||||
description: Wiki Platform
|
|
||||||
container: bookstack
|
|
||||||
|
|
||||||
- DokuWiki:
|
|
||||||
icon: dokuwiki.png
|
|
||||||
href: https://dokuwiki.kelin-casa.duckdns.org
|
|
||||||
description: Simple Wiki
|
|
||||||
container: dokuwiki
|
|
||||||
|
|
||||||
- Home Automation:
|
- Home Automation:
|
||||||
- Home Assistant:
|
- Home Assistant:
|
||||||
@@ -213,6 +153,99 @@
|
|||||||
description: MQTT Broker
|
description: MQTT Broker
|
||||||
container: mosquitto
|
container: mosquitto
|
||||||
|
|
||||||
|
- Productivity:
|
||||||
|
- Nextcloud:
|
||||||
|
icon: nextcloud.png
|
||||||
|
href: https://nextcloud.kelin-casa.duckdns.org
|
||||||
|
description: Cloud Storage & Collaboration
|
||||||
|
container: nextcloud
|
||||||
|
|
||||||
|
- Gitea:
|
||||||
|
icon: gitea.png
|
||||||
|
href: https://gitea.kelin-casa.duckdns.org
|
||||||
|
description: Git Repository
|
||||||
|
container: gitea
|
||||||
|
|
||||||
|
- Mealie:
|
||||||
|
icon: mealie.png
|
||||||
|
href: https://mealie.kelin-casa.duckdns.org
|
||||||
|
description: Recipe Manager
|
||||||
|
container: mealie
|
||||||
|
|
||||||
|
- BookStack:
|
||||||
|
icon: bookstack.png
|
||||||
|
href: https://bookstack.kelin-casa.duckdns.org
|
||||||
|
description: Wiki Platform
|
||||||
|
container: bookstack
|
||||||
|
|
||||||
|
- DokuWiki:
|
||||||
|
icon: dokuwiki.png
|
||||||
|
href: https://dokuwiki.kelin-casa.duckdns.org
|
||||||
|
description: Simple Wiki
|
||||||
|
container: dokuwiki
|
||||||
|
|
||||||
|
- WordPress:
|
||||||
|
icon: wordpress.png
|
||||||
|
href: https://wordpress.kelin-casa.duckdns.org
|
||||||
|
description: CMS Platform
|
||||||
|
container: wordpress
|
||||||
|
|
||||||
|
- Monitoring Stack:
|
||||||
|
- Grafana:
|
||||||
|
icon: grafana.png
|
||||||
|
href: https://grafana.kelin-casa.duckdns.org
|
||||||
|
description: Metrics Dashboard
|
||||||
|
container: grafana
|
||||||
|
|
||||||
|
- Prometheus:
|
||||||
|
icon: prometheus.png
|
||||||
|
href: https://prometheus.kelin-casa.duckdns.org
|
||||||
|
description: Metrics Collection
|
||||||
|
container: prometheus
|
||||||
|
|
||||||
|
- Uptime Kuma:
|
||||||
|
icon: uptime-kuma.png
|
||||||
|
href: https://uptime-kuma.kelin-casa.duckdns.org
|
||||||
|
description: Uptime Monitoring
|
||||||
|
container: uptime-kuma
|
||||||
|
|
||||||
|
- Loki:
|
||||||
|
icon: loki.png
|
||||||
|
href: https://loki.kelin-casa.duckdns.org
|
||||||
|
description: Log Aggregation
|
||||||
|
container: loki
|
||||||
|
|
||||||
|
- cAdvisor:
|
||||||
|
icon: cadvisor.png
|
||||||
|
href: https://cadvisor.kelin-casa.duckdns.org
|
||||||
|
description: Container Metrics
|
||||||
|
container: cadvisor
|
||||||
|
|
||||||
|
- Development:
|
||||||
|
- VS Code Server:
|
||||||
|
icon: vscode.png
|
||||||
|
href: https://code.kelin-casa.duckdns.org
|
||||||
|
description: Browser-based IDE
|
||||||
|
container: code-server
|
||||||
|
|
||||||
|
- GitLab:
|
||||||
|
icon: gitlab.png
|
||||||
|
href: https://gitlab.kelin-casa.duckdns.org
|
||||||
|
description: DevOps Platform
|
||||||
|
container: gitea
|
||||||
|
|
||||||
|
- Jupyter:
|
||||||
|
icon: jupyter.png
|
||||||
|
href: https://jupyter.kelin-casa.duckdns.org
|
||||||
|
description: Data Science Notebooks
|
||||||
|
container: jupyter
|
||||||
|
|
||||||
|
- pgAdmin:
|
||||||
|
icon: pgadmin.png
|
||||||
|
href: https://pgadmin.kelin-casa.duckdns.org
|
||||||
|
description: PostgreSQL Admin
|
||||||
|
container: pgadmin
|
||||||
|
|
||||||
- Utilities:
|
- Utilities:
|
||||||
- Backrest:
|
- Backrest:
|
||||||
icon: mdi-backup-restore
|
icon: mdi-backup-restore
|
||||||
@@ -238,31 +271,6 @@
|
|||||||
description: Form Builder
|
description: Form Builder
|
||||||
container: formio
|
container: formio
|
||||||
|
|
||||||
- VPN Protected:
|
|
||||||
- gluetun:
|
|
||||||
icon: gluetun.png
|
|
||||||
href: https://home.kelin-casa.duckdns.org
|
|
||||||
description: VPN Client
|
|
||||||
container: gluetun
|
|
||||||
- Downloaders:
|
|
||||||
- qBittorrent:
|
|
||||||
icon: qbittorrent.png
|
|
||||||
href: https://qbit.kelin-casa.duckdns.org
|
|
||||||
description: Torrent Client
|
|
||||||
container: qbittorrent
|
|
||||||
|
|
||||||
- Transcoders:
|
|
||||||
- Unmanic:
|
|
||||||
icon: unmanic.png
|
|
||||||
href: https://unmanic.kelin-casa.duckdns.org
|
|
||||||
description: Media Transcoding
|
|
||||||
container: unmanic
|
|
||||||
- Tdarr:
|
|
||||||
icon: tdarr.png
|
|
||||||
href: https://tdarr.kelin-casa.duckdns.org
|
|
||||||
description: Media Transcoding
|
|
||||||
container: tdarr-server
|
|
||||||
|
|
||||||
- Alternatives:
|
- Alternatives:
|
||||||
- Portainer:
|
- Portainer:
|
||||||
icon: portainer.png
|
icon: portainer.png
|
||||||
|
|||||||
@@ -3,11 +3,6 @@
|
|||||||
# Deploy this stack FIRST before any other services
|
# Deploy this stack FIRST before any other services
|
||||||
# Place in /opt/stacks/core/docker-compose.yml
|
# Place in /opt/stacks/core/docker-compose.yml
|
||||||
|
|
||||||
# Service Access URLs:
|
|
||||||
# - DuckDNS: No web UI (updates IP automatically)
|
|
||||||
# - Traefik: https://traefik.${DOMAIN}
|
|
||||||
# - Authelia: https://auth.${DOMAIN}
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# DuckDNS - Dynamic DNS updater
|
# DuckDNS - Dynamic DNS updater
|
||||||
# Updates your public IP automatically for Let's Encrypt SSL
|
# Updates your public IP automatically for Let's Encrypt SSL
|
||||||
@@ -23,7 +18,7 @@ services:
|
|||||||
- TOKEN=${DUCKDNS_TOKEN} # Your DuckDNS token
|
- TOKEN=${DUCKDNS_TOKEN} # Your DuckDNS token
|
||||||
- UPDATE_IP=ipv4 # or ipv6, or both
|
- UPDATE_IP=ipv4 # or ipv6, or both
|
||||||
volumes:
|
volumes:
|
||||||
- ./duckdns:/config
|
- /opt/stacks/core/duckdns:/config
|
||||||
labels:
|
labels:
|
||||||
- "homelab.category=infrastructure"
|
- "homelab.category=infrastructure"
|
||||||
- "homelab.description=Dynamic DNS updater"
|
- "homelab.description=Dynamic DNS updater"
|
||||||
@@ -45,9 +40,9 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- ./traefik/traefik.yml:/traefik.yml:ro
|
- /opt/stacks/core/traefik/traefik.yml:/traefik.yml:ro
|
||||||
- ./traefik/dynamic:/dynamic:ro
|
- /opt/stacks/core/traefik/dynamic:/dynamic:ro
|
||||||
- ./traefik/acme.json:/acme.json
|
- /opt/stacks/core/traefik/acme.json:/acme.json
|
||||||
environment:
|
environment:
|
||||||
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # If using Cloudflare DNS challenge
|
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # If using Cloudflare DNS challenge
|
||||||
- DUCKDNS_TOKEN=${DUCKDNS_TOKEN} # If using DuckDNS
|
- DUCKDNS_TOKEN=${DUCKDNS_TOKEN} # If using DuckDNS
|
||||||
@@ -57,8 +52,6 @@ services:
|
|||||||
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.traefik.entrypoints=websecure"
|
- "traefik.http.routers.traefik.entrypoints=websecure"
|
||||||
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.traefik.tls.domains[0].main=${DOMAIN}"
|
|
||||||
- "traefik.http.routers.traefik.tls.domains[0].sans=*.${DOMAIN}"
|
|
||||||
- "traefik.http.routers.traefik.middlewares=authelia@docker"
|
- "traefik.http.routers.traefik.middlewares=authelia@docker"
|
||||||
- "traefik.http.routers.traefik.service=api@internal"
|
- "traefik.http.routers.traefik.service=api@internal"
|
||||||
# Global HTTP to HTTPS redirect
|
# Global HTTP to HTTPS redirect
|
||||||
@@ -78,29 +71,61 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- traefik-network
|
- traefik-network
|
||||||
volumes:
|
volumes:
|
||||||
- ./authelia/configuration.yml:/config/configuration.yml:ro
|
- /opt/stacks/core/authelia/configuration.yml:/config/configuration.yml:ro
|
||||||
- ./authelia/users_database.yml:/config/users_database.yml
|
- /opt/stacks/core/authelia/users_database.yml:/config/users_database.yml
|
||||||
- authelia-data:/data
|
- authelia-data:/config
|
||||||
environment:
|
environment:
|
||||||
- TZ=${TZ}
|
- TZ=${TZ}
|
||||||
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
|
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
|
||||||
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
|
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
|
||||||
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
||||||
|
- AUTHELIA_NOTIFIER_SMTP_PASSWORD=${SMTP_PASSWORD} # If using email notifications
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN}`)"
|
- "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.authelia.entrypoints=websecure"
|
- "traefik.http.routers.authelia.entrypoints=websecure"
|
||||||
- "traefik.http.routers.authelia.tls=true"
|
- "traefik.http.routers.authelia.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
|
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
|
||||||
# Authelia middleware for other services
|
# 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.address=http://authelia:9091/api/verify?rd=https://auth.${DOMAIN}"
|
||||||
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
|
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
|
||||||
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"
|
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"
|
||||||
- "x-dockge.url=https://authelia.${DOMAIN}"
|
|
||||||
- "x-dockge.url=https://authelia.${DOMAIN}"
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- traefik
|
- traefik
|
||||||
|
|
||||||
|
# Gluetun - VPN client (Surfshark WireGuard)
|
||||||
|
# Routes download clients through VPN for security
|
||||||
|
gluetun:
|
||||||
|
image: qmcgaw/gluetun:latest
|
||||||
|
container_name: gluetun
|
||||||
|
restart: unless-stopped
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
- traefik-network
|
||||||
|
ports:
|
||||||
|
- "8888:8888/tcp" # HTTP proxy
|
||||||
|
- "8388:8388/tcp" # Shadowsocks
|
||||||
|
- "8388:8388/udp" # Shadowsocks
|
||||||
|
- "8081:8080" # qBittorrent web UI (mapped to 8081 to avoid Traefik conflict)
|
||||||
|
- "6881:6881" # qBittorrent
|
||||||
|
- "6881:6881/udp" # qBittorrent
|
||||||
|
volumes:
|
||||||
|
- /opt/stacks/core/gluetun:/gluetun
|
||||||
|
environment:
|
||||||
|
- VPN_SERVICE_PROVIDER=surfshark
|
||||||
|
- VPN_TYPE=wireguard
|
||||||
|
- WIREGUARD_PRIVATE_KEY=${SURFSHARK_PRIVATE_KEY}
|
||||||
|
- WIREGUARD_ADDRESSES=${SURFSHARK_ADDRESSES}
|
||||||
|
- SERVER_COUNTRIES=${VPN_SERVER_COUNTRIES:-Netherlands}
|
||||||
|
- TZ=${TZ}
|
||||||
|
labels:
|
||||||
|
- "homelab.category=infrastructure"
|
||||||
|
- "homelab.description=VPN client for secure downloads"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
authelia-data:
|
authelia-data:
|
||||||
driver: local
|
driver: local
|
||||||
@@ -108,3 +133,5 @@ volumes:
|
|||||||
networks:
|
networks:
|
||||||
traefik-network:
|
traefik-network:
|
||||||
external: true
|
external: true
|
||||||
|
homelab-network:
|
||||||
|
external: true
|
||||||
|
|||||||
@@ -17,15 +17,6 @@ services:
|
|||||||
# Access at: http://server-ip:9090
|
# Access at: http://server-ip:9090
|
||||||
prometheus:
|
prometheus:
|
||||||
image: prom/prometheus:v2.48.1
|
image: prom/prometheus:v2.48.1
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.75'
|
|
||||||
memory: 512M
|
|
||||||
pids: 1024
|
|
||||||
reservations:
|
|
||||||
cpus: '0.25'
|
|
||||||
memory: 256M
|
|
||||||
container_name: prometheus
|
container_name: prometheus
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -44,6 +35,7 @@ services:
|
|||||||
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
||||||
- '--web.console.templates=/etc/prometheus/consoles'
|
- '--web.console.templates=/etc/prometheus/consoles'
|
||||||
- '--web.enable-lifecycle'
|
- '--web.enable-lifecycle'
|
||||||
|
user: "${PUID:-1000}:${PGID:-1000}"
|
||||||
labels:
|
labels:
|
||||||
- "homelab.category=monitoring"
|
- "homelab.category=monitoring"
|
||||||
- "homelab.description=Metrics collection and time-series database"
|
- "homelab.description=Metrics collection and time-series database"
|
||||||
@@ -60,15 +52,6 @@ services:
|
|||||||
# Default credentials: admin / admin (change on first login)
|
# Default credentials: admin / admin (change on first login)
|
||||||
grafana:
|
grafana:
|
||||||
image: grafana/grafana:10.2.3
|
image: grafana/grafana:10.2.3
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.50'
|
|
||||||
memory: 256M
|
|
||||||
pids: 512
|
|
||||||
reservations:
|
|
||||||
cpus: '0.25'
|
|
||||||
memory: 128M
|
|
||||||
container_name: grafana
|
container_name: grafana
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -153,22 +136,11 @@ services:
|
|||||||
- "traefik.http.routers.cadvisor.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.cadvisor.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.cadvisor.middlewares=authelia@docker"
|
- "traefik.http.routers.cadvisor.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.cadvisor.loadbalancer.server.port=8080"
|
- "traefik.http.services.cadvisor.loadbalancer.server.port=8080"
|
||||||
- "x-dockge.url=https://cadvisor.${DOMAIN}"
|
|
||||||
- "x-dockge.url=https://cadvisor.${DOMAIN}"
|
|
||||||
|
|
||||||
# Uptime Kuma - Uptime monitoring
|
# Uptime Kuma - Uptime monitoring
|
||||||
# Access at: https://uptime-kuma.${DOMAIN}
|
# Access at: https://uptime-kuma.${DOMAIN}
|
||||||
uptime-kuma:
|
uptime-kuma:
|
||||||
image: louislam/uptime-kuma:1
|
image: louislam/uptime-kuma:1
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.50'
|
|
||||||
memory: 256M
|
|
||||||
pids: 512
|
|
||||||
reservations:
|
|
||||||
cpus: '0.25'
|
|
||||||
memory: 128M
|
|
||||||
container_name: uptime-kuma
|
container_name: uptime-kuma
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -193,15 +165,6 @@ services:
|
|||||||
# Access at: http://server-ip:3100
|
# Access at: http://server-ip:3100
|
||||||
loki:
|
loki:
|
||||||
image: grafana/loki:2.9.3
|
image: grafana/loki:2.9.3
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.75'
|
|
||||||
memory: 512M
|
|
||||||
pids: 1024
|
|
||||||
reservations:
|
|
||||||
cpus: '0.25'
|
|
||||||
memory: 256M
|
|
||||||
container_name: loki
|
container_name: loki
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -214,6 +177,7 @@ services:
|
|||||||
- ./config/loki:/etc/loki
|
- ./config/loki:/etc/loki
|
||||||
- loki-data:/loki
|
- loki-data:/loki
|
||||||
command: -config.file=/etc/loki/loki-config.yml
|
command: -config.file=/etc/loki/loki-config.yml
|
||||||
|
user: "${PUID:-1000}:${PGID:-1000}"
|
||||||
labels:
|
labels:
|
||||||
- "homelab.category=monitoring"
|
- "homelab.category=monitoring"
|
||||||
- "homelab.description=Log aggregation system"
|
- "homelab.description=Log aggregation system"
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
**Docker Image:** [linuxserver/qbittorrent](https://hub.docker.com/r/linuxserver/qbittorrent)
|
**Docker Image:** [linuxserver/qbittorrent](https://hub.docker.com/r/linuxserver/qbittorrent)
|
||||||
**Default Stack:** `media.yml`
|
**Default Stack:** `media.yml`
|
||||||
**Network Mode:** Via Gluetun (VPN container)
|
**Network Mode:** Via Gluetun (VPN container)
|
||||||
**Web UI:** `http://SERVER_IP:8080` (via Gluetun)
|
**Web UI:** `http://SERVER_IP:8081` (via Gluetun)
|
||||||
**Authentication:** Username/password (default: admin/adminadmin)
|
**Authentication:** Username/password (default: admin/adminadmin)
|
||||||
**Ports:** 8080 (WebUI via Gluetun), 6881 (incoming connections via Gluetun)
|
**Ports:** 8081 (WebUI via Gluetun), 6881 (incoming connections via Gluetun)
|
||||||
|
|
||||||
## What is qBittorrent?
|
## What is qBittorrent?
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ gluetun:
|
|||||||
devices:
|
devices:
|
||||||
- /dev/net/tun
|
- /dev/net/tun
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" # qBittorrent WebUI
|
- "8081:8080" # qBittorrent WebUI (host:container)
|
||||||
- "6881:6881" # qBittorrent incoming
|
- "6881:6881" # qBittorrent incoming
|
||||||
- "6881:6881/udp"
|
- "6881:6881/udp"
|
||||||
environment:
|
environment:
|
||||||
@@ -181,7 +181,7 @@ qbittorrent:
|
|||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- TZ=America/New_York
|
- TZ=America/New_York
|
||||||
- WEBUI_PORT=8080
|
- WEBUI_PORT=8081
|
||||||
volumes:
|
volumes:
|
||||||
- /opt/stacks/media/qbittorrent/config:/config
|
- /opt/stacks/media/qbittorrent/config:/config
|
||||||
- /mnt/downloads:/downloads
|
- /mnt/downloads:/downloads
|
||||||
@@ -191,7 +191,7 @@ qbittorrent:
|
|||||||
- `network_mode: "service:gluetun"` routes all traffic through VPN
|
- `network_mode: "service:gluetun"` routes all traffic through VPN
|
||||||
- Ports exposed on Gluetun, not qBittorrent
|
- Ports exposed on Gluetun, not qBittorrent
|
||||||
- No internet access if VPN down (kill switch)
|
- No internet access if VPN down (kill switch)
|
||||||
- Access via `http://SERVER_IP:8080` (Gluetun's port)
|
- Access via `http://SERVER_IP:8081` (Gluetun's port)
|
||||||
|
|
||||||
### Standalone Configuration (without VPN - NOT RECOMMENDED)
|
### Standalone Configuration (without VPN - NOT RECOMMENDED)
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ qbittorrent:
|
|||||||
container_name: qbittorrent
|
container_name: qbittorrent
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8081:8080"
|
||||||
- "6881:6881"
|
- "6881:6881"
|
||||||
- "6881:6881/udp"
|
- "6881:6881/udp"
|
||||||
environment:
|
environment:
|
||||||
@@ -238,7 +238,7 @@ qbittorrent:
|
|||||||
```
|
```
|
||||||
|
|
||||||
4. **Access Web UI:**
|
4. **Access Web UI:**
|
||||||
- Navigate to: `http://SERVER_IP:8080`
|
- Navigate to: `http://SERVER_IP:8081`
|
||||||
- Username: `admin`
|
- Username: `admin`
|
||||||
- Password: From logs above
|
- Password: From logs above
|
||||||
|
|
||||||
@@ -542,7 +542,7 @@ docker logs qbittorrent | tail -20
|
|||||||
docker logs gluetun | grep -i "connected"
|
docker logs gluetun | grep -i "connected"
|
||||||
|
|
||||||
# Test access
|
# Test access
|
||||||
curl http://localhost:8080
|
curl http://localhost:8081
|
||||||
```
|
```
|
||||||
|
|
||||||
### Slow Download Speeds
|
### Slow Download Speeds
|
||||||
|
|||||||
@@ -6,48 +6,45 @@ This document provides a comprehensive overview of all 60+ pre-configured servic
|
|||||||
|
|
||||||
| Stacks (10) | Services (70 + 6db) | SSO | Storage | Access URLs |
|
| Stacks (10) | Services (70 + 6db) | SSO | Storage | Access URLs |
|
||||||
|-------|----------|-----|---------|-------------|
|
|-------|----------|-----|---------|-------------|
|
||||||
| **📦 [core.yaml](../docker-compose/core.yml) (4)** | **Deploy First** | | | |
|
| **📦 core.yaml (4)** | **Deploy First** | | | |
|
||||||
| ├─ [DuckDNS](service-docs/duckdns.md) | Dynamic DNS updater | - | /opt/stacks/core/duckdns | No UI |
|
| ├─ DuckDNS | Dynamic DNS updater | - | /opt/stacks/core/duckdns | No UI |
|
||||||
| ├─ [Traefik](service-docs/traefik.md) | Reverse proxy + SSL | ✓ | /opt/stacks/core/traefik | traefik.${DOMAIN} |
|
| ├─ Traefik | Reverse proxy + SSL | ✓ | /opt/stacks/core/traefik | traefik.${DOMAIN} |
|
||||||
| ├─ [Authelia](service-docs/authelia.md) | SSO authentication | - | /opt/stacks/core/authelia | auth.${DOMAIN} |
|
| ├─ Authelia | SSO authentication | - | /opt/stacks/core/authelia | auth.${DOMAIN} |
|
||||||
| └─ [Gluetun](service-docs/gluetun.md) | VPN (Surfshark) | - | /opt/stacks/core/gluetun | No UI |
|
| └─ Gluetun | VPN (Surfshark) | - | /opt/stacks/core/gluetun | No UI |
|
||||||
| **🔧 [infrastructure.yaml](../docker-compose/infrastructure.yml) (8+5)** | **Deployed: 8** | | | |
|
| **🔧 infrastructure.yaml** (12) | | | | |
|
||||||
| ├─ [Dockge](service-docs/dockge.md) | Stack manager (PRIMARY) | ✓ | /opt/stacks/infrastructure | dockge.${DOMAIN} |
|
| ├─ Dockge | Stack manager (PRIMARY) | ✓ | /opt/stacks/infrastructure | dockge.${DOMAIN} |
|
||||||
| ├─ [Pi-hole](service-docs/pihole.md) | DNS + Ad blocking | ✓ | /opt/stacks/infrastructure | pihole.${DOMAIN} |
|
| ├─ Portainer | Container management | ✓ | /opt/stacks/infrastructure | portainer.${DOMAIN} |
|
||||||
| ├─ [Dozzle](service-docs/dozzle.md) | Docker log viewer | ✓ | /opt/stacks/infrastructure | dozzle.${DOMAIN} |
|
| ├─ Authentik Server | SSO with web UI | ✓ | /opt/stacks/authentik | authentik.${DOMAIN} |
|
||||||
| ├─ [Glances](service-docs/glances.md) | System monitoring | ✓ | /opt/stacks/infrastructure | glances.${DOMAIN} |
|
| │ ├─ authentik-worker | Background tasks | - | /opt/stacks/authentik | No UI |
|
||||||
|
| │ ├─ authentik-db | PostgreSQL | - | /opt/stacks/authentik | No UI |
|
||||||
|
| │ └─ authentik-redis | Cache/messaging | - | /opt/stacks/authentik | No UI |
|
||||||
|
| ├─ Pi-hole | DNS + Ad blocking | ✓ | /opt/stacks/infrastructure | pihole.${DOMAIN} |
|
||||||
| ├─ Watchtower | Auto container updates | - | /opt/stacks/infrastructure | No UI |
|
| ├─ Watchtower | Auto container updates | - | /opt/stacks/infrastructure | No UI |
|
||||||
| ├─ Code Server | VS Code in browser | ✓ | /opt/stacks/infrastructure | code.${DOMAIN} |
|
| ├─ Dozzle | Docker log viewer | ✓ | /opt/stacks/infrastructure | dozzle.${DOMAIN} |
|
||||||
| └─ [Docker Proxy](service-docs/docker-proxy.md) | Secure socket access | - | /opt/stacks/infrastructure | No UI |
|
| ├─ Glances | System monitoring | ✓ | /opt/stacks/infrastructure | glances.${DOMAIN} |
|
||||||
| **📦 [alternatives.yaml](../docker-compose/alternatives.yml) (6)** | **Not deployed** | | | |
|
| └─ Docker Proxy | Secure socket access | - | /opt/stacks/infrastructure | No UI |
|
||||||
| ├─ Plex | Media server (Alt) | ✗ | /mnt/media, /mnt/transcode | plex.${DOMAIN} |
|
| **📊 dashboards.yaml** (2) | | | | |
|
||||||
| ├─ Portainer | Container management | ✓ | /opt/stacks/alternatives | portainer.${DOMAIN} |
|
|
||||||
| ├─ Authentik Server | SSO with web UI | ✓ | /opt/stacks/alternatives | authentik.${DOMAIN} |
|
|
||||||
| │ ├─ authentik-worker | Background tasks | - | /opt/stacks/alternatives | No UI |
|
|
||||||
| │ ├─ authentik-db | PostgreSQL | - | /opt/stacks/alternatives | No UI |
|
|
||||||
| │ └─ authentik-redis | Cache/messaging | - | /opt/stacks/alternatives | No UI |
|
|
||||||
| **📊 [dashboards.yaml](../docker-compose/dashboards.yml)** (2) | | | | |
|
|
||||||
| ├─ Homepage | App dashboard (AI cfg) | ✓ | /opt/stacks/dashboards | home.${DOMAIN} |
|
| ├─ Homepage | App dashboard (AI cfg) | ✓ | /opt/stacks/dashboards | home.${DOMAIN} |
|
||||||
| └─ Homarr | Modern dashboard | ✓ | /opt/stacks/dashboards | homarr.${DOMAIN} |
|
| └─ Homarr | Modern dashboard | ✓ | /opt/stacks/dashboards | homarr.${DOMAIN} |
|
||||||
| **🎬 [media.yml](../docker-compose/media.yml)** (3) | | | | |
|
| **🎬 media** (6) | | | | |
|
||||||
|
| ├─ Plex | Media server | ✗ | /mnt/media, /mnt/transcode | plex.${DOMAIN} |
|
||||||
| ├─ Jellyfin | Media server (OSS) | ✗ | /mnt/media, /mnt/transcode | jellyfin.${DOMAIN} |
|
| ├─ Jellyfin | Media server (OSS) | ✗ | /mnt/media, /mnt/transcode | jellyfin.${DOMAIN} |
|
||||||
| ├─ Calibre-Web | Ebook reader | ✓ | /opt/stacks/media, /mnt/media | calibre.${DOMAIN} |
|
| ├─ Sonarr | TV automation | ✓ | /opt/stacks/media, /mnt/media | sonarr.${DOMAIN} |
|
||||||
|
| ├─ Radarr | Movie automation | ✓ | /opt/stacks/media, /mnt/media | radarr.${DOMAIN} |
|
||||||
|
| ├─ Prowlarr | Indexer manager | ✓ | /opt/stacks/media | prowlarr.${DOMAIN} |
|
||||||
| └─ qBittorrent | Torrent (via VPN) | ✓ | /mnt/downloads | qbit.${DOMAIN} |
|
| └─ qBittorrent | Torrent (via VPN) | ✓ | /mnt/downloads | qbit.${DOMAIN} |
|
||||||
| **📚 [media-management.yml](../docker-compose/media-management.yml)** (10) | | | | |
|
| **📚 media-extended.yaml** (10) | | | | |
|
||||||
| ├─ Sonarr | TV automation | ✓ | /opt/stacks/media-mgmt, /mnt/media | sonarr.${DOMAIN} |
|
| ├─ Readarr | Ebooks/Audiobooks | ✓ | /opt/stacks/media-ext, /mnt/media | readarr.${DOMAIN} |
|
||||||
| ├─ Radarr | Movie automation | ✓ | /opt/stacks/media-mgmt, /mnt/media | radarr.${DOMAIN} |
|
| ├─ Lidarr | Music manager | ✓ | /opt/stacks/media-ext, /mnt/media | lidarr.${DOMAIN} |
|
||||||
| ├─ Prowlarr | Indexer manager | ✓ | /opt/stacks/media-mgmt | prowlarr.${DOMAIN} |
|
| ├─ Lazy Librarian | Book automation | ✓ | /opt/stacks/media-ext, /mnt/media | lazylibrarian.${DOMAIN} |
|
||||||
| ├─ Readarr | Ebooks/Audiobooks | ✓ | /opt/stacks/media-mgmt, /mnt/media | readarr.${DOMAIN} |
|
| ├─ Mylar3 | Comic manager | ✓ | /opt/stacks/media-ext, /mnt/media | mylar.${DOMAIN} |
|
||||||
| ├─ Lidarr | Music manager | ✓ | /opt/stacks/media-mgmt, /mnt/media | lidarr.${DOMAIN} |
|
| ├─ Calibre-Web | Ebook reader | ✓ | /opt/stacks/media-ext, /mnt/media | calibre.${DOMAIN} |
|
||||||
| ├─ Lazy Librarian | Book automation | ✓ | /opt/stacks/media-mgmt, /mnt/media | lazylibrarian.${DOMAIN} |
|
| ├─ Jellyseerr | Media requests | ✓ | /opt/stacks/media-ext | jellyseerr.${DOMAIN} |
|
||||||
| ├─ Mylar3 | Comic manager | ✓ | /opt/stacks/media-mgmt, /mnt/media | mylar.${DOMAIN} |
|
| ├─ FlareSolverr | Cloudflare bypass | - | /opt/stacks/media-ext | No UI |
|
||||||
| ├─ Jellyseerr | Media requests | ✓ | /opt/stacks/media-mgmt | jellyseerr.${DOMAIN} |
|
| ├─ Tdarr Server | Transcoding server | ✓ | /opt/stacks/media-ext, /mnt/transcode | tdarr.${DOMAIN} |
|
||||||
| ├─ FlareSolverr | Cloudflare bypass | - | /opt/stacks/media-mgmt | No UI |
|
|
||||||
| ├─ Tdarr Server | Transcoding server | ✓ | /opt/stacks/media-mgmt, /mnt/transcode | tdarr.${DOMAIN} |
|
|
||||||
| ├─ Tdarr Node | Transcoding worker | - | /mnt/transcode-cache | No UI |
|
| ├─ Tdarr Node | Transcoding worker | - | /mnt/transcode-cache | No UI |
|
||||||
| ├─ Unmanic | Library optimizer | ✓ | /opt/stacks/media-mgmt, /mnt/transcode | unmanic.${DOMAIN} |
|
| └─ Unmanic | Library optimizer | ✓ | /opt/stacks/media-ext, /mnt/transcode | unmanic.${DOMAIN} |
|
||||||
| └─ Bazarr | Subtitle automation | ✓ | /opt/stacks/media-mgmt, /mnt/media | bazarr.${DOMAIN} |
|
| **🏠 homeassistant.yaml** (7) | | | | |
|
||||||
| **🏠 [homeassistant.yaml](../docker-compose/homeassistant.yml)** (7) | | | | |
|
|
||||||
| ├─ Home Assistant | HA platform | ✗ | /opt/stacks/homeassistant | ha.${DOMAIN} |
|
| ├─ Home Assistant | HA platform | ✗ | /opt/stacks/homeassistant | ha.${DOMAIN} |
|
||||||
| ├─ ESPHome | ESP firmware mgr | ✓ | /opt/stacks/homeassistant | esphome.${DOMAIN} |
|
| ├─ ESPHome | ESP firmware mgr | ✓ | /opt/stacks/homeassistant | esphome.${DOMAIN} |
|
||||||
| ├─ TasmoAdmin | Tasmota device mgr | ✓ | /opt/stacks/homeassistant | tasmoadmin.${DOMAIN} |
|
| ├─ TasmoAdmin | Tasmota device mgr | ✓ | /opt/stacks/homeassistant | tasmoadmin.${DOMAIN} |
|
||||||
@@ -55,7 +52,7 @@ This document provides a comprehensive overview of all 60+ pre-configured servic
|
|||||||
| ├─ Mosquitto | MQTT broker | - | /opt/stacks/homeassistant | Ports 1883, 9001 |
|
| ├─ Mosquitto | MQTT broker | - | /opt/stacks/homeassistant | Ports 1883, 9001 |
|
||||||
| ├─ Zigbee2MQTT | Zigbee bridge | ✓ | /opt/stacks/homeassistant | zigbee2mqtt.${DOMAIN} |
|
| ├─ Zigbee2MQTT | Zigbee bridge | ✓ | /opt/stacks/homeassistant | zigbee2mqtt.${DOMAIN} |
|
||||||
| └─ MotionEye | Video surveillance | ✓ | /opt/stacks/homeassistant, /mnt/surveillance | motioneye.${DOMAIN} |
|
| └─ MotionEye | Video surveillance | ✓ | /opt/stacks/homeassistant, /mnt/surveillance | motioneye.${DOMAIN} |
|
||||||
| **💼 [productivity.yaml](../docker-compose/productivity.yml)** (8 + 6 DBs) | | | | |
|
| **💼 productivity.yaml** (8 + 6 DBs) | | | | |
|
||||||
| ├─ Nextcloud | File sync platform | ✓ | /opt/stacks/productivity, /mnt/nextcloud | nextcloud.${DOMAIN} |
|
| ├─ Nextcloud | File sync platform | ✓ | /opt/stacks/productivity, /mnt/nextcloud | nextcloud.${DOMAIN} |
|
||||||
| │ └─ nextcloud-db | MariaDB | - | /opt/stacks/productivity | No UI |
|
| │ └─ nextcloud-db | MariaDB | - | /opt/stacks/productivity | No UI |
|
||||||
| ├─ Mealie | Recipe manager | ✗ | /opt/stacks/productivity | mealie.${DOMAIN} |
|
| ├─ Mealie | Recipe manager | ✗ | /opt/stacks/productivity | mealie.${DOMAIN} |
|
||||||
@@ -70,14 +67,15 @@ This document provides a comprehensive overview of all 60+ pre-configured servic
|
|||||||
| │ └─ mediawiki-db | MariaDB | - | /opt/stacks/productivity | No UI |
|
| │ └─ mediawiki-db | MariaDB | - | /opt/stacks/productivity | No UI |
|
||||||
| └─ Form.io | Form builder | ✓ | /opt/stacks/productivity | forms.${DOMAIN} |
|
| └─ Form.io | Form builder | ✓ | /opt/stacks/productivity | forms.${DOMAIN} |
|
||||||
| └─ formio-mongo | MongoDB | - | /opt/stacks/productivity | No UI |
|
| └─ formio-mongo | MongoDB | - | /opt/stacks/productivity | No UI |
|
||||||
| **🛠️ [utilities.yaml](../docker-compose/utilities.yml)** (6) | | | | |
|
| **🛠️ utilities.yaml** (7) | | | | |
|
||||||
| ├─ Vaultwarden | Password manager | ✗ | /opt/stacks/utilities | bitwarden.${DOMAIN} |
|
| ├─ Vaultwarden | Password manager | ✗ | /opt/stacks/utilities | bitwarden.${DOMAIN} |
|
||||||
| ├─ Backrest | Backup (restic) | ✓ | /opt/stacks/utilities, /mnt/backups | backrest.${DOMAIN} |
|
| ├─ Backrest | Backup (restic) | ✓ | /opt/stacks/utilities, /mnt/backups | backrest.${DOMAIN} |
|
||||||
| ├─ Duplicati | Encrypted backups | ✓ | /opt/stacks/utilities, /mnt/backups | duplicati.${DOMAIN} |
|
| ├─ Duplicati | Encrypted backups | ✓ | /opt/stacks/utilities, /mnt/backups | duplicati.${DOMAIN} |
|
||||||
|
| ├─ Code Server | VS Code in browser | ✓ | /opt/stacks/utilities | code.${DOMAIN} |
|
||||||
| ├─ Form.io | Form platform | ✓ | /opt/stacks/utilities | forms.${DOMAIN} |
|
| ├─ Form.io | Form platform | ✓ | /opt/stacks/utilities | forms.${DOMAIN} |
|
||||||
| │ └─ formio-mongo | MongoDB | - | /opt/stacks/utilities | No UI |
|
| │ └─ formio-mongo | MongoDB | - | /opt/stacks/utilities | No UI |
|
||||||
| └─ Authelia-Redis | Session storage | - | /opt/stacks/utilities | No UI |
|
| └─ Authelia-Redis | Session storage | - | /opt/stacks/utilities | No UI |
|
||||||
| **📈 [monitoring.yaml](../docker-compose/monitoring.yml)** (8) | | | | |
|
| **📈 monitoring.yaml** (8) | | | | |
|
||||||
| ├─ Prometheus | Metrics collection | ✓ | /opt/stacks/monitoring | prometheus.${DOMAIN} |
|
| ├─ Prometheus | Metrics collection | ✓ | /opt/stacks/monitoring | prometheus.${DOMAIN} |
|
||||||
| ├─ Grafana | Visualization | ✓ | /opt/stacks/monitoring | grafana.${DOMAIN} |
|
| ├─ Grafana | Visualization | ✓ | /opt/stacks/monitoring | grafana.${DOMAIN} |
|
||||||
| ├─ Loki | Log aggregation | - | /opt/stacks/monitoring | Via Grafana |
|
| ├─ Loki | Log aggregation | - | /opt/stacks/monitoring | Via Grafana |
|
||||||
@@ -85,7 +83,7 @@ This document provides a comprehensive overview of all 60+ pre-configured servic
|
|||||||
| ├─ Node Exporter | Host metrics | - | /opt/stacks/monitoring | No UI |
|
| ├─ Node Exporter | Host metrics | - | /opt/stacks/monitoring | No UI |
|
||||||
| ├─ cAdvisor | Container metrics | - | /opt/stacks/monitoring | Internal :8080 |
|
| ├─ cAdvisor | Container metrics | - | /opt/stacks/monitoring | Internal :8080 |
|
||||||
| └─ Uptime Kuma | Uptime monitoring | ✓ | /opt/stacks/monitoring | status.${DOMAIN} |
|
| └─ Uptime Kuma | Uptime monitoring | ✓ | /opt/stacks/monitoring | status.${DOMAIN} |
|
||||||
| **👨💻 [development.yaml](../docker-compose/development.yml)** (6) | | | | |
|
| **👨💻 development.yaml** (6) | | | | |
|
||||||
| ├─ GitLab CE | Git + CI/CD | ✓ | /opt/stacks/development, /mnt/git | gitlab.${DOMAIN} |
|
| ├─ GitLab CE | Git + CI/CD | ✓ | /opt/stacks/development, /mnt/git | gitlab.${DOMAIN} |
|
||||||
| ├─ PostgreSQL | SQL database | - | /opt/stacks/development | Port 5432 |
|
| ├─ PostgreSQL | SQL database | - | /opt/stacks/development | Port 5432 |
|
||||||
| ├─ Redis | In-memory store | - | /opt/stacks/development | Port 6379 |
|
| ├─ Redis | In-memory store | - | /opt/stacks/development | Port 6379 |
|
||||||
@@ -95,6 +93,42 @@ This document provides a comprehensive overview of all 60+ pre-configured servic
|
|||||||
|
|
||||||
**Legend:** ✓ = Protected by SSO | ✗ = Bypasses SSO | - = No web UI
|
**Legend:** ✓ = Protected by SSO | ✗ = Bypasses SSO | - = No web UI
|
||||||
|
|
||||||
|
## Quick Deployment Order
|
||||||
|
|
||||||
|
1. **Create Networks** (one-time setup)
|
||||||
|
```bash
|
||||||
|
docker network create traefik-network
|
||||||
|
docker network create homelab-network
|
||||||
|
docker network create dockerproxy-network
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Deploy Core Stack** (required first)
|
||||||
|
```bash
|
||||||
|
cd /opt/stacks/core/
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Deploy Infrastructure**
|
||||||
|
```bash
|
||||||
|
cd /opt/stacks/infrastructure/
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Deploy Dashboards**
|
||||||
|
```bash
|
||||||
|
cd /opt/stacks/dashboards/
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Deploy Additional Stacks** (as needed)
|
||||||
|
- Media: `/opt/stacks/media/`
|
||||||
|
- Extended Media: `/opt/stacks/media-extended/`
|
||||||
|
- Home Automation: `/opt/stacks/homeassistant/`
|
||||||
|
- Productivity: `/opt/stacks/productivity/`
|
||||||
|
- Utilities: `/opt/stacks/utilities/`
|
||||||
|
- Monitoring: `/opt/stacks/monitoring/`
|
||||||
|
- Development: `/opt/stacks/development/`
|
||||||
|
|
||||||
## Toggling SSO (Authelia) On/Off
|
## Toggling SSO (Authelia) On/Off
|
||||||
|
|
||||||
You can easily enable or disable SSO protection for any service by modifying its Traefik labels in the docker-compose.yml file.
|
You can easily enable or disable SSO protection for any service by modifying its Traefik labels in the docker-compose.yml file.
|
||||||
@@ -156,6 +190,137 @@ docker compose -f /opt/stacks/stack-name/docker-compose.yml down
|
|||||||
- **Gradual Exposure**: Comment out SSO only when ready to expose a service
|
- **Gradual Exposure**: Comment out SSO only when ready to expose a service
|
||||||
- **Quick Toggle**: AI assistant can modify these labels automatically when you ask
|
- **Quick Toggle**: AI assistant can modify these labels automatically when you ask
|
||||||
|
|
||||||
|
## Authelia Customization
|
||||||
|
|
||||||
|
### Available Customization Options
|
||||||
|
|
||||||
|
**1. Branding and Appearance**
|
||||||
|
Edit `/opt/stacks/core/authelia/configuration.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Custom logo and branding
|
||||||
|
theme: dark # Options: light, dark, grey, auto
|
||||||
|
|
||||||
|
# No built-in web UI for configuration
|
||||||
|
# All settings managed via YAML files
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. User Management**
|
||||||
|
Users are managed in `/opt/stacks/core/authelia/users_database.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
users:
|
||||||
|
username:
|
||||||
|
displayname: "Display Name"
|
||||||
|
password: "$argon2id$v=19$m=65536..." # Generated with authelia hash-password
|
||||||
|
email: user@example.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
|
- users
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate password hash:
|
||||||
|
```bash
|
||||||
|
docker run --rm authelia/authelia:4.37 authelia hash-password 'yourpassword'
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Access Control Rules**
|
||||||
|
Customize who can access what in `configuration.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
access_control:
|
||||||
|
default_policy: deny
|
||||||
|
|
||||||
|
rules:
|
||||||
|
# Public services (no auth)
|
||||||
|
- domain:
|
||||||
|
- "jellyfin.yourdomain.com"
|
||||||
|
- "plex.yourdomain.com"
|
||||||
|
policy: bypass
|
||||||
|
|
||||||
|
# Admin only services
|
||||||
|
- domain:
|
||||||
|
- "dockge.yourdomain.com"
|
||||||
|
- "portainer.yourdomain.com"
|
||||||
|
policy: two_factor
|
||||||
|
subject:
|
||||||
|
- "group:admins"
|
||||||
|
|
||||||
|
# All authenticated users
|
||||||
|
- domain: "*.yourdomain.com"
|
||||||
|
policy: one_factor
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Two-Factor Authentication (2FA)**
|
||||||
|
- TOTP (Time-based One-Time Password) via apps like Google Authenticator, Authy
|
||||||
|
- Configure in `configuration.yml` under `totp:` section
|
||||||
|
- Per-user enrollment via Authelia UI at `https://auth.${DOMAIN}`
|
||||||
|
|
||||||
|
**5. Session Management**
|
||||||
|
Edit `configuration.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
session:
|
||||||
|
name: authelia_session
|
||||||
|
expiration: 1h # How long before re-login required
|
||||||
|
inactivity: 5m # Timeout after inactivity
|
||||||
|
remember_me_duration: 1M # "Remember me" checkbox duration
|
||||||
|
```
|
||||||
|
|
||||||
|
**6. Notification Settings**
|
||||||
|
Email notifications for password resets, 2FA enrollment:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
notifier:
|
||||||
|
smtp:
|
||||||
|
host: smtp.gmail.com
|
||||||
|
port: 587
|
||||||
|
username: your-email@gmail.com
|
||||||
|
password: app-password
|
||||||
|
sender: authelia@yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### No Web UI for Configuration
|
||||||
|
|
||||||
|
⚠️ **Important**: Authelia does **not** have a configuration web UI. All configuration is done via YAML files:
|
||||||
|
- `/opt/stacks/core/authelia/configuration.yml` - Main settings
|
||||||
|
- `/opt/stacks/core/authelia/users_database.yml` - User accounts
|
||||||
|
|
||||||
|
This is **by design** and makes Authelia perfect for AI management and security-first approach:
|
||||||
|
- AI can read and modify YAML files
|
||||||
|
- Version control friendly
|
||||||
|
- No UI clicks required
|
||||||
|
- Infrastructure as code
|
||||||
|
- Secure by default
|
||||||
|
|
||||||
|
**Web UI Available For:**
|
||||||
|
- Login page: `https://auth.${DOMAIN}`
|
||||||
|
- User profile: Change password, enroll 2FA
|
||||||
|
- Device enrollment: Manage trusted devices
|
||||||
|
|
||||||
|
**Alternative with Web UI: Authentik**
|
||||||
|
If you need a web UI for user management, Authentik is included in the infrastructure stack:
|
||||||
|
- **Authentik**: Full-featured SSO with web UI for user/group management
|
||||||
|
- Access at: `https://authentik.${DOMAIN}`
|
||||||
|
- Includes PostgreSQL database and Redis cache
|
||||||
|
- More complex but offers GUI-based configuration
|
||||||
|
- Deploy only if you need web-based user management
|
||||||
|
|
||||||
|
**Other Alternatives:**
|
||||||
|
- **Keycloak**: Enterprise-grade SSO with web UI
|
||||||
|
- **Authelia + LDAP**: Use LDAP with web management (phpLDAPadmin, etc.)
|
||||||
|
|
||||||
|
### Quick Configuration with AI
|
||||||
|
|
||||||
|
Since all Authelia configuration is file-based, you can use the AI assistant to:
|
||||||
|
- Add/remove users
|
||||||
|
- Modify access rules
|
||||||
|
- Change session settings
|
||||||
|
- Update branding
|
||||||
|
- Enable/disable features
|
||||||
|
|
||||||
|
Just ask: "Add a new user to Authelia" or "Change session timeout to 2 hours"
|
||||||
|
|
||||||
## Storage Recommendations
|
## Storage Recommendations
|
||||||
|
|
||||||
| Data Type | Recommended Location | Reason |
|
| Data Type | Recommended Location | Reason |
|
||||||
|
|||||||
@@ -30,511 +30,10 @@ log_error() {
|
|||||||
echo -e "${RED}[ERROR]${NC} $1"
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
#==========================================
|
|
||||||
# VALIDATION FUNCTIONS
|
|
||||||
#==========================================
|
|
||||||
|
|
||||||
validate_prerequisites() {
|
|
||||||
# Check if .env file exists
|
|
||||||
if [ ! -f "$REPO_DIR/.env" ]; then
|
|
||||||
log_error ".env file not found!"
|
|
||||||
log_info "Please create and configure your .env file first:"
|
|
||||||
echo " cd $REPO_DIR"
|
|
||||||
echo " cp .env.example .env"
|
|
||||||
echo " nano .env"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if Docker is installed and running
|
|
||||||
log_info "Validating Docker installation..."
|
|
||||||
|
|
||||||
if ! command -v docker &> /dev/null; then
|
|
||||||
log_error "Docker is not installed"
|
|
||||||
log_info "Please run the setup script first:"
|
|
||||||
log_info " cd $REPO_DIR"
|
|
||||||
log_info " sudo ./scripts/setup-homelab.sh"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! docker info &> /dev/null 2>&1; then
|
|
||||||
log_error "Docker daemon is not running or not accessible"
|
|
||||||
echo ""
|
|
||||||
log_info "Troubleshooting steps:"
|
|
||||||
log_info " 1. Start Docker: sudo systemctl start docker"
|
|
||||||
log_info " 2. Enable Docker on boot: sudo systemctl enable docker"
|
|
||||||
log_info " 3. Check Docker status: sudo systemctl status docker"
|
|
||||||
log_info " 4. If recently added to docker group, log out and back in"
|
|
||||||
log_info " 5. Test access: docker ps"
|
|
||||||
echo ""
|
|
||||||
log_info "Current user: $ACTUAL_USER"
|
|
||||||
log_info "Docker group membership: $(groups $ACTUAL_USER | grep -o docker || echo 'NOT IN DOCKER GROUP')"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_success "Docker is available and running"
|
|
||||||
log_info "Docker version: $(docker --version | cut -d' ' -f3 | tr -d ',')"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Load environment variables for domain check
|
|
||||||
source "$REPO_DIR/.env"
|
|
||||||
|
|
||||||
if [ -z "$DOMAIN" ]; then
|
|
||||||
log_error "DOMAIN is not set in .env file"
|
|
||||||
log_info "Please edit .env and set your DuckDNS domain"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Using domain: $DOMAIN"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
#==========================================
|
|
||||||
# DEPLOYMENT STEP FUNCTIONS
|
|
||||||
#==========================================
|
|
||||||
|
|
||||||
step_1_create_directories() {
|
|
||||||
log_info "Step 1/7: Creating required directories..."
|
|
||||||
mkdir -p /opt/stacks/core
|
|
||||||
mkdir -p /opt/stacks/infrastructure
|
|
||||||
mkdir -p /opt/dockge/data
|
|
||||||
log_success "Directories created"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
step_2_create_networks() {
|
|
||||||
log_info "Step 2/7: Creating Docker networks..."
|
|
||||||
docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists"
|
|
||||||
docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists"
|
|
||||||
docker network create dockerproxy-network 2>/dev/null && log_success "Created dockerproxy-network" || log_info "dockerproxy-network already exists"
|
|
||||||
docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
configure_authelia() {
|
|
||||||
# Configure Authelia admin user from setup script
|
|
||||||
if [ -f /opt/stacks/.setup-temp/authelia_admin_credentials.tmp ] && [ -f /opt/stacks/.setup-temp/authelia_password_hash.tmp ]; then
|
|
||||||
log_info "Loading Authelia admin credentials from setup temp files..."
|
|
||||||
source /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
elif [ -n "${AUTHELIA_ADMIN_USER}" ] && [ -n "${AUTHELIA_ADMIN_EMAIL}" ] && [ -n "${AUTHELIA_ADMIN_PASSWORD}" ]; then
|
|
||||||
log_info "Loading Authelia admin credentials from .env file..."
|
|
||||||
ADMIN_USER="${AUTHELIA_ADMIN_USER}"
|
|
||||||
ADMIN_EMAIL="${AUTHELIA_ADMIN_EMAIL}"
|
|
||||||
ADMIN_PASSWORD="${AUTHELIA_ADMIN_PASSWORD}"
|
|
||||||
|
|
||||||
# Generate password hash from the password in .env
|
|
||||||
log_info "Generating password hash from .env credentials..."
|
|
||||||
docker run --rm authelia/authelia:4.37 authelia crypto hash generate argon2 --password "$ADMIN_PASSWORD" > /tmp/authelia_password_hash_from_env.tmp 2>/dev/null
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
# Create temp directory and files for the rest of the script
|
|
||||||
mkdir -p /opt/stacks/.setup-temp
|
|
||||||
echo "ADMIN_USER=$ADMIN_USER" > /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
echo "ADMIN_EMAIL=$ADMIN_EMAIL" >> /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
echo "ADMIN_PASSWORD=$ADMIN_PASSWORD" >> /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
chmod 600 /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
|
|
||||||
# Extract just the hash (remove "Digest: " prefix if present)
|
|
||||||
sed 's/^Digest: //' /tmp/authelia_password_hash_from_env.tmp | grep '^\$argon2' > /opt/stacks/.setup-temp/authelia_password_hash.tmp
|
|
||||||
chmod 600 /opt/stacks/.setup-temp/authelia_password_hash.tmp
|
|
||||||
rm -f /tmp/authelia_password_hash_from_env.tmp
|
|
||||||
|
|
||||||
log_success "Credentials loaded from .env file"
|
|
||||||
else
|
|
||||||
log_error "Failed to generate password hash from .env credentials"
|
|
||||||
ADMIN_USER=""
|
|
||||||
ADMIN_EMAIL=""
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f /opt/stacks/.setup-temp/authelia_admin_credentials.tmp ] && [ -f /opt/stacks/.setup-temp/authelia_password_hash.tmp ]; then
|
|
||||||
source /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
|
|
||||||
if [ -n "$ADMIN_USER" ] && [ -n "$ADMIN_EMAIL" ]; then
|
|
||||||
log_success "Using credentials: $ADMIN_USER ($ADMIN_EMAIL)"
|
|
||||||
|
|
||||||
# Create users_database.yml with credentials from setup
|
|
||||||
# Use single quotes in heredoc to prevent variable expansion issues with $ in hash
|
|
||||||
cat > /opt/stacks/core/authelia/users_database.yml << 'EOF'
|
|
||||||
###############################################################
|
|
||||||
# Users Database #
|
|
||||||
###############################################################
|
|
||||||
|
|
||||||
users:
|
|
||||||
ADMIN_USER_PLACEHOLDER:
|
|
||||||
displayname: "Admin User"
|
|
||||||
password: "PASSWORD_HASH_PLACEHOLDER"
|
|
||||||
email: ADMIN_EMAIL_PLACEHOLDER
|
|
||||||
groups:
|
|
||||||
- admins
|
|
||||||
- users
|
|
||||||
EOF
|
|
||||||
# Now safely replace placeholders
|
|
||||||
# Read hash from file (not bash variable) to avoid shell expansion
|
|
||||||
# The hash file was written directly from Docker output in setup script
|
|
||||||
export ADMIN_USER
|
|
||||||
export ADMIN_EMAIL
|
|
||||||
python3 << 'PYTHON_EOF'
|
|
||||||
# Read password hash from file to completely avoid bash variable expansion
|
|
||||||
with open('/opt/stacks/.setup-temp/authelia_password_hash.tmp', 'r') as f:
|
|
||||||
password_hash = f.read().strip()
|
|
||||||
|
|
||||||
import os
|
|
||||||
admin_user = os.environ['ADMIN_USER']
|
|
||||||
admin_email = os.environ['ADMIN_EMAIL']
|
|
||||||
|
|
||||||
content = f"""###############################################################
|
|
||||||
# Users Database #
|
|
||||||
###############################################################
|
|
||||||
|
|
||||||
users:
|
|
||||||
{admin_user}:
|
|
||||||
displayname: "Admin User"
|
|
||||||
password: "{password_hash}"
|
|
||||||
email: {admin_email}
|
|
||||||
groups:
|
|
||||||
- admins
|
|
||||||
- users
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open('/opt/stacks/core/authelia/users_database.yml', 'w') as f:
|
|
||||||
f.write(content)
|
|
||||||
PYTHON_EOF
|
|
||||||
|
|
||||||
log_success "Authelia admin user configured from setup script"
|
|
||||||
echo ""
|
|
||||||
echo "==========================================="
|
|
||||||
log_info "Authelia Login Credentials:"
|
|
||||||
echo " Username: $ADMIN_USER"
|
|
||||||
echo " Password: $ADMIN_PASSWORD"
|
|
||||||
echo " Email: $ADMIN_EMAIL"
|
|
||||||
echo "==========================================="
|
|
||||||
echo ""
|
|
||||||
log_warning "SAVE THESE CREDENTIALS!"
|
|
||||||
|
|
||||||
# Save full credentials for later reference
|
|
||||||
{
|
|
||||||
echo "Username: $ADMIN_USER"
|
|
||||||
echo "Password: $ADMIN_PASSWORD"
|
|
||||||
echo "Email: $ADMIN_EMAIL"
|
|
||||||
} > /opt/stacks/core/authelia/ADMIN_CREDENTIALS.txt
|
|
||||||
chmod 600 /opt/stacks/core/authelia/ADMIN_CREDENTIALS.txt
|
|
||||||
chown $ACTUAL_USER:$ACTUAL_USER /opt/stacks/core/authelia/ADMIN_CREDENTIALS.txt
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Clean up credentials files from setup script
|
|
||||||
rm -f /opt/stacks/.setup-temp/authelia_admin_credentials.tmp
|
|
||||||
rm -f /opt/stacks/.setup-temp/authelia_password_hash.tmp
|
|
||||||
rm -f /opt/stacks/core/authelia/ADMIN_PASSWORD.txt
|
|
||||||
rm -f /opt/stacks/core/authelia/ADMIN_CREDENTIALS.txt
|
|
||||||
rmdir /opt/stacks/.setup-temp 2>/dev/null || true
|
|
||||||
log_info "Cleaned up temporary setup files"
|
|
||||||
else
|
|
||||||
log_warning "Incomplete credentials from setup script"
|
|
||||||
log_info "Using template users_database.yml - please configure manually"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log_warning "No credentials file found from setup script"
|
|
||||||
log_info "Using template users_database.yml from config-templates"
|
|
||||||
log_info "Please run setup-homelab.sh first or configure manually"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
step_3_deploy_core() {
|
|
||||||
log_info "Step 3/7: Deploying core infrastructure stack..."
|
|
||||||
log_info " - DuckDNS (Dynamic DNS)"
|
|
||||||
log_info " - Traefik (Reverse Proxy with SSL)"
|
|
||||||
log_info " - Authelia (Single Sign-On)"
|
|
||||||
log_info " - Gluetun (VPN Client)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Copy core stack files
|
|
||||||
log_info "Preparing core stack configuration files..."
|
|
||||||
|
|
||||||
# Safety: Stop existing core stack if running (prevents file conflicts)
|
|
||||||
if [ -f "/opt/stacks/core/docker-compose.yml" ]; then
|
|
||||||
log_info "Stopping existing core stack for safe reconfiguration..."
|
|
||||||
cd /opt/stacks/core && docker compose down 2>/dev/null || true
|
|
||||||
sleep 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean up any incorrect directory structure from previous runs
|
|
||||||
if [ -d "/opt/stacks/core/traefik/acme.json" ]; then
|
|
||||||
log_warning "Removing incorrectly created acme.json directory"
|
|
||||||
rm -rf /opt/stacks/core/traefik/acme.json
|
|
||||||
fi
|
|
||||||
if [ -d "/opt/stacks/core/traefik/traefik.yml" ]; then
|
|
||||||
log_warning "Removing incorrectly created traefik.yml directory"
|
|
||||||
rm -rf /opt/stacks/core/traefik/traefik.yml
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy compose file
|
|
||||||
cp "$REPO_DIR/docker-compose/core.yml" /opt/stacks/core/docker-compose.yml
|
|
||||||
|
|
||||||
# Safely remove and replace config directories
|
|
||||||
if [ -d "/opt/stacks/core/traefik" ]; then
|
|
||||||
rm -rf /opt/stacks/core/traefik
|
|
||||||
fi
|
|
||||||
if [ -d "/opt/stacks/core/authelia" ]; then
|
|
||||||
rm -rf /opt/stacks/core/authelia
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/
|
|
||||||
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
|
||||||
|
|
||||||
# Create acme.json as a file (not directory) with correct permissions
|
|
||||||
log_info "Creating acme.json for SSL certificates..."
|
|
||||||
touch /opt/stacks/core/traefik/acme.json
|
|
||||||
chmod 600 /opt/stacks/core/traefik/acme.json
|
|
||||||
log_success "acme.json created with correct permissions"
|
|
||||||
|
|
||||||
# Replace email placeholder in traefik.yml
|
|
||||||
log_info "Configuring Traefik with email: $ACME_EMAIL..."
|
|
||||||
sed -i "s/ACME_EMAIL_PLACEHOLDER/${ACME_EMAIL}/g" /opt/stacks/core/traefik/traefik.yml
|
|
||||||
log_success "Traefik email configured"
|
|
||||||
|
|
||||||
# Replace domain placeholder in authelia configuration
|
|
||||||
log_info "Configuring Authelia for domain: $DOMAIN..."
|
|
||||||
sed -i "s/your-domain.duckdns.org/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml
|
|
||||||
|
|
||||||
# Configure Authelia admin user
|
|
||||||
configure_authelia
|
|
||||||
|
|
||||||
# Clean up old Authelia database if encryption key changed
|
|
||||||
# This prevents "encryption key does not appear to be valid" errors
|
|
||||||
if [ -d "/var/lib/docker/volumes/core_authelia-data/_data" ]; then
|
|
||||||
log_info "Checking for existing Authelia database..."
|
|
||||||
# Check if database exists and might have encryption key mismatch
|
|
||||||
if [ -f "/var/lib/docker/volumes/core_authelia-data/_data/db.sqlite3" ]; then
|
|
||||||
log_warning "Existing Authelia database found from previous deployment"
|
|
||||||
log_info "If deployment fails with encryption key errors, run: sudo ./scripts/reset-test-environment.sh"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Deploy core stack
|
|
||||||
cd /opt/stacks/core
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
log_success "Core infrastructure deployed"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Wait for Traefik to be ready
|
|
||||||
log_info "Waiting for Traefik to initialize..."
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# Check if Traefik is healthy
|
|
||||||
if docker ps | grep -q "traefik.*Up"; then
|
|
||||||
log_success "Traefik is running"
|
|
||||||
else
|
|
||||||
log_warning "Traefik container check inconclusive, continuing..."
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
step_4_deploy_infrastructure() {
|
|
||||||
log_info "Step 4/7: Deploying infrastructure stack..."
|
|
||||||
log_info " - Dockge (Docker Compose Manager)"
|
|
||||||
log_info " - Pi-hole (DNS Ad Blocker)"
|
|
||||||
log_info " - Dozzle (Log Viewer)"
|
|
||||||
log_info " - Glances (System Monitor)"
|
|
||||||
log_info " - Docker Proxy (Security)"
|
|
||||||
log_info " - Watchtower (Automatic Updates)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Copy infrastructure stack
|
|
||||||
cp "$REPO_DIR/docker-compose/infrastructure.yml" /opt/stacks/infrastructure/docker-compose.yml
|
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env
|
|
||||||
|
|
||||||
# Deploy infrastructure stack
|
|
||||||
cd /opt/stacks/infrastructure
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
log_success "Infrastructure stack deployed"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
step_5_deploy_dashboards() {
|
|
||||||
log_info "Step 5/7: Deploying dashboards stack..."
|
|
||||||
log_info " - Homepage (AI-configurable Dashboard)"
|
|
||||||
log_info " - Homarr (Modern Dashboard)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Copy dashboards stack
|
|
||||||
mkdir -p /opt/stacks/dashboards
|
|
||||||
cp "$REPO_DIR/docker-compose/dashboards.yml" /opt/stacks/dashboards/docker-compose.yml
|
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/dashboards/.env
|
|
||||||
|
|
||||||
# Copy and configure homepage templates
|
|
||||||
if [ -d "$REPO_DIR/config-templates/homepage" ]; then
|
|
||||||
cp -r "$REPO_DIR/config-templates/homepage" /opt/stacks/dashboards/
|
|
||||||
|
|
||||||
# Replace HOMEPAGE_VAR_DOMAIN with actual domain in all homepage config files
|
|
||||||
# Homepage doesn't support environment variables in configs
|
|
||||||
find /opt/stacks/dashboards/homepage -type f \( -name "*.yaml" -o -name "*.yml" \) -exec sed -i "s/{{HOMEPAGE_VAR_DOMAIN}}/${DOMAIN}/g" {} \;
|
|
||||||
|
|
||||||
log_info "Homepage configuration templates copied and configured"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Deploy dashboards stack
|
|
||||||
cd /opt/stacks/dashboards
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
log_success "Dashboards stack deployed"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
step_6_prepare_additional_stacks() {
|
|
||||||
log_info "Step 6/7: Preparing additional stacks for Dockge..."
|
|
||||||
echo ""
|
|
||||||
log_info "The following stacks can be deployed through Dockge's web UI:"
|
|
||||||
log_info " - media.yml (Jellyfin, Calibre-web, qBittorrent)"
|
|
||||||
log_info " - media-management.yml (Sonarr, Radarr, *arr apps)"
|
|
||||||
log_info " - homeassistant.yml (Home Assistant and accessories)"
|
|
||||||
log_info " - productivity.yml (Nextcloud, Gitea, wikis)"
|
|
||||||
log_info " - monitoring.yml (Grafana, Prometheus, etc.)"
|
|
||||||
log_info " - utilities.yml (Backups, code editors, etc.)"
|
|
||||||
log_info " - alternatives.yml (Portainer, Authentik)"
|
|
||||||
log_info " - development.yml (VS Code Server, GitLab, Jupyter)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Ask user if they want to pre-pull images for additional stacks
|
|
||||||
read -p "Pre-pull Docker images for additional stacks? This will take time but speeds up first deployment (y/N): " PULL_IMAGES
|
|
||||||
PULL_IMAGES=${PULL_IMAGES:-n}
|
|
||||||
|
|
||||||
# Copy additional stacks to Dockge directory
|
|
||||||
ADDITIONAL_STACKS=("media" "media-management" "homeassistant" "productivity" "monitoring" "utilities" "alternatives" "development")
|
|
||||||
|
|
||||||
for stack in "${ADDITIONAL_STACKS[@]}"; do
|
|
||||||
mkdir -p "/opt/stacks/$stack"
|
|
||||||
cp "$REPO_DIR/docker-compose/${stack}.yml" "/opt/stacks/$stack/docker-compose.yml"
|
|
||||||
cp "$REPO_DIR/.env" "/opt/stacks/$stack/.env"
|
|
||||||
|
|
||||||
# Pre-pull images if requested
|
|
||||||
if [[ "$PULL_IMAGES" =~ ^[Yy]$ ]]; then
|
|
||||||
log_info "Pulling images for $stack stack..."
|
|
||||||
cd "/opt/stacks/$stack"
|
|
||||||
docker compose pull 2>&1 | grep -E '(Pulling|Downloaded|Already exists|up to date)' || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log_success "Additional stacks prepared in Dockge"
|
|
||||||
log_info "These stacks are NOT started - deploy them via Dockge web UI as needed"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
step_7_wait_for_dockge() {
|
|
||||||
log_info "Step 7/7: Waiting for Dockge web UI to be ready..."
|
|
||||||
|
|
||||||
DOCKGE_URL="https://dockge.${DOMAIN}"
|
|
||||||
MAX_WAIT=60 # Maximum wait time in seconds
|
|
||||||
WAITED=0
|
|
||||||
|
|
||||||
# Function to check if Dockge is accessible
|
|
||||||
check_dockge() {
|
|
||||||
# Try to connect to Dockge (ignore SSL cert warnings for self-signed during startup)
|
|
||||||
curl -k -s -o /dev/null -w "%{http_code}" "$DOCKGE_URL" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Wait for Dockge to respond
|
|
||||||
while [ $WAITED -lt $MAX_WAIT ]; do
|
|
||||||
HTTP_CODE=$(check_dockge)
|
|
||||||
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "401" ]; then
|
|
||||||
log_success "Dockge web UI is ready!"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo -n "."
|
|
||||||
sleep 2
|
|
||||||
WAITED=$((WAITED + 2))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [ $WAITED -ge $MAX_WAIT ]; then
|
|
||||||
log_warning "Dockge did not respond within ${MAX_WAIT} seconds"
|
|
||||||
log_info "It may still be starting up. Check manually at: $DOCKGE_URL"
|
|
||||||
else
|
|
||||||
# Try to open browser
|
|
||||||
log_info "Opening Dockge in your browser..."
|
|
||||||
|
|
||||||
# Detect and use available browser
|
|
||||||
if command -v xdg-open &> /dev/null; then
|
|
||||||
xdg-open "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
elif command -v gnome-open &> /dev/null; then
|
|
||||||
gnome-open "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
elif command -v firefox &> /dev/null; then
|
|
||||||
firefox "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
elif command -v google-chrome &> /dev/null; then
|
|
||||||
google-chrome "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
else
|
|
||||||
log_warning "No browser detected. Please manually open: $DOCKGE_URL"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
show_final_summary() {
|
|
||||||
echo "=========================================="
|
|
||||||
log_success "Deployment completed successfully!"
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
log_info "Access your services:"
|
|
||||||
echo ""
|
|
||||||
echo " 🚀 Dockge: https://dockge.${DOMAIN}"
|
|
||||||
echo " 🔒 Authelia: https://auth.${DOMAIN}"
|
|
||||||
echo " 🔀 Traefik: https://traefik.${DOMAIN}"
|
|
||||||
echo ""
|
|
||||||
log_info "SSL Certificates:"
|
|
||||||
echo " 📝 Let's Encrypt certificates will be acquired automatically within 2-5 minutes"
|
|
||||||
echo " ⚠️ Initial access uses self-signed certs (browser warning is normal)"
|
|
||||||
echo " 🔓 Ensure ports 80/443 are accessible from internet for Let's Encrypt"
|
|
||||||
echo " 💾 Admin credentials saved to: /opt/stacks/core/authelia/ADMIN_CREDENTIALS.txt"
|
|
||||||
echo ""
|
|
||||||
log_info "Next steps:"
|
|
||||||
echo ""
|
|
||||||
echo " 1. Log in to Dockge using your Authelia credentials"
|
|
||||||
echo " (saved in /opt/stacks/core/authelia/ADMIN_CREDENTIALS.txt)"
|
|
||||||
echo ""
|
|
||||||
echo " 2. Deploy additional stacks through Dockge's web UI:"
|
|
||||||
echo " - media.yml (Jellyfin, Calibre-web, qBittorrent)"
|
|
||||||
echo " - media-management.yml (Sonarr, Radarr, *arr apps)"
|
|
||||||
echo " - homeassistant.yml (Home Assistant and accessories)"
|
|
||||||
echo " - productivity.yml (Nextcloud, Gitea, wikis)"
|
|
||||||
echo " - monitoring.yml (Grafana, Prometheus, etc.)"
|
|
||||||
echo " - utilities.yml (Backups, code editors, etc.)"
|
|
||||||
echo " - alternatives.yml (Portainer, Authentik - optional)"
|
|
||||||
echo ""
|
|
||||||
echo " 3. Access your dashboards:"
|
|
||||||
echo " 🏠 Homepage: https://home.${DOMAIN}"
|
|
||||||
echo " 📊 Homarr: https://homarr.${DOMAIN}"
|
|
||||||
echo ""
|
|
||||||
echo " 4. Configure services via the AI assistant in VS Code"
|
|
||||||
echo ""
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
log_info "For documentation, see: $REPO_DIR/docs/"
|
|
||||||
log_info "For troubleshooting, see: $REPO_DIR/docs/quick-reference.md"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
#==========================================
|
|
||||||
# MAIN EXECUTION
|
|
||||||
#==========================================
|
|
||||||
|
|
||||||
# Check if running as root
|
# Check if running as root
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -eq 0 ]; then
|
||||||
log_error "Please run as root (use: sudo ./deploy-homelab.sh)"
|
log_error "Please do NOT run this script as root or with sudo"
|
||||||
exit 1
|
log_info "Run as: ./deploy-homelab.sh"
|
||||||
fi
|
|
||||||
|
|
||||||
# Get the actual user who invoked sudo
|
|
||||||
ACTUAL_USER="${SUDO_USER:-$USER}"
|
|
||||||
if [ "$ACTUAL_USER" = "root" ]; then
|
|
||||||
log_error "Please run this script with sudo, not as root user"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -543,16 +42,221 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
REPO_DIR="$( cd "$SCRIPT_DIR/.." && pwd )"
|
REPO_DIR="$( cd "$SCRIPT_DIR/.." && pwd )"
|
||||||
|
|
||||||
log_info "AI-Homelab Deployment Script"
|
log_info "AI-Homelab Deployment Script"
|
||||||
log_info "Running as user: $ACTUAL_USER"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Execute deployment steps
|
# Check if .env file exists
|
||||||
validate_prerequisites
|
if [ ! -f "$REPO_DIR/.env" ]; then
|
||||||
step_1_create_directories
|
log_error ".env file not found!"
|
||||||
step_2_create_networks
|
log_info "Please create and configure your .env file first:"
|
||||||
step_3_deploy_core
|
echo " cd $REPO_DIR"
|
||||||
step_4_deploy_infrastructure
|
echo " cp .env.example .env"
|
||||||
step_5_deploy_dashboards
|
echo " nano .env"
|
||||||
step_6_prepare_additional_stacks
|
exit 1
|
||||||
step_7_wait_for_dockge
|
fi
|
||||||
show_final_summary
|
|
||||||
|
# Check if Docker is installed and running
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
log_error "Docker is not installed. Please run setup-homelab.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker info &> /dev/null; then
|
||||||
|
log_error "Docker daemon is not running or you don't have permission."
|
||||||
|
log_info "Try: sudo systemctl start docker"
|
||||||
|
log_info "Or log out and log back in for group changes to take effect"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Docker is available and running"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Load environment variables for domain check
|
||||||
|
source "$REPO_DIR/.env"
|
||||||
|
|
||||||
|
if [ -z "$DOMAIN" ]; then
|
||||||
|
log_error "DOMAIN is not set in .env file"
|
||||||
|
log_info "Please edit .env and set your DuckDNS domain"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Using domain: $DOMAIN"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 1: Create required directories
|
||||||
|
log_info "Step 1/5: Creating required directories..."
|
||||||
|
mkdir -p /opt/stacks/core
|
||||||
|
mkdir -p /opt/stacks/infrastructure
|
||||||
|
mkdir -p /opt/dockge/data
|
||||||
|
log_success "Directories created"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 2: Create Docker networks (if they don't exist)
|
||||||
|
log_info "Step 2/5: Creating Docker networks..."
|
||||||
|
docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists"
|
||||||
|
docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists"
|
||||||
|
docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 3: Deploy core infrastructure (DuckDNS, Traefik, Authelia, Gluetun)
|
||||||
|
log_info "Step 3/5: Deploying core infrastructure stack..."
|
||||||
|
log_info " - DuckDNS (Dynamic DNS)"
|
||||||
|
log_info " - Traefik (Reverse Proxy with SSL)"
|
||||||
|
log_info " - Authelia (Single Sign-On)"
|
||||||
|
log_info " - Gluetun (VPN Client)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy core stack files with overwrite checks
|
||||||
|
if [ -f "/opt/stacks/core/docker-compose.yml" ]; then
|
||||||
|
log_warning "docker-compose.yml already exists in /opt/stacks/core/"
|
||||||
|
log_info "Creating backup: docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
|
cp /opt/stacks/core/docker-compose.yml /opt/stacks/core/docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
cp "$REPO_DIR/docker-compose/core.yml" /opt/stacks/core/docker-compose.yml
|
||||||
|
|
||||||
|
if [ -d "/opt/stacks/core/traefik" ]; then
|
||||||
|
log_warning "Traefik configuration already exists in /opt/stacks/core/"
|
||||||
|
log_info "Creating backup: traefik.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
|
mv /opt/stacks/core/traefik /opt/stacks/core/traefik.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/
|
||||||
|
|
||||||
|
if [ -d "/opt/stacks/core/authelia" ]; then
|
||||||
|
log_warning "Authelia configuration already exists in /opt/stacks/core/"
|
||||||
|
log_info "Creating backup: authelia.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
|
mv /opt/stacks/core/authelia /opt/stacks/core/authelia.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
||||||
|
|
||||||
|
if [ -f "/opt/stacks/core/.env" ]; then
|
||||||
|
log_warning ".env already exists in /opt/stacks/core/"
|
||||||
|
log_info "Creating backup: .env.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
|
cp /opt/stacks/core/.env /opt/stacks/core/.env.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
||||||
|
|
||||||
|
# Deploy core stack
|
||||||
|
cd /opt/stacks/core
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
log_success "Core infrastructure deployed"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Wait for Traefik to be ready
|
||||||
|
log_info "Waiting for Traefik to initialize..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Check if Traefik is healthy
|
||||||
|
if docker ps | grep -q "traefik.*Up"; then
|
||||||
|
log_success "Traefik is running"
|
||||||
|
else
|
||||||
|
log_warning "Traefik container check inconclusive, continuing..."
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 4: Deploy infrastructure stack (Dockge and monitoring tools)
|
||||||
|
log_info "Step 4/5: Deploying infrastructure stack..."
|
||||||
|
log_info " - Dockge (Docker Compose Manager)"
|
||||||
|
log_info " - Portainer (Alternative Docker UI)"
|
||||||
|
log_info " - Pi-hole (DNS Ad Blocker)"
|
||||||
|
log_info " - Watchtower (Container Updates)"
|
||||||
|
log_info " - Dozzle (Log Viewer)"
|
||||||
|
log_info " - Glances (System Monitor)"
|
||||||
|
log_info " - Docker Proxy (Security)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy infrastructure stack
|
||||||
|
cp "$REPO_DIR/docker-compose/infrastructure.yml" /opt/stacks/infrastructure/docker-compose.yml
|
||||||
|
cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env
|
||||||
|
|
||||||
|
# Deploy infrastructure stack
|
||||||
|
cd /opt/stacks/infrastructure
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
log_success "Infrastructure stack deployed"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 5: Wait for Dockge to be ready and open browser
|
||||||
|
log_info "Step 5/5: Waiting for Dockge web UI to be ready..."
|
||||||
|
|
||||||
|
DOCKGE_URL="https://dockge.${DOMAIN}"
|
||||||
|
MAX_WAIT=60 # Maximum wait time in seconds
|
||||||
|
WAITED=0
|
||||||
|
|
||||||
|
# Function to check if Dockge is accessible
|
||||||
|
check_dockge() {
|
||||||
|
# Try to connect to Dockge (ignore SSL cert warnings for self-signed during startup)
|
||||||
|
curl -k -s -o /dev/null -w "%{http_code}" "$DOCKGE_URL" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wait for Dockge to respond
|
||||||
|
while [ $WAITED -lt $MAX_WAIT ]; do
|
||||||
|
HTTP_CODE=$(check_dockge)
|
||||||
|
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "401" ]; then
|
||||||
|
log_success "Dockge web UI is ready!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo -n "."
|
||||||
|
sleep 2
|
||||||
|
WAITED=$((WAITED + 2))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $WAITED -ge $MAX_WAIT ]; then
|
||||||
|
log_warning "Dockge did not respond within ${MAX_WAIT} seconds"
|
||||||
|
log_info "It may still be starting up. Check manually at: $DOCKGE_URL"
|
||||||
|
else
|
||||||
|
# Try to open browser
|
||||||
|
log_info "Opening Dockge in your browser..."
|
||||||
|
|
||||||
|
# Detect and use available browser
|
||||||
|
if command -v xdg-open &> /dev/null; then
|
||||||
|
xdg-open "$DOCKGE_URL" &> /dev/null &
|
||||||
|
log_success "Browser opened"
|
||||||
|
elif command -v gnome-open &> /dev/null; then
|
||||||
|
gnome-open "$DOCKGE_URL" &> /dev/null &
|
||||||
|
log_success "Browser opened"
|
||||||
|
elif command -v firefox &> /dev/null; then
|
||||||
|
firefox "$DOCKGE_URL" &> /dev/null &
|
||||||
|
log_success "Browser opened"
|
||||||
|
elif command -v google-chrome &> /dev/null; then
|
||||||
|
google-chrome "$DOCKGE_URL" &> /dev/null &
|
||||||
|
log_success "Browser opened"
|
||||||
|
else
|
||||||
|
log_warning "No browser detected. Please manually open: $DOCKGE_URL"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
log_success "Deployment completed successfully!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
log_info "Access your services:"
|
||||||
|
echo ""
|
||||||
|
echo " 🚀 Dockge: $DOCKGE_URL"
|
||||||
|
echo " 🔒 Authelia: https://auth.${DOMAIN}"
|
||||||
|
echo " 🔀 Traefik: https://traefik.${DOMAIN}"
|
||||||
|
echo ""
|
||||||
|
log_info "Next steps:"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Log in to Dockge using your Authelia credentials"
|
||||||
|
echo " (configured in /opt/stacks/core/authelia/users_database.yml)"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Deploy additional stacks through Dockge's web UI:"
|
||||||
|
echo " - dashboards.yml (Homepage, Homarr)"
|
||||||
|
echo " - media.yml (Plex, Jellyfin, Sonarr, Radarr, etc.)"
|
||||||
|
echo " - media-extended.yml (Readarr, Lidarr, etc.)"
|
||||||
|
echo " - homeassistant.yml (Home Assistant and accessories)"
|
||||||
|
echo " - productivity.yml (Nextcloud, Gitea, wikis)"
|
||||||
|
echo " - monitoring.yml (Grafana, Prometheus, etc.)"
|
||||||
|
echo " - utilities.yml (Backups, code editors, etc.)"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Configure services via the AI assistant in VS Code"
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
log_info "For documentation, see: $REPO_DIR/docs/"
|
||||||
|
log_info "For troubleshooting, see: $REPO_DIR/docs/quick-reference.md"
|
||||||
|
echo ""
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
33
services to proxy.yaml
Normal file
33
services to proxy.yaml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# This is just a list of services by ip:port that should be
|
||||||
|
# proxied via traefik on 192.168.4.4 (Raspberry Pi 4 4GB)
|
||||||
|
|
||||||
|
bookstack: 192.168.4.11:6875
|
||||||
|
mealie: 192.168.4.11:9000
|
||||||
|
wordpress: 192.168.4.11:8088 Set Proxy Host to knot-u.kelinreij.duckdns.org
|
||||||
|
backrest: 192.168.4.11:9898
|
||||||
|
motioneye: 192.168.4.11:8081 (main web UI; additional ports 8765,9901-9904 for streams/cameras)
|
||||||
|
dokuwiki: 192.168.4.11:8087 Set Proxy Host to wiki.kelinreij.duckdns.org
|
||||||
|
formio: 192.168.4.11:3002 set Proxy Host to forms.kelinreij.duckdns.org
|
||||||
|
glances: 192.168.4.11:61208
|
||||||
|
dozzle: 192.168.4.11:8085
|
||||||
|
dockge: 192.168.4.11:5001 set Proxy Host to jarvis.kelinreij.duckdns.org
|
||||||
|
nextcloud: 192.168.4.11:8089 Set Proxy Host to cloud.kelinreij.duckdns.org
|
||||||
|
kopia: 192.168.4.11:51515
|
||||||
|
openkm: 192.168.4.11:18080
|
||||||
|
unmanic: 192.168.4.11:8888
|
||||||
|
gitea: 192.168.4.11:3010 (web UI; SSH on 2222)
|
||||||
|
bitwarden: 192.168.4.11:8000
|
||||||
|
duplicati: 192.168.4.11:8200
|
||||||
|
|
||||||
|
jellyfin: 192.168.4.11:8096
|
||||||
|
jellyseerr: 192.168.4.11:5055
|
||||||
|
prowlarr: 192.168.4.11:9696
|
||||||
|
radarr: 192.168.4.11:7878
|
||||||
|
sonarr: 192.168.4.11:8989
|
||||||
|
lidarr: 192.168.4.11:8686
|
||||||
|
readarr: 192.168.4.11:8787
|
||||||
|
calibre-web: 192.168.4.11:8083
|
||||||
|
mylar3: 192.168.4.11:8090
|
||||||
|
qbittorrent: 192.168.4.11:8080 (behind VPN)
|
||||||
|
tdarr-server: 192.168.4.11:8265 (web UI; API on 8266)
|
||||||
|
|
||||||
Reference in New Issue
Block a user