Major improvements to environment variable management: 1. Added .env.example files for ALL stacks - Each stack now has its own .env.example with only required variables - Variables include comments explaining their purpose - Examples: core, dockge, infrastructure, dashboards, arcane, media, media-management, transcoders, monitoring, sablier, utilities, productivity, wikis, vpn, homeassistant, alternatives 2. Created .env.global generation - Generates /opt/stacks/.env.global with all variables - Strips comments and blank lines for clean output - Available to all stacks for reference 3. Improved variable replacement strategy - Variable replacement now ONLY targets labels and x-dockge sections in docker-compose files - Configuration files (traefik, authelia) still get full variable replacement - Uses Python script for precise section detection - Preserves environment variables and volume mounts as-is 4. New deployment approach - Each stack copies .env.example to .env - Values populated from main ~/EZ-Homelab/.env file - No more manual sed commands to remove unused variables - Consistent approach across all deployment functions 5. Updated deployment functions - deploy_dockge, deploy_core, deploy_infrastructure, deploy_dashboards, deploy_arcane, deploy_sablier_stack - All now use process_stack_env() for clean .env handling - All use updated localize_yml_file() for targeted variable replacement Benefits: - Clean, minimal .env files for each stack - No unnecessary variables or comments in deployed .env files - Variables in compose files preserved for Docker Compose to handle - Easier to understand what each stack needs - Uniform deployment approach across all stacks
Docker Compose Stacks
This directory contains Docker Compose templates for managing your homelab services. Each stack is organized in its own folder for better organization and maintainability.
Structure
docker-compose/
├── core/ # Core infrastructure (MUST DEPLOY FIRST)
│ ├── docker-compose.yml
│ ├── authelia/ # SSO configuration
│ ├── duckdns/ # DNS configuration
│ └── traefik/ # Reverse proxy configuration
│ └── dynamic/ # External routing YAML files (multi-server)
├── sablier/ # Lazy loading service (per-server)
├── dockge/ # Docker management web UI
├── infrastructure/ # Additional infrastructure (Pi-hole, etc.)
├── dashboards/ # Dashboard services (Homepage, Homarr)
├── media/ # Media services (Plex, Jellyfin, etc.)
├── media-management/ # *arr services (Sonarr, Radarr, etc.)
├── monitoring/ # Observability stack (Prometheus, Grafana, etc.)
├── homeassistant/ # Home Assistant stack
├── productivity/ # Productivity tools (Nextcloud, Gitea, etc.)
├── utilities/ # Utility services (Duplicati, FreshRSS, etc.)
├── wikis/ # Mediawiki, Dokuwiki, Bookstacks
└── vpn/ # VPN services (Gluetun, qBittorrent)
Multi-Server Architecture
EZ-Homelab supports two deployment models:
Single Server:
- Core + all other stacks on one machine
- Simplest setup for beginners
Multi-Server:
- Core Server: DuckDNS, Traefik (multi-provider), Authelia
- Remote Servers: Traefik (local-only), Sablier (local-only), application services
- All services accessed through unified domain
See docs/Ondemand-Remote-Services.md for multi-server setup.
Deployment
Use the unified setup script:
cd ~/EZ-Homelab
./scripts/ez-homelab.sh
Single Server Traefik service labels
services:
myservice:
labels:
# TRAEFIK CONFIGURATION
# ==========================================
# Service metadata
- "com.centurylinklabs.watchtower.enable=true"
- "homelab.category=category-name"
- "homelab.description=Brief service description"
# Traefik labels
- "traefik.enable=true"
# Router configuration
- "traefik.http.routers.myservice.rule=Host(`myservice.${DOMAIN}`)"
- "traefik.http.routers.myservice.entrypoints=websecure"
- "traefik.http.routers.myservice.tls.certresolver=letsencrypt"
- "traefik.http.routers.myservice.middlewares=authelia@docker" # SSO (remove to disable)
# Service configuration
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
# Sablier configuration (lazy loading)
- "sablier.enable=true"
- "sablier.group=${SERVER_HOSTNAME}-myservice"
- "sablier.start-on-demand=true"
Multi-Server Traefik
On Core Server
On Remote Server
Disabling SSO (Media Servers)
Remove or comment the authelia middleware line:
# SSO enabled (default):
- "traefik.http.routers.myservice.middlewares=authelia@docker"
# SSO disabled (for Plex, Jellyfin, etc.):
# - "traefik.http.routers.myservice.middlewares=authelia@docker"
Disabling Lazy Loading (Always-On Services)
Remove Sablier labels and use restart: unless-stopped:
services:
myservice:
restart: unless-stopped # Always running
# No sablier labels
Best Practices
- Pin Versions: Always specify image versions (e.g.,
nginx:1.25.3notnginx:latest) - Use Labels: Add labels for organization and documentation
- Health Checks: Define health checks for critical services
- Resource Limits: Set memory and CPU limits for resource-intensive services
- Logging: Configure log rotation to prevent disk space issues
- Restart Policies: Use
unless-stoppedfor most services - Comments: Document non-obvious configurations
Template
When creating a new service, use this template:
services:
service-name:
image: vendor/image:version
container_name: service-name
restart: unless-stopped
networks:
- homelab-network
- traefik-network
ports:
- "host_port:container_port"
volumes:
- ./config/service-name:/config
- service-data:/data
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:port/health"]
interval: 30s
timeout: 10s
retries: 3
labels:
- "homelab.category=category"
- "homelab.description=Service description"
volumes:
service-data:
driver: local
networks:
homelab-network:
external: true