Update Homepage dashboard and deployment scripts
- Homepage: Reorganize services by stack instead of by category
- Homepage: Add comprehensive Available to Install sections for all stacks
- Homepage: Update config templates with {{HOMEPAGE_VAR_DOMAIN}} placeholder
- Homepage: Change layout from row to column style
- Scripts: Add sudo requirement to deploy-homelab.sh
- Scripts: Replace NVIDIA driver installation with official installer method
- Scripts: Add build prerequisites and nouveau blacklisting
- Docs: Add AI Automation Guidelines section to docker-guidelines.md
- Docs: Document Homepage auto-update requirements and workflow
- Config: Add bookmarks.yaml template for Homepage
- Config: Add alternatives.yml compose file (Portainer, Authentik)
- Config: Update .env.example and authelia configuration
This commit is contained in:
225
.env.example
225
.env.example
@@ -1,7 +1,11 @@
|
|||||||
# Environment Variables Template
|
# Environment Variables Template
|
||||||
# Copy this file to .env and fill in your values
|
# Copy this file to .env and fill in your values: cp .env.example .env
|
||||||
# NEVER commit .env to git!
|
# NEVER commit .env to git!
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# SYSTEM CONFIGURATION
|
||||||
|
# ====================================
|
||||||
|
|
||||||
# User and Group IDs (get with: id -u and id -g)
|
# User and Group IDs (get with: id -u and id -g)
|
||||||
PUID=1000
|
PUID=1000
|
||||||
PGID=1000
|
PGID=1000
|
||||||
@@ -12,59 +16,115 @@ TZ=America/New_York
|
|||||||
# Server IP address
|
# Server IP address
|
||||||
SERVER_IP=192.168.1.100
|
SERVER_IP=192.168.1.100
|
||||||
|
|
||||||
# Domain Configuration
|
# ====================================
|
||||||
DOMAIN=yourdomain.duckdns.org # Your DuckDNS domain
|
# DOMAIN & DNS CONFIGURATION
|
||||||
|
# ====================================
|
||||||
|
|
||||||
# Directory Paths
|
# Your DuckDNS domain (without https://)
|
||||||
USERDIR=/opt/stacks
|
DOMAIN=yourdomain.duckdns.org
|
||||||
MEDIADIR=/mnt/media # Large media files on separate drive
|
|
||||||
DOWNLOADDIR=/mnt/downloads # Downloads on separate drive
|
|
||||||
PROJECTDIR=/home/username/projects
|
|
||||||
|
|
||||||
# DuckDNS Configuration
|
# DuckDNS Configuration
|
||||||
DUCKDNS_TOKEN=your-duckdns-token
|
DUCKDNS_TOKEN=your-duckdns-token
|
||||||
DUCKDNS_SUBDOMAINS=yourdomain # Without .duckdns.org
|
DUCKDNS_SUBDOMAINS=yourdomain # Without .duckdns.org
|
||||||
|
|
||||||
# Let's Encrypt / ACME
|
# Let's Encrypt / ACME (for SSL certificates)
|
||||||
ACME_EMAIL=your-email@example.com
|
ACME_EMAIL=your-email@example.com
|
||||||
|
ADMIN_EMAIL=your-email@example.com # Used for admin user account
|
||||||
|
|
||||||
# Authelia Secrets (generate with: openssl rand -hex 64)
|
# Cloudflare API (optional, for DNS challenge instead of DuckDNS)
|
||||||
AUTHELIA_JWT_SECRET=your-jwt-secret-here-64-chars
|
# CF_DNS_API_TOKEN=your-cloudflare-api-token
|
||||||
AUTHELIA_SESSION_SECRET=your-session-secret-here-64-chars
|
|
||||||
AUTHELIA_STORAGE_ENCRYPTION_KEY=your-encryption-key-here-64-chars
|
|
||||||
|
|
||||||
# SMTP for Authelia Notifications (optional)
|
# ====================================
|
||||||
SMTP_USERNAME=your-email@example.com
|
# AUTHELIA SSO CONFIGURATION
|
||||||
SMTP_PASSWORD=your-smtp-password
|
# ====================================
|
||||||
|
# These secrets are AUTO-GENERATED by setup-homelab.sh
|
||||||
|
# DO NOT manually set these - the setup script will create them!
|
||||||
|
|
||||||
# Authentik SSO (optional - alternative to Authelia with web UI)
|
AUTHELIA_JWT_SECRET=will-be-auto-generated-by-setup-script
|
||||||
# Generate secrets with: openssl rand -hex 50
|
AUTHELIA_SESSION_SECRET=will-be-auto-generated-by-setup-script
|
||||||
AUTHENTIK_SECRET_KEY=your-authentik-secret-key-here-100-chars
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=will-be-auto-generated-by-setup-script
|
||||||
AUTHENTIK_DB_USER=authentik
|
|
||||||
AUTHENTIK_DB_PASSWORD=changeme-authentik-db-password
|
|
||||||
AUTHENTIK_DB_NAME=authentik
|
|
||||||
|
|
||||||
# VPN Configuration (Surfshark)
|
# SMTP for Authelia Notifications (OPTIONAL)
|
||||||
|
# If not configured, notifications are saved to file instead
|
||||||
|
# SMTP_USERNAME=your-email@example.com
|
||||||
|
# SMTP_PASSWORD=your-smtp-password
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# VPN CONFIGURATION (GLUETUN)
|
||||||
|
# ====================================
|
||||||
|
|
||||||
|
# Surfshark OpenVPN (RECOMMENDED - Default)
|
||||||
|
SURFSHARK_USERNAME=your-surfshark-username
|
||||||
|
SURFSHARK_PASSWORD=your-surfshark-password
|
||||||
|
VPN_SERVER_COUNTRIES=Netherlands # Preferred VPN server location
|
||||||
|
|
||||||
|
# Surfshark WireGuard (OPTIONAL - Advanced users only)
|
||||||
|
# Only needed if you prefer WireGuard over OpenVPN
|
||||||
# Get WireGuard details from Surfshark dashboard
|
# Get WireGuard details from Surfshark dashboard
|
||||||
SURFSHARK_PRIVATE_KEY=your-wireguard-private-key
|
# SURFSHARK_PRIVATE_KEY=your-wireguard-private-key
|
||||||
SURFSHARK_ADDRESSES=10.14.0.2/16
|
# SURFSHARK_ADDRESSES=10.14.0.2/16
|
||||||
VPN_COUNTRY=Netherlands # Preferred VPN server location
|
|
||||||
|
|
||||||
# Alternative: OpenVPN credentials (if not using WireGuard)
|
# ====================================
|
||||||
# SURFSHARK_USERNAME=your-surfshark-username
|
# DIRECTORY PATHS
|
||||||
# SURFSHARK_PASSWORD=your-surfshark-password
|
# ====================================
|
||||||
|
|
||||||
|
USERDIR=/opt/stacks
|
||||||
|
MEDIADIR=/mnt/media # Large media files on separate drive
|
||||||
|
DOWNLOADDIR=/mnt/downloads # Downloads on separate drive
|
||||||
|
PROJECTDIR=/home/username/projects
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# ALTERNATIVE SERVICES (OPTIONAL)
|
||||||
|
# Deploy alternatives.yml stack if you want these
|
||||||
|
# ====================================
|
||||||
|
|
||||||
|
# Authentik SSO (alternative to Authelia with web UI)
|
||||||
|
# WARNING: Do not run both Authelia and Authentik at the same time
|
||||||
|
# Generate secrets with: openssl rand -hex 50
|
||||||
|
# AUTHENTIK_SECRET_KEY=your-authentik-secret-key-here-100-chars
|
||||||
|
# AUTHENTIK_DB_USER=authentik
|
||||||
|
# AUTHENTIK_DB_PASSWORD=changeme-authentik-db-password
|
||||||
|
# AUTHENTIK_DB_NAME=authentik
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# MEDIA SERVICES
|
||||||
|
# ====================================
|
||||||
|
|
||||||
# Media Services
|
|
||||||
PLEX_CLAIM=claim-xxxxxxxxxx
|
PLEX_CLAIM=claim-xxxxxxxxxx
|
||||||
|
|
||||||
# Monitoring & Dashboards
|
# qBittorrent
|
||||||
|
QBITTORRENT_USER=admin
|
||||||
|
QBITTORRENT_PASS=changeme
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# INFRASTRUCTURE SERVICES
|
||||||
|
# ====================================
|
||||||
|
|
||||||
|
# Pi-hole
|
||||||
|
PIHOLE_PASSWORD=changeme
|
||||||
|
|
||||||
|
# Watchtower Notifications (optional)
|
||||||
|
# WATCHTOWER_NOTIFICATION_URL=
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# MONITORING & DASHBOARDS
|
||||||
|
# ====================================
|
||||||
|
|
||||||
GRAFANA_ADMIN_PASSWORD=changeme
|
GRAFANA_ADMIN_PASSWORD=changeme
|
||||||
|
|
||||||
# Development Tools
|
# ====================================
|
||||||
|
# DEVELOPMENT TOOLS
|
||||||
|
# ====================================
|
||||||
|
|
||||||
CODE_SERVER_PASSWORD=changeme
|
CODE_SERVER_PASSWORD=changeme
|
||||||
CODE_SERVER_SUDO_PASSWORD=changeme
|
CODE_SERVER_SUDO_PASSWORD=changeme
|
||||||
|
|
||||||
# Databases - General
|
JUPYTER_TOKEN=changeme
|
||||||
|
|
||||||
|
# ====================================
|
||||||
|
# DATABASES - GENERAL
|
||||||
|
# ====================================
|
||||||
|
|
||||||
POSTGRES_USER=postgres
|
POSTGRES_USER=postgres
|
||||||
POSTGRES_PASSWORD=changeme
|
POSTGRES_PASSWORD=changeme
|
||||||
POSTGRES_DB=homelab
|
POSTGRES_DB=homelab
|
||||||
@@ -72,47 +132,54 @@ POSTGRES_DB=homelab
|
|||||||
PGADMIN_EMAIL=admin@example.com
|
PGADMIN_EMAIL=admin@example.com
|
||||||
PGADMIN_PASSWORD=changeme
|
PGADMIN_PASSWORD=changeme
|
||||||
|
|
||||||
# Infrastructure
|
# ====================================
|
||||||
PIHOLE_PASSWORD=changeme
|
# PRODUCTIVITY SERVICES
|
||||||
WATCHTOWER_NOTIFICATION_URL=
|
# ====================================
|
||||||
|
|
||||||
# Productivity Services - Nextcloud
|
# Nextcloud
|
||||||
NEXTCLOUD_ADMIN_USER=admin
|
NEXTCLOUD_ADMIN_USER=admin
|
||||||
NEXTCLOUD_ADMIN_PASSWORD=changeme
|
NEXTCLOUD_ADMIN_PASSWORD=changeme
|
||||||
NEXTCLOUD_DB_PASSWORD=changeme
|
NEXTCLOUD_DB_PASSWORD=changeme
|
||||||
NEXTCLOUD_DB_ROOT_PASSWORD=changeme
|
NEXTCLOUD_DB_ROOT_PASSWORD=changeme
|
||||||
|
|
||||||
# Productivity Services - Gitea
|
# Gitea
|
||||||
GITEA_DB_PASSWORD=changeme
|
GITEA_DB_PASSWORD=changeme
|
||||||
|
|
||||||
# Productivity Services - WordPress
|
# WordPress
|
||||||
WORDPRESS_DB_PASSWORD=changeme
|
WORDPRESS_DB_PASSWORD=changeme
|
||||||
WORDPRESS_DB_ROOT_PASSWORD=changeme
|
WORDPRESS_DB_ROOT_PASSWORD=changeme
|
||||||
|
|
||||||
# Productivity Services - BookStack
|
# BookStack
|
||||||
BOOKSTACK_DB_PASSWORD=changeme
|
BOOKSTACK_DB_PASSWORD=changeme
|
||||||
BOOKSTACK_DB_ROOT_PASSWORD=changeme
|
BOOKSTACK_DB_ROOT_PASSWORD=changeme
|
||||||
|
|
||||||
# Productivity Services - MediaWiki
|
# MediaWiki
|
||||||
MEDIAWIKI_DB_PASSWORD=changeme
|
MEDIAWIKI_DB_PASSWORD=changeme
|
||||||
MEDIAWIKI_DB_ROOT_PASSWORD=changeme
|
MEDIAWIKI_DB_ROOT_PASSWORD=changeme
|
||||||
|
|
||||||
# Utilities - Form.io
|
# ====================================
|
||||||
|
# UTILITIES
|
||||||
|
# ====================================
|
||||||
|
|
||||||
|
# Bitwarden (Vaultwarden) Password Manager
|
||||||
|
# Admin token: openssl rand -base64 48
|
||||||
|
BITWARDEN_ADMIN_TOKEN=changeme-bitwarden-admin-token
|
||||||
|
BITWARDEN_SIGNUPS_ALLOWED=true # Set to false after creating accounts
|
||||||
|
BITWARDEN_INVITATIONS_ALLOWED=true
|
||||||
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_FROM=bitwarden@yourdomain.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_SECURITY=starttls
|
||||||
|
|
||||||
|
# Form.io
|
||||||
FORMIO_JWT_SECRET=changeme
|
FORMIO_JWT_SECRET=changeme
|
||||||
FORMIO_DB_SECRET=changeme
|
FORMIO_DB_SECRET=changeme
|
||||||
|
|
||||||
# Development - Jupyter
|
# ====================================
|
||||||
JUPYTER_TOKEN=changeme
|
# HOMEPAGE DASHBOARD - API KEYS
|
||||||
|
|
||||||
# Cloudflare API (optional, for DNS challenge)
|
|
||||||
# CF_DNS_API_TOKEN=your-cloudflare-api-token
|
|
||||||
|
|
||||||
# qBittorrent
|
|
||||||
QBITTORRENT_USER=admin
|
|
||||||
QBITTORRENT_PASS=changeme
|
|
||||||
|
|
||||||
# Homepage Dashboard - API Keys and Tokens
|
|
||||||
# Generate these from each service's settings page
|
# Generate these from each service's settings page
|
||||||
|
# ====================================
|
||||||
|
|
||||||
HOMEPAGE_VAR_DOMAIN=${DOMAIN}
|
HOMEPAGE_VAR_DOMAIN=${DOMAIN}
|
||||||
HOMEPAGE_VAR_SERVER_IP=${SERVER_IP}
|
HOMEPAGE_VAR_SERVER_IP=${SERVER_IP}
|
||||||
HOMEPAGE_VAR_PORTAINER_KEY=your-portainer-api-key
|
HOMEPAGE_VAR_PORTAINER_KEY=your-portainer-api-key
|
||||||
@@ -140,55 +207,3 @@ HOMEPAGE_VAR_UNIFI_USER=your-unifi-username
|
|||||||
HOMEPAGE_VAR_UNIFI_PASS=your-unifi-password
|
HOMEPAGE_VAR_UNIFI_PASS=your-unifi-password
|
||||||
|
|
||||||
# Add your own variables below
|
# Add your own variables below
|
||||||
|
|
||||||
# Get WireGuard details from Surfshark dashboard
|
|
||||||
SURFSHARK_PRIVATE_KEY=your-wireguard-private-key
|
|
||||||
SURFSHARK_ADDRESSES=10.14.0.2/16
|
|
||||||
VPN_COUNTRY=Netherlands # Preferred VPN server location
|
|
||||||
|
|
||||||
# Alternative: OpenVPN credentials (if not using WireGuard)
|
|
||||||
# SURFSHARK_USERNAME=your-surfshark-username
|
|
||||||
# SURFSHARK_PASSWORD=your-surfshark-password
|
|
||||||
|
|
||||||
# Plex Configuration
|
|
||||||
PLEX_CLAIM=claim-xxxxxxxxxx
|
|
||||||
|
|
||||||
# Monitoring Passwords
|
|
||||||
GRAFANA_ADMIN_PASSWORD=changeme
|
|
||||||
|
|
||||||
# Code Server Passwords
|
|
||||||
CODE_SERVER_PASSWORD=changeme
|
|
||||||
CODE_SERVER_SUDO_PASSWORD=changeme
|
|
||||||
|
|
||||||
# Database Credentials
|
|
||||||
POSTGRES_USER=postgres
|
|
||||||
POSTGRES_PASSWORD=changeme
|
|
||||||
POSTGRES_DB=homelab
|
|
||||||
|
|
||||||
PGADMIN_EMAIL=admin@example.com
|
|
||||||
PGADMIN_PASSWORD=changeme
|
|
||||||
|
|
||||||
# Jupyter Token
|
|
||||||
JUPYTER_TOKEN=changeme
|
|
||||||
|
|
||||||
# Pi-hole
|
|
||||||
PIHOLE_PASSWORD=changeme
|
|
||||||
|
|
||||||
# Bitwarden (Vaultwarden) Password Manager
|
|
||||||
# Admin token: openssl rand -base64 48
|
|
||||||
BITWARDEN_ADMIN_TOKEN=changeme-bitwarden-admin-token
|
|
||||||
BITWARDEN_SIGNUPS_ALLOWED=true # Set to false after creating accounts
|
|
||||||
BITWARDEN_INVITATIONS_ALLOWED=true
|
|
||||||
SMTP_HOST=smtp.gmail.com
|
|
||||||
SMTP_FROM=bitwarden@yourdomain.com
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_SECURITY=starttls
|
|
||||||
# SMTP_USERNAME and SMTP_PASSWORD defined above
|
|
||||||
|
|
||||||
# Watchtower Notifications (optional)
|
|
||||||
# WATCHTOWER_NOTIFICATION_URL=
|
|
||||||
|
|
||||||
# Cloudflare API (optional, for DNS challenge)
|
|
||||||
# CF_DNS_API_TOKEN=your-cloudflare-api-token
|
|
||||||
|
|
||||||
# Add your own variables below
|
|
||||||
|
|||||||
272
AGENT_INSTRUCTIONS.md
Normal file
272
AGENT_INSTRUCTIONS.md
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
# AI Agent Instructions for Homelab Management
|
||||||
|
|
||||||
|
## Primary Directive
|
||||||
|
You are an AI agent specialized in managing Docker-based homelab infrastructure using Dockge. Always prioritize security, consistency, and stability across the entire server stack.
|
||||||
|
|
||||||
|
## Core Operating Principles
|
||||||
|
|
||||||
|
### 1. Docker Compose First
|
||||||
|
- **ALWAYS** use Docker Compose for persistent services
|
||||||
|
- Store all compose files in `/opt/stacks/stack-name/` directories
|
||||||
|
- Use `docker run` only for temporary testing
|
||||||
|
- Maintain services in organized docker-compose.yml files
|
||||||
|
|
||||||
|
### 2. Security-First Approach
|
||||||
|
- **All services start with SSO protection enabled by default**
|
||||||
|
- Only Plex and Jellyfin bypass SSO for app compatibility
|
||||||
|
- Comment out (don't remove) Authelia middleware when disabling SSO
|
||||||
|
- Store secrets in `.env` files, never in compose files
|
||||||
|
|
||||||
|
### 3. File Structure Standards
|
||||||
|
```
|
||||||
|
/opt/stacks/
|
||||||
|
├── core/ # Deploy FIRST (DuckDNS, Traefik, Authelia, Gluetun)
|
||||||
|
├── infrastructure/ # Dockge, Portainer, Pi-hole
|
||||||
|
├── dashboards/ # Homepage, Homarr
|
||||||
|
├── media/ # Plex, Jellyfin, *arr services
|
||||||
|
└── [other-stacks]/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Storage Strategy
|
||||||
|
- **Config files**: `/opt/stacks/stack-name/config/`
|
||||||
|
- **Large data**: Separate drives (`/mnt/media`, `/mnt/downloads`)
|
||||||
|
- **Small data**: Docker named volumes
|
||||||
|
- **Secrets**: `.env` files (never commit)
|
||||||
|
|
||||||
|
## Service Creation Template
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
service-name:
|
||||||
|
image: image:tag # Always pin versions
|
||||||
|
container_name: service-name
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
ports:
|
||||||
|
- "host:container" # Only if not using Traefik
|
||||||
|
volumes:
|
||||||
|
- /opt/stacks/stack-name/config:/config
|
||||||
|
- service-data:/data
|
||||||
|
# Large data on separate drives:
|
||||||
|
# - /mnt/media:/media
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
labels:
|
||||||
|
# Traefik routing
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.service-name.rule=Host(`service.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.service-name.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.service-name.tls.certresolver=letsencrypt"
|
||||||
|
# SSO protection (ENABLED BY DEFAULT)
|
||||||
|
- "traefik.http.routers.service-name.middlewares=authelia@docker"
|
||||||
|
# Organization
|
||||||
|
- "homelab.category=category-name"
|
||||||
|
- "homelab.description=Service description"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
service-data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
homelab-network:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Critical Deployment Order
|
||||||
|
|
||||||
|
1. **Core Stack First**: Deploy `/opt/stacks/core/docker-compose.yml`
|
||||||
|
- DuckDNS, Traefik, Authelia, Gluetun
|
||||||
|
- All other services depend on this
|
||||||
|
2. **Infrastructure**: Dockge, Portainer, monitoring
|
||||||
|
3. **Applications**: Media services, dashboards, etc.
|
||||||
|
|
||||||
|
## VPN Integration Rules
|
||||||
|
|
||||||
|
Use Gluetun for services requiring VPN:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
download-client:
|
||||||
|
network_mode: "service:gluetun"
|
||||||
|
depends_on:
|
||||||
|
- gluetun
|
||||||
|
# No ports section - use Gluetun's ports
|
||||||
|
```
|
||||||
|
|
||||||
|
Map ports in Gluetun service:
|
||||||
|
```yaml
|
||||||
|
gluetun:
|
||||||
|
ports:
|
||||||
|
- 8080:8080 # Download client web UI
|
||||||
|
```
|
||||||
|
|
||||||
|
## SSO Management
|
||||||
|
|
||||||
|
### Enable SSO (Default)
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.service.middlewares=authelia@docker"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable SSO (Only for Plex/Jellyfin/Apps)
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
# - "traefik.http.routers.service.middlewares=authelia@docker"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Agent Actions Checklist
|
||||||
|
|
||||||
|
### Before Any Change
|
||||||
|
- [ ] Read existing compose files for context
|
||||||
|
- [ ] Check port availability
|
||||||
|
- [ ] Verify network dependencies
|
||||||
|
- [ ] Ensure core stack is deployed
|
||||||
|
- [ ] Backup current configuration
|
||||||
|
|
||||||
|
### When Creating Services
|
||||||
|
- [ ] Use LinuxServer.io images when available
|
||||||
|
- [ ] Pin image versions (no `:latest`)
|
||||||
|
- [ ] Apply consistent naming conventions
|
||||||
|
- [ ] Enable Authelia middleware by default
|
||||||
|
- [ ] Configure proper volume mounts
|
||||||
|
- [ ] Set PUID/PGID for file permissions
|
||||||
|
|
||||||
|
### When Modifying Services
|
||||||
|
- [ ] Maintain existing patterns
|
||||||
|
- [ ] Consider stack-wide impact
|
||||||
|
- [ ] Update only necessary components
|
||||||
|
- [ ] Validate YAML syntax
|
||||||
|
- [ ] Test service restart
|
||||||
|
|
||||||
|
### File Management
|
||||||
|
- [ ] Store configs in `/opt/stacks/stack-name/`
|
||||||
|
- [ ] Use `/mnt/` for large data (>50GB)
|
||||||
|
- [ ] Create `.env.example` templates
|
||||||
|
- [ ] Document non-obvious configurations
|
||||||
|
|
||||||
|
## Common Agent Tasks
|
||||||
|
|
||||||
|
### Deploy New Service
|
||||||
|
1. Create stack directory: `/opt/stacks/stack-name/`
|
||||||
|
2. Write docker-compose.yml with template
|
||||||
|
3. Create `.env` file for secrets
|
||||||
|
4. Deploy: `docker compose up -d`
|
||||||
|
5. Verify Traefik routing
|
||||||
|
6. Test SSO protection
|
||||||
|
|
||||||
|
### Update Existing Service
|
||||||
|
1. Read current configuration
|
||||||
|
2. Make minimal necessary changes
|
||||||
|
3. Validate dependencies still work
|
||||||
|
4. Redeploy: `docker compose up -d service-name`
|
||||||
|
5. Check logs for errors
|
||||||
|
|
||||||
|
### Enable/Disable VPN
|
||||||
|
1. For VPN: Add `network_mode: "service:gluetun"`
|
||||||
|
2. Move port mapping to Gluetun service
|
||||||
|
3. Add `depends_on: gluetun`
|
||||||
|
4. For no VPN: Remove network_mode, add ports directly
|
||||||
|
|
||||||
|
### Toggle SSO
|
||||||
|
1. Enable: Add authelia middleware label
|
||||||
|
2. Disable: Comment out middleware label
|
||||||
|
3. Redeploy service
|
||||||
|
4. Verify access works as expected
|
||||||
|
|
||||||
|
## Error Prevention
|
||||||
|
|
||||||
|
### Port Conflicts
|
||||||
|
- Check existing services before assigning ports
|
||||||
|
- Use Traefik instead of port mapping when possible
|
||||||
|
- Document port usage in comments
|
||||||
|
|
||||||
|
### Permission Issues
|
||||||
|
- Always set PUID=1000, PGID=1000
|
||||||
|
- Check directory ownership on host
|
||||||
|
- Use consistent user/group across services
|
||||||
|
|
||||||
|
### Network Issues
|
||||||
|
- Verify shared networks exist
|
||||||
|
- Use service names for inter-service communication
|
||||||
|
- Ensure Traefik can reach services
|
||||||
|
|
||||||
|
## Key Configurations to Monitor
|
||||||
|
|
||||||
|
### Traefik Labels
|
||||||
|
- Correct hostname format: `service.${DOMAIN}`
|
||||||
|
- Proper entrypoint: `websecure`
|
||||||
|
- Certificate resolver: `letsencrypt`
|
||||||
|
- Port specification if needed
|
||||||
|
|
||||||
|
### Authelia Integration
|
||||||
|
- Middleware applied to protected services
|
||||||
|
- Bypass rules for apps requiring direct access
|
||||||
|
- User database and access rules updated
|
||||||
|
|
||||||
|
### Volume Mounts
|
||||||
|
- Config files in stack directories
|
||||||
|
- Large data on separate drives
|
||||||
|
- Named volumes for persistent data
|
||||||
|
- Proper permissions and ownership
|
||||||
|
|
||||||
|
## Emergency Procedures
|
||||||
|
|
||||||
|
### Service Won't Start
|
||||||
|
1. Check logs: `docker compose logs service-name`
|
||||||
|
2. Verify YAML syntax
|
||||||
|
3. Check file permissions
|
||||||
|
4. Validate environment variables
|
||||||
|
5. Ensure dependencies are running
|
||||||
|
|
||||||
|
### SSL Certificate Issues
|
||||||
|
- Verify DuckDNS is updating IP
|
||||||
|
- Check Traefik logs
|
||||||
|
- Ensure port 80/443 accessible
|
||||||
|
- Validate domain DNS resolution
|
||||||
|
|
||||||
|
### Authentication Problems
|
||||||
|
- Check Authelia logs
|
||||||
|
- Verify user database format
|
||||||
|
- Test bypass rules
|
||||||
|
- Validate Traefik middleware configuration
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
- All services accessible via HTTPS
|
||||||
|
- SSO working for protected services
|
||||||
|
- No port conflicts
|
||||||
|
- Proper file permissions
|
||||||
|
- Services restart automatically
|
||||||
|
- Logs show no persistent errors
|
||||||
|
- Certificates auto-renew
|
||||||
|
- VPN routing working for download clients
|
||||||
|
|
||||||
|
## Agent Limitations
|
||||||
|
|
||||||
|
### Never Do
|
||||||
|
- Use `docker run` for permanent services
|
||||||
|
- Commit secrets to compose files
|
||||||
|
- Use `:latest` tags in production
|
||||||
|
- Bypass security without explicit request
|
||||||
|
- Modify core stack without understanding dependencies
|
||||||
|
|
||||||
|
### Always Do
|
||||||
|
- Read existing configurations first
|
||||||
|
- Test changes in isolation when possible
|
||||||
|
- Document complex configurations
|
||||||
|
- Follow established naming patterns
|
||||||
|
- Prioritize security over convenience
|
||||||
|
- Maintain consistency across the stack
|
||||||
|
|
||||||
|
## Communication Guidelines
|
||||||
|
|
||||||
|
- Explain what you're doing and why
|
||||||
|
- Highlight security implications
|
||||||
|
- Warn about service dependencies
|
||||||
|
- Provide rollback instructions
|
||||||
|
- Document any manual steps required
|
||||||
|
- Ask for clarification on ambiguous requests
|
||||||
|
|
||||||
|
This framework ensures reliable, secure, and maintainable homelab infrastructure while enabling automated management through AI agents.
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
# Authelia Configuration
|
# Authelia Configuration
|
||||||
# Copy to /opt/stacks/authelia/configuration.yml
|
# Copy to /opt/stacks/authelia/configuration.yml
|
||||||
|
# IMPORTANT: Replace 'your-domain.duckdns.org' with your actual DuckDNS domain
|
||||||
|
|
||||||
server:
|
server:
|
||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
@@ -12,10 +13,10 @@ theme: dark
|
|||||||
|
|
||||||
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
||||||
|
|
||||||
default_redirection_url: https://auth.${DOMAIN}
|
default_redirection_url: https://auth.your-domain.duckdns.org
|
||||||
|
|
||||||
totp:
|
totp:
|
||||||
issuer: ${DOMAIN}
|
issuer: your-domain.duckdns.org
|
||||||
period: 30
|
period: 30
|
||||||
skew: 1
|
skew: 1
|
||||||
|
|
||||||
@@ -35,25 +36,25 @@ access_control:
|
|||||||
|
|
||||||
rules:
|
rules:
|
||||||
# Bypass Authelia for Jellyfin (allow app access)
|
# Bypass Authelia for Jellyfin (allow app access)
|
||||||
- domain: jellyfin.${DOMAIN}
|
- domain: jellyfin.your-domain.duckdns.org
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for Plex (allow app access)
|
# Bypass for Plex (allow app access)
|
||||||
- domain: plex.${DOMAIN}
|
- domain: plex.your-domain.duckdns.org
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for Home Assistant (has its own auth)
|
# Bypass for Home Assistant (has its own auth)
|
||||||
- domain: ha.${DOMAIN}
|
- domain: ha.your-domain.duckdns.org
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Protected: All other services require authentication
|
# Protected: All other services require authentication
|
||||||
- domain: "*.${DOMAIN}"
|
- domain: "*.your-domain.duckdns.org"
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Two-factor for admin services (optional)
|
# Two-factor for admin services (optional)
|
||||||
# - domain:
|
# - domain:
|
||||||
# - "admin.${DOMAIN}"
|
# - "admin.your-domain.duckdns.org"
|
||||||
# - "portainer.${DOMAIN}"
|
# - "portainer.your-domain.duckdns.org"
|
||||||
# policy: two_factor
|
# policy: two_factor
|
||||||
|
|
||||||
session:
|
session:
|
||||||
@@ -62,7 +63,7 @@ session:
|
|||||||
expiration: 1h
|
expiration: 1h
|
||||||
inactivity: 5m
|
inactivity: 5m
|
||||||
remember_me_duration: 1M
|
remember_me_duration: 1M
|
||||||
domain: ${DOMAIN}
|
domain: your-domain.duckdns.org
|
||||||
|
|
||||||
regulation:
|
regulation:
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
@@ -72,22 +73,9 @@ regulation:
|
|||||||
storage:
|
storage:
|
||||||
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
||||||
local:
|
local:
|
||||||
path: /config/db.sqlite3
|
path: /data/db.sqlite3
|
||||||
|
|
||||||
notifier:
|
notifier:
|
||||||
# File-based notifications (for testing)
|
# File-based notifications (for development/testing)
|
||||||
filesystem:
|
filesystem:
|
||||||
filename: /config/notification.txt
|
filename: /data/notification.txt
|
||||||
|
|
||||||
# SMTP notifications (recommended for production)
|
|
||||||
# smtp:
|
|
||||||
# host: smtp.gmail.com
|
|
||||||
# port: 587
|
|
||||||
# username: ${SMTP_USERNAME}
|
|
||||||
# password: ${AUTHELIA_NOTIFIER_SMTP_PASSWORD}
|
|
||||||
# sender: authelia@${DOMAIN}
|
|
||||||
# identifier: localhost
|
|
||||||
# subject: "[Authelia] {title}"
|
|
||||||
# startup_check_address: test@authelia.com
|
|
||||||
# disable_require_tls: false
|
|
||||||
# disable_html_emails: false
|
|
||||||
|
|||||||
29
config-templates/homepage/bookmarks.yaml
Normal file
29
config-templates/homepage/bookmarks.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
# Homepage Bookmarks
|
||||||
|
|
||||||
|
- Documentation:
|
||||||
|
- GitHub Repository:
|
||||||
|
- icon: github.png
|
||||||
|
href: https://github.com/kelinfoxy/AI-Homelab
|
||||||
|
description: AI-Homelab Repository
|
||||||
|
|
||||||
|
- Homepage Docs:
|
||||||
|
- icon: homepage.png
|
||||||
|
href: https://gethomepage.dev
|
||||||
|
description: Homepage Documentation
|
||||||
|
|
||||||
|
- Useful Links:
|
||||||
|
- DuckDNS:
|
||||||
|
- icon: duckdns.png
|
||||||
|
href: https://www.duckdns.org
|
||||||
|
description: Dynamic DNS Service
|
||||||
|
|
||||||
|
- Let's Encrypt:
|
||||||
|
- icon: si-letsencrypt
|
||||||
|
href: https://letsencrypt.org
|
||||||
|
description: SSL Certificate Provider
|
||||||
|
|
||||||
|
- Docker Hub:
|
||||||
|
- icon: docker.png
|
||||||
|
href: https://hub.docker.com
|
||||||
|
description: Container Images
|
||||||
@@ -1,373 +1,245 @@
|
|||||||
# Homepage Configuration - Services
|
|
||||||
# Copy to /opt/stacks/homepage/config/services.yaml
|
|
||||||
# This file is AI-configurable - Homepage will auto-discover services via Docker labels
|
|
||||||
|
|
||||||
---
|
---
|
||||||
# Infrastructure Services
|
# Currently Installed Services - Grouped by Stack
|
||||||
- Infrastructure:
|
|
||||||
- Dockge:
|
|
||||||
icon: dockge.png
|
|
||||||
href: https://dockge.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Docker Compose Stack Manager (PRIMARY)
|
|
||||||
container: dockge
|
|
||||||
widget:
|
|
||||||
type: dockge
|
|
||||||
url: http://dockge:5001
|
|
||||||
|
|
||||||
|
- Core Stack (core.yml):
|
||||||
- Traefik:
|
- Traefik:
|
||||||
icon: traefik.png
|
icon: traefik.png
|
||||||
href: https://traefik.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://traefik.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Reverse Proxy & SSL
|
description: Reverse Proxy & SSL
|
||||||
container: traefik
|
|
||||||
widget:
|
|
||||||
type: traefik
|
|
||||||
url: http://traefik:8080
|
|
||||||
|
|
||||||
- Authelia:
|
- Authelia:
|
||||||
icon: authelia.png
|
icon: authelia.png
|
||||||
href: https://auth.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://auth.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Single Sign-On
|
description: Authentication Portal
|
||||||
container: authelia
|
|
||||||
widget:
|
|
||||||
type: authelia
|
|
||||||
url: http://authelia:9091
|
|
||||||
|
|
||||||
- Portainer:
|
- Infrastructure Stack (infrastructure.yml):
|
||||||
icon: portainer.png
|
- Dockge:
|
||||||
href: https://portainer.{{HOMEPAGE_VAR_DOMAIN}}
|
icon: dockge.png
|
||||||
description: Docker Management (Secondary)
|
href: https://dockge.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
container: portainer
|
description: Docker Compose Manager
|
||||||
widget:
|
|
||||||
type: portainer
|
|
||||||
url: http://portainer:9000
|
|
||||||
env: 1
|
|
||||||
key: {{HOMEPAGE_VAR_PORTAINER_KEY}}
|
|
||||||
|
|
||||||
- Pi-hole:
|
|
||||||
icon: pi-hole.png
|
|
||||||
href: https://pihole.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Network-wide Ad Blocking
|
|
||||||
container: pihole
|
|
||||||
widget:
|
|
||||||
type: pihole
|
|
||||||
url: http://pihole
|
|
||||||
key: {{HOMEPAGE_VAR_PIHOLE_KEY}}
|
|
||||||
|
|
||||||
- Dozzle:
|
- Dozzle:
|
||||||
icon: dozzle.png
|
icon: dozzle.png
|
||||||
href: https://dozzle.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://dozzle.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Real-time Docker Logs
|
description: Real-time Log Viewer
|
||||||
container: dozzle
|
|
||||||
|
|
||||||
- Glances:
|
- Glances:
|
||||||
icon: glances.png
|
icon: glances.png
|
||||||
href: https://glances.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://glances.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: System Monitoring
|
description: System Monitoring
|
||||||
container: glances
|
|
||||||
widget:
|
|
||||||
type: glances
|
|
||||||
url: http://glances:61208
|
|
||||||
metric: cpu
|
|
||||||
|
|
||||||
# Dashboards
|
- Pi-hole:
|
||||||
- Dashboards:
|
icon: pi-hole.png
|
||||||
|
href: https://pihole.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Network-wide Ad Blocking
|
||||||
|
|
||||||
|
- Dashboards Stack (dashboards.yml):
|
||||||
|
- Homepage:
|
||||||
|
icon: homepage.png
|
||||||
|
href: https://home.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: This Dashboard
|
||||||
|
|
||||||
- Homarr:
|
- Homarr:
|
||||||
icon: homarr.png
|
icon: homarr.png
|
||||||
href: https://homarr.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://homarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Alternative Dashboard
|
description: Alternative Dashboard
|
||||||
container: homarr
|
|
||||||
|
|
||||||
- Uptime Kuma:
|
- Alternatives Stack (alternatives.yml):
|
||||||
icon: uptime-kuma.png
|
- Portainer:
|
||||||
href: https://status.{{HOMEPAGE_VAR_DOMAIN}}
|
icon: portainer.png
|
||||||
description: Uptime Monitoring
|
href: https://portainer.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
container: uptime-kuma
|
description: Container Management UI
|
||||||
widget:
|
|
||||||
type: uptimekuma
|
|
||||||
url: http://uptime-kuma:3001
|
|
||||||
slug: {{HOMEPAGE_VAR_UPTIMEKUMA_SLUG}}
|
|
||||||
|
|
||||||
# Media - Streaming
|
- Authentik:
|
||||||
- Media Streaming:
|
icon: authentik.png
|
||||||
|
href: https://authentik.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Alternative Auth Provider
|
||||||
|
|
||||||
|
# Available to Install - Grouped by Stack
|
||||||
|
|
||||||
|
- Media Stack (media.yml):
|
||||||
- Plex:
|
- Plex:
|
||||||
icon: plex.png
|
icon: plex.png
|
||||||
href: https://plex.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://plex.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Media Server
|
description: Media Server
|
||||||
container: plex
|
|
||||||
widget:
|
|
||||||
type: plex
|
|
||||||
url: http://plex:32400
|
|
||||||
key: {{HOMEPAGE_VAR_PLEX_KEY}}
|
|
||||||
|
|
||||||
- Jellyfin:
|
- Jellyfin:
|
||||||
icon: jellyfin.png
|
icon: jellyfin.png
|
||||||
href: https://jellyfin.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://jellyfin.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Open Source Media Server
|
description: Open Source Media Server
|
||||||
container: jellyfin
|
|
||||||
widget:
|
|
||||||
type: jellyfin
|
|
||||||
url: http://jellyfin:8096
|
|
||||||
key: {{HOMEPAGE_VAR_JELLYFIN_KEY}}
|
|
||||||
|
|
||||||
- Jellyseerr:
|
|
||||||
icon: jellyseerr.png
|
|
||||||
href: https://jellyseerr.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Media Requests
|
|
||||||
container: jellyseerr
|
|
||||||
widget:
|
|
||||||
type: jellyseerr
|
|
||||||
url: http://jellyseerr:5055
|
|
||||||
key: {{HOMEPAGE_VAR_JELLYSEERR_KEY}}
|
|
||||||
|
|
||||||
# Media - Management
|
|
||||||
- Media Management:
|
|
||||||
- Sonarr:
|
- Sonarr:
|
||||||
icon: sonarr.png
|
icon: sonarr.png
|
||||||
href: https://sonarr.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://sonarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: TV Show Management
|
description: TV Shows Automation
|
||||||
container: sonarr
|
|
||||||
widget:
|
|
||||||
type: sonarr
|
|
||||||
url: http://sonarr:8989
|
|
||||||
key: {{HOMEPAGE_VAR_SONARR_KEY}}
|
|
||||||
|
|
||||||
- Radarr:
|
- Radarr:
|
||||||
icon: radarr.png
|
icon: radarr.png
|
||||||
href: https://radarr.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://radarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Movie Management
|
description: Movies Automation
|
||||||
container: radarr
|
|
||||||
widget:
|
|
||||||
type: radarr
|
|
||||||
url: http://radarr:7878
|
|
||||||
key: {{HOMEPAGE_VAR_RADARR_KEY}}
|
|
||||||
|
|
||||||
- Lidarr:
|
|
||||||
icon: lidarr.png
|
|
||||||
href: https://lidarr.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Music Management
|
|
||||||
container: lidarr
|
|
||||||
widget:
|
|
||||||
type: lidarr
|
|
||||||
url: http://lidarr:8686
|
|
||||||
key: {{HOMEPAGE_VAR_LIDARR_KEY}}
|
|
||||||
|
|
||||||
- Readarr:
|
|
||||||
icon: readarr.png
|
|
||||||
href: https://readarr.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Book Management
|
|
||||||
container: readarr
|
|
||||||
widget:
|
|
||||||
type: readarr
|
|
||||||
url: http://readarr:8787
|
|
||||||
key: {{HOMEPAGE_VAR_READARR_KEY}}
|
|
||||||
|
|
||||||
- Prowlarr:
|
- Prowlarr:
|
||||||
icon: prowlarr.png
|
icon: prowlarr.png
|
||||||
href: https://prowlarr.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://prowlarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Indexer Manager
|
description: Indexer Manager
|
||||||
container: prowlarr
|
|
||||||
widget:
|
|
||||||
type: prowlarr
|
|
||||||
url: http://prowlarr:9696
|
|
||||||
key: {{HOMEPAGE_VAR_PROWLARR_KEY}}
|
|
||||||
|
|
||||||
# Downloads
|
|
||||||
- Downloads:
|
|
||||||
- qBittorrent:
|
- qBittorrent:
|
||||||
icon: qbittorrent.png
|
icon: qbittorrent.png
|
||||||
href: https://qbit.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://qbit.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Torrent Client (via VPN)
|
description: Torrent Client
|
||||||
container: qbittorrent
|
|
||||||
widget:
|
|
||||||
type: qbittorrent
|
|
||||||
url: http://gluetun:8080
|
|
||||||
username: {{HOMEPAGE_VAR_QBITTORRENT_USER}}
|
|
||||||
password: {{HOMEPAGE_VAR_QBITTORRENT_PASS}}
|
|
||||||
|
|
||||||
- Gluetun:
|
- Media Extended Stack (media-extended.yml):
|
||||||
icon: gluetun.png
|
- Readarr:
|
||||||
href: http://gluetun:8000
|
icon: readarr.png
|
||||||
description: VPN Client (Surfshark)
|
href: https://readarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
container: gluetun
|
description: Books Automation
|
||||||
|
|
||||||
|
- Lidarr:
|
||||||
|
icon: lidarr.png
|
||||||
|
href: https://lidarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Music Automation
|
||||||
|
|
||||||
|
- Mylar3:
|
||||||
|
icon: mylar.png
|
||||||
|
href: https://mylar.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Comics Manager
|
||||||
|
|
||||||
# Books & Comics
|
|
||||||
- Books & Comics:
|
|
||||||
- Calibre-Web:
|
- Calibre-Web:
|
||||||
icon: calibre-web.png
|
icon: calibre-web.png
|
||||||
href: https://calibre.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://calibre.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Ebook Library
|
description: Ebook Library
|
||||||
container: calibre-web
|
|
||||||
|
|
||||||
- Lazy Librarian:
|
- Jellyseerr:
|
||||||
icon: lazylibrarian.png
|
icon: jellyseerr.png
|
||||||
href: https://lazylibrarian.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://jellyseerr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Book Manager
|
description: Media Request Manager
|
||||||
container: lazylibrarian
|
|
||||||
|
|
||||||
- Mylar3:
|
|
||||||
icon: mylar3.png
|
|
||||||
href: https://mylar.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Comic Book Manager
|
|
||||||
container: mylar3
|
|
||||||
|
|
||||||
# Transcoding
|
|
||||||
- Transcoding:
|
|
||||||
- Tdarr:
|
- Tdarr:
|
||||||
icon: tdarr.png
|
icon: tdarr.png
|
||||||
href: https://tdarr.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://tdarr.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Distributed Transcoding
|
description: Media Transcoding
|
||||||
container: tdarr-server
|
|
||||||
widget:
|
|
||||||
type: tdarr
|
|
||||||
url: http://tdarr-server:8265
|
|
||||||
|
|
||||||
- Unmanic:
|
- Home Automation Stack (homeassistant.yml):
|
||||||
icon: unmanic.png
|
|
||||||
href: https://unmanic.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Library Optimizer
|
|
||||||
container: unmanic
|
|
||||||
|
|
||||||
# Home Automation
|
|
||||||
- Home Automation:
|
|
||||||
- Home Assistant:
|
- Home Assistant:
|
||||||
icon: home-assistant.png
|
icon: home-assistant.png
|
||||||
href: https://ha.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://ha.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Home Automation Hub
|
description: Home Automation Platform
|
||||||
# Note: Uses host network, configure manually
|
|
||||||
widget:
|
|
||||||
type: homeassistant
|
|
||||||
url: http://{{HOMEPAGE_VAR_SERVER_IP}}:8123
|
|
||||||
key: {{HOMEPAGE_VAR_HA_KEY}}
|
|
||||||
|
|
||||||
- ESPHome:
|
- ESPHome:
|
||||||
icon: esphome.png
|
icon: esphome.png
|
||||||
href: https://esphome.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://esphome.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: ESP Device Manager
|
description: ESP Device Manager
|
||||||
container: esphome
|
|
||||||
|
|
||||||
- Node-RED:
|
- Node-RED:
|
||||||
icon: node-red.png
|
icon: node-red.png
|
||||||
href: https://nodered.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://nodered.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Flow Automation
|
description: Flow-based Automation
|
||||||
container: nodered
|
|
||||||
|
|
||||||
- TasmoAdmin:
|
|
||||||
icon: tasmota.png
|
|
||||||
href: https://tasmoadmin.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Tasmota Device Manager
|
|
||||||
container: tasmoadmin
|
|
||||||
|
|
||||||
- Zigbee2MQTT:
|
- Zigbee2MQTT:
|
||||||
icon: zigbee2mqtt.png
|
icon: zigbee2mqtt.png
|
||||||
href: https://zigbee2mqtt.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://zigbee.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Zigbee Bridge
|
description: Zigbee Bridge
|
||||||
container: zigbee2mqtt
|
|
||||||
|
|
||||||
- MotionEye:
|
- Mosquitto:
|
||||||
icon: motioneye.png
|
icon: mosquitto.png
|
||||||
href: https://motioneye.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://mqtt.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Video Surveillance
|
description: MQTT Broker
|
||||||
container: motioneye
|
|
||||||
|
|
||||||
# Productivity
|
- Productivity Stack (productivity.yml):
|
||||||
- Productivity:
|
|
||||||
- Nextcloud:
|
- Nextcloud:
|
||||||
icon: nextcloud.png
|
icon: nextcloud.png
|
||||||
href: https://nextcloud.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://nextcloud.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: File Sync & Share
|
description: Cloud Storage & Collaboration
|
||||||
container: nextcloud
|
|
||||||
widget:
|
- Gitea:
|
||||||
type: nextcloud
|
icon: gitea.png
|
||||||
url: http://nextcloud
|
href: https://gitea.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
username: {{HOMEPAGE_VAR_NEXTCLOUD_USER}}
|
description: Git Repository
|
||||||
password: {{HOMEPAGE_VAR_NEXTCLOUD_PASS}}
|
|
||||||
|
|
||||||
- Mealie:
|
- Mealie:
|
||||||
icon: mealie.png
|
icon: mealie.png
|
||||||
href: https://mealie.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://mealie.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Recipe Manager
|
description: Recipe Manager
|
||||||
container: mealie
|
|
||||||
|
|
||||||
- Gitea:
|
|
||||||
icon: gitea.png
|
|
||||||
href: https://git.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Git Service
|
|
||||||
container: gitea
|
|
||||||
|
|
||||||
- Code Server:
|
|
||||||
icon: vscode.png
|
|
||||||
href: https://code.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: VS Code in Browser
|
|
||||||
container: code-server
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
- Documentation:
|
|
||||||
- BookStack:
|
- BookStack:
|
||||||
icon: bookstack.png
|
icon: bookstack.png
|
||||||
href: https://docs.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://bookstack.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Documentation Platform
|
description: Wiki Platform
|
||||||
container: bookstack
|
|
||||||
widget:
|
|
||||||
type: bookstack
|
|
||||||
url: http://bookstack
|
|
||||||
key: {{HOMEPAGE_VAR_BOOKSTACK_KEY}}
|
|
||||||
|
|
||||||
- DokuWiki:
|
- DokuWiki:
|
||||||
icon: dokuwiki.png
|
icon: dokuwiki.png
|
||||||
href: https://wiki.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://dokuwiki.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: File-based Wiki
|
description: Simple Wiki
|
||||||
container: dokuwiki
|
|
||||||
|
|
||||||
- MediaWiki:
|
|
||||||
icon: mediawiki.png
|
|
||||||
href: https://mediawiki.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Wiki Platform
|
|
||||||
container: mediawiki
|
|
||||||
|
|
||||||
- WordPress:
|
- WordPress:
|
||||||
icon: wordpress.png
|
icon: wordpress.png
|
||||||
href: https://blog.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://wordpress.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Blog Platform
|
description: CMS Platform
|
||||||
container: wordpress
|
|
||||||
|
|
||||||
# Backups & Monitoring
|
- Monitoring Stack (monitoring.yml):
|
||||||
- Backups & Tools:
|
|
||||||
- Backrest:
|
|
||||||
icon: backrest.png
|
|
||||||
href: https://backrest.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Backup Manager (Restic)
|
|
||||||
container: backrest
|
|
||||||
|
|
||||||
- Duplicati:
|
|
||||||
icon: duplicati.png
|
|
||||||
href: https://duplicati.{{HOMEPAGE_VAR_DOMAIN}}
|
|
||||||
description: Backup Software
|
|
||||||
container: duplicati
|
|
||||||
|
|
||||||
# Monitoring Stack
|
|
||||||
- Monitoring:
|
|
||||||
- Grafana:
|
- Grafana:
|
||||||
icon: grafana.png
|
icon: grafana.png
|
||||||
href: https://grafana.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://grafana.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Metrics Visualization
|
description: Metrics Dashboard
|
||||||
container: grafana
|
|
||||||
widget:
|
|
||||||
type: grafana
|
|
||||||
url: http://grafana:3000
|
|
||||||
username: {{HOMEPAGE_VAR_GRAFANA_USER}}
|
|
||||||
password: {{HOMEPAGE_VAR_GRAFANA_PASS}}
|
|
||||||
|
|
||||||
- Prometheus:
|
- Prometheus:
|
||||||
icon: prometheus.png
|
icon: prometheus.png
|
||||||
href: https://prometheus.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://prometheus.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Metrics Collection
|
description: Metrics Collection
|
||||||
container: prometheus
|
|
||||||
widget:
|
|
||||||
type: prometheus
|
|
||||||
url: http://prometheus:9090
|
|
||||||
|
|
||||||
- Uptime Kuma:
|
- Uptime Kuma:
|
||||||
icon: uptime-kuma.png
|
icon: uptime-kuma.png
|
||||||
href: https://status.{{HOMEPAGE_VAR_DOMAIN}}
|
href: https://uptime.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
description: Status Page
|
description: Uptime Monitoring
|
||||||
container: uptime-kuma
|
|
||||||
|
- Loki:
|
||||||
|
icon: loki.png
|
||||||
|
href: https://loki.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Log Aggregation
|
||||||
|
|
||||||
|
- cAdvisor:
|
||||||
|
icon: cadvisor.png
|
||||||
|
href: https://cadvisor.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Container Metrics
|
||||||
|
|
||||||
|
- Development Stack (development.yml):
|
||||||
|
- VS Code Server:
|
||||||
|
icon: vscode.png
|
||||||
|
href: https://code.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Browser-based IDE
|
||||||
|
|
||||||
|
- GitLab:
|
||||||
|
icon: gitlab.png
|
||||||
|
href: https://gitlab.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: DevOps Platform
|
||||||
|
|
||||||
|
- Jupyter:
|
||||||
|
icon: jupyter.png
|
||||||
|
href: https://jupyter.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Data Science Notebooks
|
||||||
|
|
||||||
|
- pgAdmin:
|
||||||
|
icon: pgadmin.png
|
||||||
|
href: https://pgadmin.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: PostgreSQL Admin
|
||||||
|
|
||||||
|
- Utilities Stack (utilities.yml):
|
||||||
|
- Backrest:
|
||||||
|
icon: mdi-backup-restore
|
||||||
|
href: https://backrest.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Backup Solution
|
||||||
|
|
||||||
|
- Duplicati:
|
||||||
|
icon: duplicati.png
|
||||||
|
href: https://duplicati.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Backup Software
|
||||||
|
|
||||||
|
- Vaultwarden:
|
||||||
|
icon: vaultwarden.png
|
||||||
|
href: https://vault.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Password Manager
|
||||||
|
|
||||||
|
- Formio:
|
||||||
|
icon: mdi-form-select
|
||||||
|
href: https://formio.{{HOMEPAGE_VAR_DOMAIN}}
|
||||||
|
description: Form Builder
|
||||||
|
|||||||
@@ -1,63 +1,45 @@
|
|||||||
# Homepage Configuration - Settings
|
|
||||||
# Copy to /opt/stacks/homepage/config/settings.yaml
|
|
||||||
|
|
||||||
---
|
---
|
||||||
title: Homelab Dashboard
|
# Homepage Settings
|
||||||
background: https://images.unsplash.com/photo-1558591710-4b4a1ae0f04d
|
# For all configuration options: https://gethomepage.dev/configs/settings/
|
||||||
backgroundOpacity: 0.2
|
|
||||||
|
title: AI Homelab Dashboard
|
||||||
|
|
||||||
theme: dark
|
theme: dark
|
||||||
color: slate
|
color: slate
|
||||||
headerStyle: boxed
|
|
||||||
hideVersion: true
|
|
||||||
hideErrors: false
|
|
||||||
showStats: true
|
|
||||||
target: _self # Open links in same tab
|
|
||||||
|
|
||||||
# Layout configuration
|
|
||||||
layout:
|
layout:
|
||||||
Infrastructure:
|
Core Stack (core.yml):
|
||||||
style: row
|
style: column
|
||||||
columns: 4
|
|
||||||
Dashboards:
|
|
||||||
style: row
|
|
||||||
columns: 2
|
columns: 2
|
||||||
Media Streaming:
|
Infrastructure Stack (infrastructure.yml):
|
||||||
style: row
|
style: column
|
||||||
columns: 3
|
|
||||||
Media Management:
|
|
||||||
style: row
|
|
||||||
columns: 4
|
columns: 4
|
||||||
Downloads:
|
Dashboards Stack (dashboards.yml):
|
||||||
style: row
|
style: column
|
||||||
columns: 2
|
columns: 2
|
||||||
Books & Comics:
|
Alternatives Stack (alternatives.yml):
|
||||||
style: row
|
style: column
|
||||||
columns: 3
|
|
||||||
Home Automation:
|
|
||||||
style: row
|
|
||||||
columns: 4
|
|
||||||
Productivity:
|
|
||||||
style: row
|
|
||||||
columns: 4
|
|
||||||
Documentation:
|
|
||||||
style: row
|
|
||||||
columns: 4
|
|
||||||
Backups & Tools:
|
|
||||||
style: row
|
|
||||||
columns: 2
|
columns: 2
|
||||||
Monitoring:
|
Media Stack (media.yml):
|
||||||
style: row
|
style: column
|
||||||
columns: 3
|
columns: 3
|
||||||
|
Media Extended Stack (media-extended.yml):
|
||||||
|
style: column
|
||||||
|
columns: 3
|
||||||
|
Home Automation Stack (homeassistant.yml):
|
||||||
|
style: column
|
||||||
|
columns: 3
|
||||||
|
Productivity Stack (productivity.yml):
|
||||||
|
style: column
|
||||||
|
columns: 3
|
||||||
|
Monitoring Stack (monitoring.yml):
|
||||||
|
style: column
|
||||||
|
columns: 3
|
||||||
|
Development Stack (development.yml):
|
||||||
|
style: column
|
||||||
|
columns: 4
|
||||||
|
Utilities Stack (utilities.yml):
|
||||||
|
style: column
|
||||||
|
columns: 4
|
||||||
|
|
||||||
# Quick search
|
headerStyle: boxed
|
||||||
quicklaunch:
|
|
||||||
searchDescriptions: true
|
|
||||||
hideInternetSearch: false
|
|
||||||
showSearchSuggestions: true
|
|
||||||
|
|
||||||
# Providers for additional functionality
|
|
||||||
providers:
|
|
||||||
longhorn:
|
|
||||||
url: http://longhorn:9500
|
|
||||||
openweathermap: {{HOMEPAGE_VAR_OPENWEATHER_KEY}}
|
|
||||||
weatherapi: {{HOMEPAGE_VAR_WEATHERAPI_KEY}}
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
# Homepage Configuration - Widgets
|
|
||||||
# Copy to /opt/stacks/homepage/config/widgets.yaml
|
|
||||||
# Displays system resources and other information
|
|
||||||
|
|
||||||
---
|
---
|
||||||
- logo:
|
# Homepage Widgets Configuration
|
||||||
icon: https://avatars.githubusercontent.com/u/... # Your logo here
|
# Service widgets omitted per user request
|
||||||
|
|
||||||
- search:
|
- resources:
|
||||||
provider: google
|
cpu: true
|
||||||
target: _blank
|
memory: true
|
||||||
|
disk: /
|
||||||
|
|
||||||
- datetime:
|
- datetime:
|
||||||
text_size: xl
|
text_size: xl
|
||||||
@@ -16,34 +13,3 @@
|
|||||||
dateStyle: long
|
dateStyle: long
|
||||||
timeStyle: short
|
timeStyle: short
|
||||||
hourCycle: h23
|
hourCycle: h23
|
||||||
|
|
||||||
- resources:
|
|
||||||
label: System
|
|
||||||
cpu: true
|
|
||||||
memory: true
|
|
||||||
disk: /
|
|
||||||
cputemp: true
|
|
||||||
uptime: true
|
|
||||||
units: metric
|
|
||||||
|
|
||||||
- resources:
|
|
||||||
label: Storage
|
|
||||||
disk: /mnt/media
|
|
||||||
expanded: true
|
|
||||||
|
|
||||||
- resources:
|
|
||||||
label: Downloads
|
|
||||||
disk: /mnt/downloads
|
|
||||||
expanded: true
|
|
||||||
|
|
||||||
- openmeteo:
|
|
||||||
label: Weather
|
|
||||||
latitude: 40.7128
|
|
||||||
longitude: -74.0060
|
|
||||||
units: metric
|
|
||||||
cache: 5
|
|
||||||
|
|
||||||
- unifi_console:
|
|
||||||
url: http://unifi:8443
|
|
||||||
username: {{HOMEPAGE_VAR_UNIFI_USER}}
|
|
||||||
password: {{HOMEPAGE_VAR_UNIFI_PASS}}
|
|
||||||
|
|||||||
149
docker-compose/alternatives.yml
Normal file
149
docker-compose/alternatives.yml
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# Alternative Services Stack
|
||||||
|
# This stack contains alternative/optional services that are not deployed by default
|
||||||
|
# Deploy manually through Dockge if you want to use these alternatives
|
||||||
|
# Place in /opt/stacks/alternatives/docker-compose.yml
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Portainer - Docker management UI (Alternative to Dockge)
|
||||||
|
# Access at: https://portainer.${DOMAIN}
|
||||||
|
# NOTE: Dockge is the default Docker management UI. Deploy Portainer only if you prefer its interface
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:2.19.4
|
||||||
|
container_name: portainer
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
- traefik-network
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- portainer-data:/data
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
labels:
|
||||||
|
- "homelab.category=alternatives"
|
||||||
|
- "homelab.description=Docker container management UI (Alternative to Dockge)"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.portainer.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.routers.portainer.middlewares=authelia@docker"
|
||||||
|
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||||
|
|
||||||
|
# Authentik - Alternative SSO/Identity Provider with Web UI
|
||||||
|
# Access at: https://authentik.${DOMAIN}
|
||||||
|
# NOTE: Authelia is the default SSO. Deploy Authentik only if you need a web UI for user management
|
||||||
|
# WARNING: Do not run both Authelia and Authentik at the same time
|
||||||
|
authentik-server:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.2.0
|
||||||
|
container_name: authentik-server
|
||||||
|
restart: unless-stopped
|
||||||
|
command: server
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
- traefik-network
|
||||||
|
volumes:
|
||||||
|
- /opt/stacks/authentik/media:/media
|
||||||
|
- /opt/stacks/authentik/custom-templates:/templates
|
||||||
|
environment:
|
||||||
|
- AUTHENTIK_REDIS__HOST=authentik-redis
|
||||||
|
- AUTHENTIK_POSTGRESQL__HOST=authentik-db
|
||||||
|
- AUTHENTIK_POSTGRESQL__USER=${AUTHENTIK_DB_USER:-authentik}
|
||||||
|
- AUTHENTIK_POSTGRESQL__NAME=${AUTHENTIK_DB_NAME:-authentik}
|
||||||
|
- AUTHENTIK_POSTGRESQL__PASSWORD=${AUTHENTIK_DB_PASSWORD}
|
||||||
|
- AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
|
||||||
|
- AUTHENTIK_ERROR_REPORTING__ENABLED=false
|
||||||
|
labels:
|
||||||
|
- "homelab.category=alternatives"
|
||||||
|
- "homelab.description=SSO/Identity provider with web UI (Alternative to Authelia)"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.authentik.rule=Host(`authentik.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.authentik.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.routers.authentik.middlewares=authelia@docker"
|
||||||
|
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
|
||||||
|
depends_on:
|
||||||
|
- authentik-db
|
||||||
|
- authentik-redis
|
||||||
|
|
||||||
|
# Authentik Worker - Background task processor
|
||||||
|
authentik-worker:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.2.0
|
||||||
|
container_name: authentik-worker
|
||||||
|
restart: unless-stopped
|
||||||
|
command: worker
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
volumes:
|
||||||
|
- /opt/stacks/authentik/media:/media
|
||||||
|
- /opt/stacks/authentik/certs:/certs
|
||||||
|
- /opt/stacks/authentik/custom-templates:/templates
|
||||||
|
environment:
|
||||||
|
- AUTHENTIK_REDIS__HOST=authentik-redis
|
||||||
|
- AUTHENTIK_POSTGRESQL__HOST=authentik-db
|
||||||
|
- AUTHENTIK_POSTGRESQL__USER=${AUTHENTIK_DB_USER:-authentik}
|
||||||
|
- AUTHENTIK_POSTGRESQL__NAME=${AUTHENTIK_DB_NAME:-authentik}
|
||||||
|
- AUTHENTIK_POSTGRESQL__PASSWORD=${AUTHENTIK_DB_PASSWORD}
|
||||||
|
- AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
|
||||||
|
- AUTHENTIK_ERROR_REPORTING__ENABLED=false
|
||||||
|
labels:
|
||||||
|
- "homelab.category=alternatives"
|
||||||
|
- "homelab.description=Authentik background worker"
|
||||||
|
depends_on:
|
||||||
|
- authentik-db
|
||||||
|
- authentik-redis
|
||||||
|
|
||||||
|
# Authentik Database - PostgreSQL
|
||||||
|
authentik-db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: authentik-db
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
volumes:
|
||||||
|
- authentik-db-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=${AUTHENTIK_DB_USER:-authentik}
|
||||||
|
- POSTGRES_PASSWORD=${AUTHENTIK_DB_PASSWORD}
|
||||||
|
- POSTGRES_DB=${AUTHENTIK_DB_NAME:-authentik}
|
||||||
|
labels:
|
||||||
|
- "homelab.category=alternatives"
|
||||||
|
- "homelab.description=Authentik database"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${AUTHENTIK_DB_USER:-authentik}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# Authentik Redis - Cache and message queue
|
||||||
|
authentik-redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: authentik-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
volumes:
|
||||||
|
- authentik-redis-data:/data
|
||||||
|
command: --save 60 1 --loglevel warning
|
||||||
|
labels:
|
||||||
|
- "homelab.category=alternatives"
|
||||||
|
- "homelab.description=Authentik cache and messaging"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
portainer-data:
|
||||||
|
driver: local
|
||||||
|
authentik-db-data:
|
||||||
|
driver: local
|
||||||
|
authentik-redis-data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
homelab-network:
|
||||||
|
external: true
|
||||||
|
traefik-network:
|
||||||
|
external: true
|
||||||
|
|
||||||
@@ -21,13 +21,14 @@ services:
|
|||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
- TZ=${TZ}
|
- TZ=${TZ}
|
||||||
|
- HOMEPAGE_ALLOWED_HOSTS=home.${DOMAIN}
|
||||||
labels:
|
labels:
|
||||||
- "homelab.category=dashboard"
|
- "homelab.category=dashboard"
|
||||||
- "homelab.description=Application dashboard (AI-configurable)"
|
- "homelab.description=Application dashboard (AI-configurable)"
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.homepage.rule=Host(`home.${DOMAIN}`)"
|
- "traefik.http.routers.homepage.rule=Host(`home.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.homepage.entrypoints=websecure"
|
- "traefik.http.routers.homepage.entrypoints=websecure"
|
||||||
- "traefik.http.routers.homepage.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.homepage.tls=true"
|
||||||
- "traefik.http.routers.homepage.middlewares=authelia@docker"
|
- "traefik.http.routers.homepage.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.homepage.loadbalancer.server.port=3000"
|
- "traefik.http.services.homepage.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.homarr.rule=Host(`homarr.${DOMAIN}`)"
|
- "traefik.http.routers.homarr.rule=Host(`homarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.homarr.entrypoints=websecure"
|
- "traefik.http.routers.homarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.homarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.homarr.tls=true"
|
||||||
- "traefik.http.routers.homarr.middlewares=authelia@docker"
|
- "traefik.http.routers.homarr.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.homarr.loadbalancer.server.port=7575"
|
- "traefik.http.services.homarr.loadbalancer.server.port=7575"
|
||||||
|
|
||||||
|
|||||||
@@ -990,6 +990,65 @@ docker compose -f docker-compose/service.yml logs -f
|
|||||||
# Access service and verify it works
|
# Access service and verify it works
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## AI Automation Guidelines
|
||||||
|
|
||||||
|
### Homepage Dashboard Management
|
||||||
|
|
||||||
|
**Automatic Configuration Updates**
|
||||||
|
|
||||||
|
Homepage configuration must be kept synchronized with deployed services. The AI assistant handles this automatically:
|
||||||
|
|
||||||
|
**Template Location:**
|
||||||
|
- Config templates: `/home/kelin/AI-Homelab/config-templates/homepage/`
|
||||||
|
- Active configs: `/opt/stacks/homepage/config/`
|
||||||
|
|
||||||
|
**Key Principles:**
|
||||||
|
|
||||||
|
1. **Hard-Coded URLs Required**: Homepage does NOT support variables in href links
|
||||||
|
- Template uses `{{HOMEPAGE_VAR_DOMAIN}}` as placeholder
|
||||||
|
- Active config uses `kelin-hass.duckdns.org` hard-coded
|
||||||
|
- AI must replace placeholders when deploying configs
|
||||||
|
|
||||||
|
2. **No Container Restart Needed**: Homepage picks up config changes instantly
|
||||||
|
- Simply edit YAML files in `/opt/stacks/homepage/config/`
|
||||||
|
- Refresh browser to see changes
|
||||||
|
- DO NOT restart the container
|
||||||
|
|
||||||
|
3. **Stack-Based Organization**: Services grouped by their compose file
|
||||||
|
- **Currently Installed**: Shows running services grouped by stack
|
||||||
|
- **Available to Install**: Shows undeployed services from repository
|
||||||
|
|
||||||
|
4. **Automatic Updates Required**: AI must update Homepage configs when:
|
||||||
|
- New service is deployed → Add to appropriate stack section
|
||||||
|
- Service is removed → Remove from stack section
|
||||||
|
- Domain/subdomain changes → Update all affected href URLs
|
||||||
|
- Stack file is renamed → Update section headers
|
||||||
|
|
||||||
|
**Configuration Structure:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# services.yaml
|
||||||
|
- Stack Name (compose-file.yml):
|
||||||
|
- Service Name:
|
||||||
|
icon: service.png
|
||||||
|
href: https://subdomain.kelin-hass.duckdns.org # Hard-coded!
|
||||||
|
description: Service description
|
||||||
|
```
|
||||||
|
|
||||||
|
**Deployment Workflow:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# When deploying from template:
|
||||||
|
cp /home/kelin/AI-Homelab/config-templates/homepage/*.yaml /opt/stacks/homepage/config/
|
||||||
|
sed -i 's/{{HOMEPAGE_VAR_DOMAIN}}/kelin-hass.duckdns.org/g' /opt/stacks/homepage/config/services.yaml
|
||||||
|
|
||||||
|
# No restart needed - configs load instantly
|
||||||
|
```
|
||||||
|
|
||||||
|
**Critical Reminder:** Homepage is the single source of truth for service inventory. Keep it updated or users won't know what's deployed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
Following these guidelines ensures:
|
Following these guidelines ensures:
|
||||||
|
|||||||
@@ -31,9 +31,15 @@ log_error() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Check if running as root
|
# Check if running as root
|
||||||
if [ "$EUID" -eq 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
log_error "Please do NOT run this script as root or with sudo"
|
log_error "Please run as root (use: sudo ./deploy-homelab.sh)"
|
||||||
log_info "Run as: ./deploy-homelab.sh"
|
exit 1
|
||||||
|
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
|
||||||
|
|
||||||
@@ -42,6 +48,7 @@ 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 ""
|
||||||
|
|
||||||
# Check if .env file exists
|
# Check if .env file exists
|
||||||
@@ -111,6 +118,37 @@ cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/
|
|||||||
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Create Authelia users database with admin credentials from setup script
|
||||||
|
if [ -f /tmp/authelia_admin_credentials.tmp ]; then
|
||||||
|
log_info "Configuring Authelia admin user..."
|
||||||
|
source /tmp/authelia_admin_credentials.tmp
|
||||||
|
|
||||||
|
cat > /opt/stacks/core/authelia/users_database.yml << EOF
|
||||||
|
###############################################################
|
||||||
|
# Users Database #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
users:
|
||||||
|
${ADMIN_USER}:
|
||||||
|
displayname: "${ADMIN_USER}"
|
||||||
|
password: "${PASSWORD_HASH}"
|
||||||
|
email: ${ADMIN_EMAIL}
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
|
- dev
|
||||||
|
EOF
|
||||||
|
|
||||||
|
log_success "Authelia admin user configured: $ADMIN_USER"
|
||||||
|
rm -f /tmp/authelia_admin_credentials.tmp
|
||||||
|
else
|
||||||
|
log_warning "Admin credentials not found. Using template users_database.yml"
|
||||||
|
log_info "You will need to manually configure /opt/stacks/core/authelia/users_database.yml"
|
||||||
|
fi
|
||||||
|
|
||||||
# Deploy core stack
|
# Deploy core stack
|
||||||
cd /opt/stacks/core
|
cd /opt/stacks/core
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@@ -133,7 +171,6 @@ echo ""
|
|||||||
# Step 4: Deploy infrastructure stack (Dockge and monitoring tools)
|
# Step 4: Deploy infrastructure stack (Dockge and monitoring tools)
|
||||||
log_info "Step 4/5: Deploying infrastructure stack..."
|
log_info "Step 4/5: Deploying infrastructure stack..."
|
||||||
log_info " - Dockge (Docker Compose Manager)"
|
log_info " - Dockge (Docker Compose Manager)"
|
||||||
log_info " - Portainer (Alternative Docker UI)"
|
|
||||||
log_info " - Pi-hole (DNS Ad Blocker)"
|
log_info " - Pi-hole (DNS Ad Blocker)"
|
||||||
log_info " - Watchtower (Container Updates)"
|
log_info " - Watchtower (Container Updates)"
|
||||||
log_info " - Dozzle (Log Viewer)"
|
log_info " - Dozzle (Log Viewer)"
|
||||||
@@ -222,6 +259,7 @@ echo " 1. Log in to Dockge using your Authelia credentials"
|
|||||||
echo " (configured in /opt/stacks/core/authelia/users_database.yml)"
|
echo " (configured in /opt/stacks/core/authelia/users_database.yml)"
|
||||||
echo ""
|
echo ""
|
||||||
echo " 2. Deploy additional stacks through Dockge's web UI:"
|
echo " 2. Deploy additional stacks through Dockge's web UI:"
|
||||||
|
echo " - alternatives.yml (Portainer, Authentik - optional alternatives)"
|
||||||
echo " - dashboards.yml (Homepage, Homarr)"
|
echo " - dashboards.yml (Homepage, Homarr)"
|
||||||
echo " - media.yml (Plex, Jellyfin, Sonarr, Radarr, etc.)"
|
echo " - media.yml (Plex, Jellyfin, Sonarr, Radarr, etc.)"
|
||||||
echo " - media-extended.yml (Readarr, Lidarr, etc.)"
|
echo " - media-extended.yml (Readarr, Lidarr, etc.)"
|
||||||
|
|||||||
@@ -152,58 +152,117 @@ else
|
|||||||
log_warning "SSH server failed to start, check configuration"
|
log_warning "SSH server failed to start, check configuration"
|
||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
# Step 7: Generate Authelia Secrets
|
||||||
|
log_info "Step 7/9: Generating Authelia authentication secrets..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Step 7: Detect and Install NVIDIA Drivers (if applicable)
|
# Function to generate a secure random secret
|
||||||
log_info "Step 7/9: Checking for NVIDIA GPU..."
|
generate_secret() {
|
||||||
|
openssl rand -hex 64
|
||||||
|
}
|
||||||
|
|
||||||
# Detect NVIDIA GPU
|
# Check if .env file exists in the repo
|
||||||
if lspci | grep -i nvidia > /dev/null; then
|
REPO_ENV_FILE="$HOME/AI-Homelab/.env"
|
||||||
log_info "NVIDIA GPU detected:"
|
if [ ! -f "$REPO_ENV_FILE" ]; then
|
||||||
lspci | grep -i nvidia
|
log_error ".env file not found at $REPO_ENV_FILE"
|
||||||
echo ""
|
log_info "Please create .env file from .env.example first"
|
||||||
|
exit 1
|
||||||
# Check if NVIDIA drivers are already installed
|
|
||||||
if nvidia-smi &> /dev/null; then
|
|
||||||
log_warning "NVIDIA drivers are already installed"
|
|
||||||
NVIDIA_INSTALLED=true
|
|
||||||
else
|
|
||||||
log_info "Installing NVIDIA drivers..."
|
|
||||||
# Install NVIDIA driver (non-interactive)
|
|
||||||
apt-get install -y nvidia-driver
|
|
||||||
log_success "NVIDIA drivers installed"
|
|
||||||
NVIDIA_INSTALLED=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if NVIDIA Container Toolkit is installed
|
|
||||||
if command -v nvidia-container-runtime &> /dev/null; then
|
|
||||||
log_warning "NVIDIA Container Toolkit is already installed"
|
|
||||||
else
|
|
||||||
log_info "Installing NVIDIA Container Toolkit..."
|
|
||||||
# Install NVIDIA Container Toolkit
|
|
||||||
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
|
|
||||||
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
|
|
||||||
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
|
|
||||||
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
|
||||||
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y nvidia-container-toolkit
|
|
||||||
|
|
||||||
# Configure Docker to use NVIDIA runtime
|
|
||||||
nvidia-ctk runtime configure --runtime=docker
|
|
||||||
systemctl restart docker
|
|
||||||
|
|
||||||
log_success "NVIDIA Container Toolkit installed and configured"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$NVIDIA_INSTALLED" = false ]; then
|
|
||||||
log_warning "NVIDIA drivers were installed. A reboot may be required for changes to take effect."
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
else
|
|
||||||
log_info "No NVIDIA GPU detected, skipping driver installation"
|
|
||||||
echo ""
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if secrets are already set (not placeholder values)
|
||||||
|
CURRENT_JWT=$(grep "^AUTHELIA_JWT_SECRET=" "$REPO_ENV_FILE" | cut -d'=' -f2)
|
||||||
|
if [ -n "$CURRENT_JWT" ] && [ "$CURRENT_JWT" != "your-jwt-secret-here" ] && [ ${#CURRENT_JWT} -ge 64 ]; then
|
||||||
|
log_warning "Authelia secrets appear to already be set in .env"
|
||||||
|
read -p "Do you want to regenerate them? (y/N): " -n 1 -r
|
||||||
|
echo ""
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Keeping existing secrets"
|
||||||
|
else
|
||||||
|
# Generate new secrets
|
||||||
|
log_info "Generating new JWT secret..."
|
||||||
|
JWT_SECRET=$(generate_secret)
|
||||||
|
log_info "Generating new session secret..."
|
||||||
|
SESSION_SECRET=$(generate_secret)
|
||||||
|
log_info "Generating new storage encryption key..."
|
||||||
|
ENCRYPTION_KEY=$(generate_secret)
|
||||||
|
|
||||||
|
# Update .env file
|
||||||
|
sed -i "s|^AUTHELIA_JWT_SECRET=.*|AUTHELIA_JWT_SECRET=${JWT_SECRET}|" "$REPO_ENV_FILE"
|
||||||
|
sed -i "s|^AUTHELIA_SESSION_SECRET=.*|AUTHELIA_SESSION_SECRET=${SESSION_SECRET}|" "$REPO_ENV_FILE"
|
||||||
|
sed -i "s|^AUTHELIA_STORAGE_ENCRYPTION_KEY=.*|AUTHELIA_STORAGE_ENCRYPTION_KEY=${ENCRYPTION_KEY}|" "$REPO_ENV_FILE"
|
||||||
|
|
||||||
|
log_success "New secrets generated and saved to .env"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Generate secrets for first time
|
||||||
|
log_info "Generating new JWT secret..."
|
||||||
|
JWT_SECRET=$(generate_secret)
|
||||||
|
log_info "Generating new session secret..."
|
||||||
|
SESSION_SECRET=$(generate_secret)
|
||||||
|
log_info "Generating new storage encryption key..."
|
||||||
|
ENCRYPTION_KEY=$(generate_secret)
|
||||||
|
|
||||||
|
# Update .env file
|
||||||
|
sed -i "s|^AUTHELIA_JWT_SECRET=.*|AUTHELIA_JWT_SECRET=${JWT_SECRET}|" "$REPO_ENV_FILE"
|
||||||
|
sed -i "s|^AUTHELIA_SESSION_SECRET=.*|AUTHELIA_SESSION_SECRET=${SESSION_SECRET}|" "$REPO_ENV_FILE"
|
||||||
|
sed -i "s|^AUTHELIA_STORAGE_ENCRYPTION_KEY=.*|AUTHELIA_STORAGE_ENCRYPTION_KEY=${ENCRYPTION_KEY}|" "$REPO_ENV_FILE"
|
||||||
|
|
||||||
|
log_success "Secrets generated and saved to .env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt for admin password
|
||||||
|
echo ""
|
||||||
|
log_info "Setting up Authelia admin user..."
|
||||||
|
echo ""
|
||||||
|
read -p "Enter admin username (default: admin): " ADMIN_USER
|
||||||
|
ADMIN_USER=${ADMIN_USER:-admin}
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -sp "Enter password for $ADMIN_USER: " ADMIN_PASSWORD
|
||||||
|
echo ""
|
||||||
|
read -sp "Confirm password: " ADMIN_PASSWORD_CONFIRM
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$ADMIN_PASSWORD" = "$ADMIN_PASSWORD_CONFIRM" ]; then
|
||||||
|
if [ ${#ADMIN_PASSWORD} -lt 8 ]; then
|
||||||
|
log_warning "Password should be at least 8 characters long"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log_warning "Passwords do not match, please try again"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Generate password hash using Docker
|
||||||
|
log_info "Generating password hash (this may take a moment)..."
|
||||||
|
PASSWORD_HASH=$(docker run --rm authelia/authelia:4.37 authelia crypto hash generate argon2 --password "$ADMIN_PASSWORD" | grep '^\$argon2')
|
||||||
|
|
||||||
|
if [ -z "$PASSWORD_HASH" ]; then
|
||||||
|
log_error "Failed to generate password hash"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read admin email from .env or prompt
|
||||||
|
ADMIN_EMAIL=$(grep "^ADMIN_EMAIL=" "$REPO_ENV_FILE" | cut -d'=' -f2)
|
||||||
|
if [ -z "$ADMIN_EMAIL" ] || [ "$ADMIN_EMAIL" = "admin@example.com" ]; then
|
||||||
|
read -p "Enter admin email address: " ADMIN_EMAIL
|
||||||
|
sed -i "s|^ADMIN_EMAIL=.*|ADMIN_EMAIL=${ADMIN_EMAIL}|" "$REPO_ENV_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Admin user configured: $ADMIN_USER"
|
||||||
|
log_success "Password hash generated and will be applied during deployment"
|
||||||
|
|
||||||
|
# Store the admin credentials for the deployment script
|
||||||
|
cat > /tmp/authelia_admin_credentials.tmp << EOF
|
||||||
|
ADMIN_USER=$ADMIN_USER
|
||||||
|
ADMIN_EMAIL=$ADMIN_EMAIL
|
||||||
|
PASSWORD_HASH=$PASSWORD_HASH
|
||||||
|
EOF
|
||||||
|
chmod 600 /tmp/authelia_admin_credentials.tmp
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Step 8: Create Directory Structure
|
# Step 8: Create Directory Structure
|
||||||
log_info "Step 8/9: Creating directory structure..."
|
log_info "Step 8/9: Creating directory structure..."
|
||||||
mkdir -p /opt/stacks
|
mkdir -p /opt/stacks
|
||||||
@@ -223,6 +282,7 @@ chown -R "$ACTUAL_USER:$ACTUAL_USER" /mnt/backups
|
|||||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" /mnt/surveillance
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" /mnt/surveillance
|
||||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" /mnt/git
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" /mnt/git
|
||||||
|
|
||||||
|
|
||||||
log_success "Directory structure created"
|
log_success "Directory structure created"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@@ -235,38 +295,195 @@ su - "$ACTUAL_USER" -c "docker network create dockerproxy-network 2>/dev/null ||
|
|||||||
log_success "Docker networks created"
|
log_success "Docker networks created"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Optional: Detect and Install NVIDIA Drivers (if applicable)
|
||||||
|
log_info "Optional: Checking for NVIDIA GPU..."
|
||||||
|
|
||||||
|
# Detect NVIDIA GPU
|
||||||
|
if lspci | grep -i nvidia > /dev/null; then
|
||||||
|
log_info "NVIDIA GPU detected:"
|
||||||
|
GPU_INFO=$(lspci | grep -i nvidia)
|
||||||
|
echo "$GPU_INFO"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if NVIDIA drivers are already installed
|
||||||
|
if nvidia-smi &> /dev/null; then
|
||||||
|
log_warning "NVIDIA drivers are already installed"
|
||||||
|
nvidia-smi
|
||||||
|
NVIDIA_INSTALLED=true
|
||||||
|
NVIDIA_REBOOT_NEEDED=false
|
||||||
|
else
|
||||||
|
log_warning "NVIDIA GPU detected but drivers not installed"
|
||||||
|
echo ""
|
||||||
|
read -p "Do you want to install NVIDIA drivers now? (y/N): " -n 1 -r
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Installing NVIDIA drivers using official installer..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Extract GPU model for driver selection
|
||||||
|
GPU_MODEL=$(echo "$GPU_INFO" | grep -oP 'NVIDIA.*' | head -1)
|
||||||
|
log_info "GPU Model: $GPU_MODEL"
|
||||||
|
|
||||||
|
# Determine recommended driver version
|
||||||
|
log_info "Determining recommended driver version..."
|
||||||
|
|
||||||
|
# Install prerequisites for NVIDIA installer
|
||||||
|
log_info "Installing build prerequisites..."
|
||||||
|
apt-get install -y build-essential linux-headers-$(uname -r) pkg-config libglvnd-dev 2>&1 | tee /tmp/nvidia-prereq.log
|
||||||
|
|
||||||
|
# Disable nouveau driver
|
||||||
|
log_info "Disabling nouveau driver..."
|
||||||
|
if [ ! -f /etc/modprobe.d/blacklist-nouveau.conf ]; then
|
||||||
|
cat > /etc/modprobe.d/blacklist-nouveau.conf << EOF
|
||||||
|
blacklist nouveau
|
||||||
|
options nouveau modeset=0
|
||||||
|
EOF
|
||||||
|
update-initramfs -u
|
||||||
|
log_success "Nouveau driver blacklisted (reboot required)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Download latest NVIDIA driver
|
||||||
|
NVIDIA_VERSION="550.127.05" # Latest Production Branch as of script creation
|
||||||
|
DRIVER_URL="https://us.download.nvidia.com/XFree86/Linux-x86_64/${NVIDIA_VERSION}/NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run"
|
||||||
|
DRIVER_FILE="/tmp/NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run"
|
||||||
|
|
||||||
|
log_info "Downloading NVIDIA driver version ${NVIDIA_VERSION}..."
|
||||||
|
log_info "URL: $DRIVER_URL"
|
||||||
|
|
||||||
|
if curl -fL -o "$DRIVER_FILE" "$DRIVER_URL" 2>&1 | tee /tmp/nvidia-download.log; then
|
||||||
|
log_success "Driver downloaded successfully"
|
||||||
|
chmod +x "$DRIVER_FILE"
|
||||||
|
|
||||||
|
log_info "Running NVIDIA installer (this may take several minutes)..."
|
||||||
|
log_info "The installer will compile kernel modules and configure the driver."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Run NVIDIA installer with appropriate flags
|
||||||
|
if "$DRIVER_FILE" --silent --dkms --no-questions 2>&1 | tee /tmp/nvidia-install.log; then
|
||||||
|
log_success "NVIDIA drivers installed successfully"
|
||||||
|
NVIDIA_INSTALLED=true
|
||||||
|
NVIDIA_REBOOT_NEEDED=true
|
||||||
|
else
|
||||||
|
log_error "NVIDIA driver installation failed"
|
||||||
|
log_info "Installation log saved to: /tmp/nvidia-install.log"
|
||||||
|
log_info "Common issues:"
|
||||||
|
log_info " - Secure Boot may need to be disabled in BIOS"
|
||||||
|
log_info " - You may need to sign the kernel modules for Secure Boot"
|
||||||
|
log_info " - Try running installer manually: sudo $DRIVER_FILE"
|
||||||
|
log_info ""
|
||||||
|
log_info "For latest driver, visit: https://www.nvidia.com/Download/index.aspx"
|
||||||
|
NVIDIA_INSTALLED=false
|
||||||
|
NVIDIA_REBOOT_NEEDED=true # Still need reboot for nouveau blacklist
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup installer
|
||||||
|
rm -f "$DRIVER_FILE"
|
||||||
|
else
|
||||||
|
log_error "Failed to download NVIDIA driver"
|
||||||
|
log_info "Download log saved to: /tmp/nvidia-download.log"
|
||||||
|
log_info "You can download and install manually from:"
|
||||||
|
log_info " https://www.nvidia.com/Download/index.aspx"
|
||||||
|
NVIDIA_INSTALLED=false
|
||||||
|
NVIDIA_REBOOT_NEEDED=false
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Skipping NVIDIA driver installation"
|
||||||
|
log_info "To install later, visit: https://www.nvidia.com/Download/index.aspx"
|
||||||
|
NVIDIA_INSTALLED=false
|
||||||
|
NVIDIA_REBOOT_NEEDED=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if NVIDIA Container Toolkit is installed
|
||||||
|
if [ "$NVIDIA_INSTALLED" = true ]; then
|
||||||
|
if command -v nvidia-container-runtime &> /dev/null; then
|
||||||
|
log_warning "NVIDIA Container Toolkit is already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing NVIDIA Container Toolkit..."
|
||||||
|
|
||||||
|
# Install NVIDIA Container Toolkit (with error handling)
|
||||||
|
{
|
||||||
|
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg && \
|
||||||
|
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
|
||||||
|
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
|
||||||
|
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y nvidia-container-toolkit && \
|
||||||
|
nvidia-ctk runtime configure --runtime=docker && \
|
||||||
|
systemctl restart docker
|
||||||
|
} 2>&1 | tee /tmp/nvidia-toolkit-install.log
|
||||||
|
|
||||||
|
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
||||||
|
log_success "NVIDIA Container Toolkit installed and configured"
|
||||||
|
else
|
||||||
|
log_error "NVIDIA Container Toolkit installation failed"
|
||||||
|
log_info "Installation log saved to: /tmp/nvidia-toolkit-install.log"
|
||||||
|
log_info "Docker will work without GPU support"
|
||||||
|
log_info "You can try installing manually later from:"
|
||||||
|
log_info " https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
log_info "No NVIDIA GPU detected, skipping driver installation"
|
||||||
|
NVIDIA_REBOOT_NEEDED=false
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Final Summary
|
# Final Summary
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
log_success "AI-Homelab setup completed successfully!"
|
log_success "AI-Homelab setup completed successfully!"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
if [ "${NVIDIA_REBOOT_NEEDED:-false}" = true ]; then
|
||||||
|
log_warning "⚠️ REBOOT REQUIRED FOR NVIDIA DRIVERS ⚠️"
|
||||||
|
echo ""
|
||||||
|
log_info "NVIDIA drivers were installed and require a system reboot."
|
||||||
|
log_info "Please reboot before running the deployment script."
|
||||||
|
echo ""
|
||||||
|
echo " After reboot, verify drivers with: nvidia-smi"
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Next steps:"
|
log_info "Next steps:"
|
||||||
echo ""
|
echo ""
|
||||||
echo " 1. Log out and log back in for group changes to take effect"
|
echo " 1. Log out and log back in for group changes to take effect"
|
||||||
echo " (or run: newgrp docker)"
|
echo " (or run: newgrp docker)"
|
||||||
echo ""
|
echo ""
|
||||||
|
if [ "${NVIDIA_REBOOT_NEEDED:-false}" = true ]; then
|
||||||
|
echo " 2. REBOOT YOUR SYSTEM for NVIDIA drivers to load"
|
||||||
|
echo " Run: sudo reboot"
|
||||||
|
echo ""
|
||||||
|
echo " 3. After reboot, navigate to your AI-Homelab repository:"
|
||||||
|
else
|
||||||
echo " 2. Navigate to your AI-Homelab repository:"
|
echo " 2. Navigate to your AI-Homelab repository:"
|
||||||
|
fi
|
||||||
echo " cd ~/AI-Homelab"
|
echo " cd ~/AI-Homelab"
|
||||||
echo ""
|
echo ""
|
||||||
echo " 3. Edit the .env file with your configuration:"
|
if [ "${NVIDIA_REBOOT_NEEDED:-false}" = true ]; then
|
||||||
echo " cp .env.example .env"
|
|
||||||
echo " nano .env"
|
|
||||||
echo ""
|
|
||||||
echo " 4. Run the deployment script:"
|
echo " 4. Run the deployment script:"
|
||||||
|
else
|
||||||
|
echo " 3. Run the deployment script:"
|
||||||
|
fi
|
||||||
echo " ./scripts/deploy-homelab.sh"
|
echo " ./scripts/deploy-homelab.sh"
|
||||||
echo ""
|
echo ""
|
||||||
|
if [ "${NVIDIA_REBOOT_NEEDED:-false}" = true ]; then
|
||||||
echo " 5. Access Dockge at: https://dockge.yourdomain.duckdns.org"
|
echo " 5. Access Dockge at: https://dockge.yourdomain.duckdns.org"
|
||||||
|
else
|
||||||
|
echo " 4. Access Dockge at: https://dockge.yourdomain.duckdns.org"
|
||||||
|
fi
|
||||||
echo " (Use your configured domain and Authelia credentials)"
|
echo " (Use your configured domain and Authelia credentials)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
if lspci | grep -i nvidia > /dev/null && [ "$NVIDIA_INSTALLED" = false ]; then
|
|
||||||
echo ""
|
|
||||||
log_warning "REMINDER: Reboot required for NVIDIA driver changes"
|
|
||||||
echo " Run: sudo reboot"
|
|
||||||
echo "=========================================="
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
log_info "Setup complete! Please log out and log back in."
|
log_info "Setup complete!"
|
||||||
|
if [ "${NVIDIA_REBOOT_NEEDED:-false}" != true ]; then
|
||||||
|
log_info "Please log out and log back in."
|
||||||
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user