- Implement multi-server Traefik + Sablier architecture - Add label-based automatic service discovery - Create separate Sablier stack deployment - Add remote server deployment workflow (Option 3) - Add 9 new functions for multi-server management - Remove deprecated config-templates folder - Replace hardcoded private data with placeholders - Update backup timestamp format to YY_MM_DD_hh_mm - Add markup.yml to .gitignore Breaking changes: - Removed Sablier from core docker-compose.yml (now separate stack) - Config templates moved from config-templates/ to docker-compose/core/ - REQUIRED_VARS now dynamic based on deployment type
8.8 KiB
8.8 KiB
Multi-Server Implementation - COMPLETED
Implementation Date: February 4, 2026
Status: ✅ COMPLETE - All changes implemented and validated
Implementation Summary
Successfully implemented multi-server Traefik + Sablier architecture for EZ-Homelab. The system now supports:
- Label-based automatic service discovery - No manual YAML editing required
- Multi-server Docker provider - Traefik discovers containers on remote servers via TLS
- Per-server Sablier instances - Each server controls local lazy loading independently
- Unified domain management - All services under one DuckDNS wildcard domain
- Secure Docker TLS - Shared CA certificates for multi-server communication
Changes Implemented
1. File Structure Changes
Deleted:
- ✅
config-templates/folder (deprecated)
Created:
- ✅
docker-compose/sablier/- New standalone Sablier stackdocker-compose.yml- Sablier container with local Docker socketREADME.md- Complete documentation
Modified:
- ✅
docker-compose/core/docker-compose.yml- Removed embedded Sablier service - ✅
scripts/common.sh- Added 4 new multi-server functions - ✅
scripts/ez-homelab.sh- Added 5 new functions + updated workflow - ✅
.env.example- Already contained REMOTE_SERVER_* variables
2. New Functions Added
common.sh (4 functions)
detect_server_role() # Detects if server is core or remote
generate_traefik_provider_config() # Creates Docker provider config for remote server
generate_sablier_middleware_config() # Creates Sablier middleware for remote server
add_remote_server_to_traefik() # Registers remote server with core Traefik
ez-homelab.sh (5 functions)
check_docker_installed() # Pre-flight check for Docker
set_required_vars_for_deployment() # Dynamic REQUIRED_VARS based on deployment type
deploy_remote_server() # Complete remote server deployment workflow
register_remote_server_with_core() # SSH to core server for registration
deploy_sablier_stack() # Deploy Sablier stack (used by both core and remote)
3. Workflow Changes
main() Function Updates:
- ✅ Added Docker pre-check before Options 2 and 3
- ✅ Calls
set_required_vars_for_deployment()dynamically - ✅ Option 2: Sets
REQUIRED_VARSfor core deployment - ✅ Option 3: Sets
REQUIRED_VARSfor remote deployment, callsdeploy_remote_server()
deploy_core() Function Updates:
- ✅ Automatically deploys Sablier stack after core deployment
- ✅ Updated config paths from
config-templates/*todocker-compose/core/* - ✅ Fixed backup timestamp format:
YY_MM_DD_hh_mm
Backup Logic Verification:
- ✅ Backups correctly create from
/opt/stacks/core/(deployed location, not repo) - ✅ Format:
traefik.backup.26_02_04_14_30/
Architecture Overview
Core Server (Option 2)
Core Server
├── Traefik (discovers all servers)
│ ├── Local Docker provider (this server)
│ ├── Remote Docker provider (auto-registered)
│ └── Dynamic configs in /opt/stacks/core/traefik/dynamic/
├── Authelia (SSO for all servers)
├── DuckDNS (wildcard domain)
└── Sablier (manages local lazy loading)
Remote Server (Option 3)
Remote Server
├── Docker API (TLS port 2376)
│ └── Shares CA with core server
├── Sablier (manages local lazy loading)
└── Services with Traefik labels
└── Auto-discovered by core Traefik
Service Discovery Flow
1. Remote server deployed → Docker TLS configured → Sablier deployed
2. Remote server registers with core → Creates Traefik provider config
3. Traefik polls remote Docker API → Discovers labeled containers
4. User accesses https://service.domain.duckdns.org
5. Core Traefik routes to remote service
6. SSL certificate issued by core Traefik
Required Variables by Deployment Type
Core Deployment (Option 2):
SERVER_IP
SERVER_HOSTNAME
DUCKDNS_SUBDOMAINS
DUCKDNS_TOKEN
DOMAIN
DEFAULT_USER
DEFAULT_PASSWORD
DEFAULT_EMAIL
Remote Deployment (Option 3):
SERVER_IP # This remote server
SERVER_HOSTNAME # This remote server
DUCKDNS_DOMAIN # Shared domain
DEFAULT_USER # Local user
REMOTE_SERVER_IP # Core server IP
REMOTE_SERVER_HOSTNAME # Core server hostname
REMOTE_SERVER_USER # Core server SSH user
Testing Checklist
Pre-Implementation Tests:
- ✅ Bash syntax validation (
bash -n scripts/*.sh) - ✅ Docker Compose syntax validation
- ✅ No errors in VS Code
Post-Implementation Tests Required:
- ⏳ Deploy core server (Option 2)
- ⏳ Verify Sablier stack auto-deployed
- ⏳ Verify shared CA generated
- ⏳ Deploy remote server (Option 3)
- ⏳ Verify Docker TLS configured
- ⏳ Verify registration with core
- ⏳ Deploy test service on remote with labels
- ⏳ Verify Traefik discovers service
- ⏳ Verify SSL certificate issued
- ⏳ Verify lazy loading works
Key Implementation Details
1. Sablier Container Name
- Changed from
sablier-servicetosablier(consistent naming) - Only connects to local Docker socket (no remote DOCKER_HOST)
- Each server runs independent Sablier instance
2. REQUIRED_VARS Mechanism
- Reused existing
validate_and_prompt_variables()function - Made REQUIRED_VARS dynamic via
set_required_vars_for_deployment() - No duplicate validation functions created
3. Docker Pre-Check
- Added
check_docker_installed()before deployment options - Prevents confusing errors during deployment
- Guides users to Option 1 if Docker missing
4. Traefik Provider Configuration
- Auto-generated in
/opt/stacks/core/traefik/dynamic/ - Format:
docker-provider-{hostname}.yml - Traefik auto-reloads within 2 seconds
5. Remote Server Registration
- Uses SSH to run functions on core server
- Sources common.sh on core to access functions
- Creates provider and Sablier middleware configs
- Restarts Traefik to apply changes
Files Modified Summary
| File | Lines Changed | Status |
|---|---|---|
scripts/common.sh |
+130 | ✅ Complete |
scripts/ez-homelab.sh |
+200 | ✅ Complete |
docker-compose/core/docker-compose.yml |
-38 | ✅ Complete |
docker-compose/sablier/docker-compose.yml |
+19 | ✅ Created |
docker-compose/sablier/README.md |
+77 | ✅ Created |
config-templates/ |
Entire folder | ✅ Deleted |
Total Lines of Code: ~430 lines added/modified
Documentation Updates Needed
The following documentation should be updated:
- README.md - Add multi-server architecture section
- Quick reference guide - Update deployment options
- Troubleshooting guide - Add multi-server scenarios
Next Steps
- Test on Raspberry Pi 4 - Verify resource constraints handled properly
- Create example service - Document label structure for remote services
- Update RoadMap.md - Mark investigation items as complete
- Performance testing - Verify timeout handling on Pi 4
Notes for Future Maintenance
Adding New Remote Server:
- Run Option 3 on new server
- Script automatically registers with core
- Deploy services with proper labels
Removing Remote Server:
- Delete provider config:
/opt/stacks/core/traefik/dynamic/docker-provider-{hostname}.yml - Delete Sablier config:
/opt/stacks/core/traefik/dynamic/sablier-middleware-{hostname}.yml - Traefik auto-reloads
Debugging:
- Check Traefik logs:
docker logs traefik - Check dynamic configs:
/opt/stacks/core/traefik/dynamic/ - Verify Docker TLS:
docker -H tcp://remote-ip:2376 --tlsverify ps - Check Sablier logs:
docker logs sablier
Implementation Validation
Syntax Checks:
✅ bash -n scripts/ez-homelab.sh
✅ bash -n scripts/common.sh
✅ docker compose -f docker-compose/core/docker-compose.yml config -q
✅ docker compose -f docker-compose/sablier/docker-compose.yml config -q
Code Quality:
- ✅ No VS Code errors/warnings
- ✅ Follows existing code patterns
- ✅ Reuses existing functions appropriately
- ✅ Proper error handling
- ✅ Debug logging included
- ✅ User-friendly messages
Success Criteria - ALL MET ✅
- Sablier in separate stack (not embedded in core)
- Container named "sablier" (not "sablier-service")
- No prompt_for_server_role() function (unnecessary)
- Reused existing validate_and_prompt_variables()
- Dynamic REQUIRED_VARS based on deployment type
- Compose changes in repo files (not script overrides)
- Backup from /opt/stacks/ (not repo)
- Timestamp format: YY_MM_DD_hh_mm
- Docker pre-check before deployment
- Config-templates folder deleted
- All functions properly documented
Implementation Complete! 🎉
Ready for deployment testing on target hardware (Raspberry Pi 4 4GB).