feat: Complete EZ-Homelab deployment system overhaul
- Add unified ez-homelab.sh script with guided menu interface - Create dedicated Dockge stack in /opt/dockge for clean isolation - Move dockerproxy from core to infrastructure stack - Fix Authelia configuration with proper variable placeholders - Update all compose files to use variables - Enhance script with comprehensive variable replacement - Fix sed delimiter conflicts and middleware issues - Add proper step numbering and error handling - Prepare all stacks for Dockge management - Update README with new deployment instructions
This commit is contained in:
17
README.md
17
README.md
@@ -26,17 +26,16 @@ Plus Dockge for visual management of containers, and Homepage dashboard to easil
|
|||||||
git clone https://github.com/kelinfoxy/EZ-Homelab.git
|
git clone https://github.com/kelinfoxy/EZ-Homelab.git
|
||||||
cd EZ-Homelab
|
cd EZ-Homelab
|
||||||
|
|
||||||
# Configure environment
|
# Run the unified setup script (guided installation)
|
||||||
cp .env.example .env
|
./scripts/ez-homelab.sh
|
||||||
nano .env # Add your domain and tokens
|
|
||||||
|
|
||||||
# Run setup script (installs Docker, generates secrets)
|
|
||||||
sudo ./scripts/setup-homelab.sh
|
|
||||||
|
|
||||||
# Deploy all services
|
|
||||||
sudo ./scripts/deploy-homelab.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**What the script does:**
|
||||||
|
- Installs Docker and required system packages
|
||||||
|
- Guides you through configuration (domain, admin credentials, etc.)
|
||||||
|
- Deploys selected services based on your needs
|
||||||
|
- Sets up all stacks for Dockge management
|
||||||
|
|
||||||
**Access your homelab:**
|
**Access your homelab:**
|
||||||
- **Dockge**: `https://dockge.yourdomain.duckdns.org` (primary management interface)
|
- **Dockge**: `https://dockge.yourdomain.duckdns.org` (primary management interface)
|
||||||
- **Homepage**: `https://homepage.yourdomain.duckdns.org` (service dashboard)
|
- **Homepage**: `https://homepage.yourdomain.duckdns.org` (service dashboard)
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ theme: dark
|
|||||||
|
|
||||||
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
||||||
|
|
||||||
default_redirection_url: https://auth.your-domain.duckdns.org
|
default_redirection_url: https://auth.${DOMAIN}
|
||||||
|
|
||||||
totp:
|
totp:
|
||||||
issuer: your-domain.duckdns.org
|
issuer: ${DOMAIN}
|
||||||
period: 30
|
period: 30
|
||||||
skew: 1
|
skew: 1
|
||||||
|
|
||||||
@@ -36,25 +36,25 @@ access_control:
|
|||||||
|
|
||||||
rules:
|
rules:
|
||||||
# Bypass Authelia for Jellyfin (allow app access)
|
# Bypass Authelia for Jellyfin (allow app access)
|
||||||
- domain: jellyfin.your-domain.duckdns.org
|
- domain: jellyfin.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for Plex (allow app access)
|
# Bypass for Plex (allow app access)
|
||||||
- domain: plex.your-domain.duckdns.org
|
- domain: plex.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for Home Assistant (has its own auth)
|
# Bypass for Home Assistant (has its own auth)
|
||||||
- domain: ha.your-domain.duckdns.org
|
- domain: ha.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Protected: All other services require authentication
|
# Protected: All other services require authentication
|
||||||
- domain: "*.your-domain.duckdns.org"
|
- domain: "*.${DOMAIN}"
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Two-factor for admin services (optional)
|
# Two-factor for admin services (optional)
|
||||||
# - domain:
|
# - domain:
|
||||||
# - "admin.your-domain.duckdns.org"
|
# - "admin.${DOMAIN}"
|
||||||
# - "portainer.your-domain.duckdns.org"
|
# - "portainer.${DOMAIN}"
|
||||||
# policy: two_factor
|
# policy: two_factor
|
||||||
|
|
||||||
session:
|
session:
|
||||||
@@ -63,7 +63,12 @@ session:
|
|||||||
expiration: 24h # Session expires after 24 hours
|
expiration: 24h # Session expires after 24 hours
|
||||||
inactivity: 24h # Session expires after 24 hours of inactivity
|
inactivity: 24h # Session expires after 24 hours of inactivity
|
||||||
remember_me_duration: 1M
|
remember_me_duration: 1M
|
||||||
domain: your-domain.duckdns.org
|
domain: ${DOMAIN}
|
||||||
|
cookies:
|
||||||
|
- name: authelia_session
|
||||||
|
domain: ${DOMAIN}
|
||||||
|
secure: true
|
||||||
|
same_site: lax
|
||||||
|
|
||||||
regulation:
|
regulation:
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Authelia Configuration
|
# Authelia Configuration
|
||||||
# Copy to /opt/stacks/authelia/configuration.yml
|
# Copy to /opt/stacks/authelia/configuration.yml
|
||||||
# IMPORTANT: Replace 'kelin-casa.duckdns.org' with your actual DuckDNS domain
|
# IMPORTANT: Replace '${DOMAIN}' with your actual DuckDNS domain
|
||||||
|
|
||||||
server:
|
server:
|
||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
@@ -13,10 +13,10 @@ theme: dark
|
|||||||
|
|
||||||
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
||||||
|
|
||||||
default_redirection_url: https://auth.kelin-casa.duckdns.org
|
default_redirection_url: https://auth.${DOMAIN}
|
||||||
|
|
||||||
totp:
|
totp:
|
||||||
issuer: kelin-casa.duckdns.org
|
issuer: ${DOMAIN}
|
||||||
period: 30
|
period: 30
|
||||||
skew: 1
|
skew: 1
|
||||||
|
|
||||||
@@ -36,31 +36,31 @@ access_control:
|
|||||||
|
|
||||||
rules:
|
rules:
|
||||||
# Bypass Authelia for Jellyfin (allow app access)
|
# Bypass Authelia for Jellyfin (allow app access)
|
||||||
- domain: jellyfin.kelin-casa.duckdns.org
|
- domain: jellyfin.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for Plex (allow app access)
|
# Bypass for Plex (allow app access)
|
||||||
- domain: plex.kelin-casa.duckdns.org
|
- domain: plex.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for Home Assistant (has its own auth)
|
# Bypass for Home Assistant (has its own auth)
|
||||||
- domain: ha.kelin-casa.duckdns.org
|
- domain: ha.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Bypass for development services (they have their own auth or setup)
|
# Bypass for development services (they have their own auth or setup)
|
||||||
- domain: pgadmin.kelin-casa.duckdns.org
|
- domain: pgadmin.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
- domain: gitlab.kelin-casa.duckdns.org
|
- domain: gitlab.${DOMAIN}
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Protected: All other services require authentication
|
# Protected: All other services require authentication
|
||||||
- domain: "*.kelin-casa.duckdns.org"
|
- domain: "*.${DOMAIN}"
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Two-factor for admin services (optional)
|
# Two-factor for admin services (optional)
|
||||||
# - domain:
|
# - domain:
|
||||||
# - "admin.kelin-casa.duckdns.org"
|
# - "admin.${DOMAIN}"
|
||||||
# - "portainer.kelin-casa.duckdns.org"
|
# - "portainer.${DOMAIN}"
|
||||||
# policy: two_factor
|
# policy: two_factor
|
||||||
|
|
||||||
session:
|
session:
|
||||||
@@ -69,7 +69,7 @@ session:
|
|||||||
expiration: 24h # Session expires after 24 hours
|
expiration: 24h # Session expires after 24 hours
|
||||||
inactivity: 24h # Session expires after 24 hours of inactivity
|
inactivity: 24h # Session expires after 24 hours of inactivity
|
||||||
remember_me_duration: 1M
|
remember_me_duration: 1M
|
||||||
domain: kelin-casa.duckdns.org
|
domain: ${DOMAIN}
|
||||||
|
|
||||||
regulation:
|
regulation:
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
|
|||||||
@@ -73,25 +73,6 @@ services:
|
|||||||
- traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true
|
- traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true
|
||||||
- x-dockge.url=https://auth.${DOMAIN}
|
- x-dockge.url=https://auth.${DOMAIN}
|
||||||
|
|
||||||
dockerproxy:
|
|
||||||
image: tecnativa/docker-socket-proxy:latest
|
|
||||||
container_name: dockerproxy
|
|
||||||
privileged: true
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 2375:2375
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
environment:
|
|
||||||
- CONTAINERS=1
|
|
||||||
- SERVICES=1
|
|
||||||
- TASKS=1
|
|
||||||
- NETWORKS=1
|
|
||||||
- NODES=1
|
|
||||||
labels:
|
|
||||||
- homelab.category=infrastructure
|
|
||||||
- homelab.description=Docker socket proxy for security
|
|
||||||
|
|
||||||
# Sablier - Lazy loading service for Docker containers
|
# Sablier - Lazy loading service for Docker containers
|
||||||
sablier-service:
|
sablier-service:
|
||||||
image: sablierapp/sablier:latest
|
image: sablierapp/sablier:latest
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- homelab-network
|
- homelab-network
|
||||||
- traefik-network
|
- traefik-network
|
||||||
- dockerproxy-network
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./homepage:/app/config
|
- ./homepage:/app/config
|
||||||
- /var/run/docker.sock:/var/run/docker.sock # For Docker integration do not mount RO
|
- /var/run/docker.sock:/var/run/docker.sock # For Docker integration do not mount RO
|
||||||
@@ -86,5 +85,3 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
traefik-network:
|
traefik-network:
|
||||||
external: true
|
external: true
|
||||||
dockerproxy-network:
|
|
||||||
external: true
|
|
||||||
|
|||||||
51
docker-compose/dockge/docker-compose.yml
Normal file
51
docker-compose/dockge/docker-compose.yml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Dockge Stack
|
||||||
|
# Docker Compose Stack Manager
|
||||||
|
# Place in /opt/dockge/docker-compose.yml
|
||||||
|
|
||||||
|
# Service Access URLs:
|
||||||
|
# - Dockge: https://dockge.${DOMAIN}
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Dockge - Docker Compose Stack Manager (PRIMARY - preferred over Portainer)
|
||||||
|
# Access at: https://dockge.${DOMAIN}
|
||||||
|
dockge:
|
||||||
|
image: louislam/dockge:1
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.50'
|
||||||
|
memory: 256M
|
||||||
|
pids: 512
|
||||||
|
reservations:
|
||||||
|
cpus: '0.25'
|
||||||
|
memory: 128M
|
||||||
|
container_name: dockge
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- homelab-network
|
||||||
|
- traefik-network
|
||||||
|
ports:
|
||||||
|
- "5001:5001" # Optional: direct access
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /opt/stacks:/opt/stacks # Dockge manages stacks in this directory
|
||||||
|
- ./data:/app/data
|
||||||
|
environment:
|
||||||
|
- DOCKGE_STACKS_DIR=/opt/stacks
|
||||||
|
- DOCKGE_ENABLE_CONSOLE=true
|
||||||
|
labels:
|
||||||
|
- "homelab.category=infrastructure"
|
||||||
|
- "homelab.description=Docker Compose stack manager (PRIMARY)"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.dockge.rule=Host(`dockge.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.dockge.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.dockge.tls=true"
|
||||||
|
- "traefik.http.routers.dockge.middlewares=authelia@docker"
|
||||||
|
- "traefik.http.services.dockge.loadbalancer.server.port=5001"
|
||||||
|
- "x-dockge.url=https://dockge.${DOMAIN}"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
homelab-network:
|
||||||
|
external: true
|
||||||
|
traefik-network:
|
||||||
|
external: true
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
# See /opt/stacks/traefik/, /opt/stacks/authelia/, etc.
|
# See /opt/stacks/traefik/, /opt/stacks/authelia/, etc.
|
||||||
|
|
||||||
# Service Access URLs:
|
# Service Access URLs:
|
||||||
# - Dockge: https://dockge.${DOMAIN}
|
|
||||||
# - Portainer: https://portainer.${DOMAIN}
|
# - Portainer: https://portainer.${DOMAIN}
|
||||||
# - Pi-hole: https://pihole.${DOMAIN}
|
# - Pi-hole: https://pihole.${DOMAIN}
|
||||||
# - Dozzle: https://dozzle.${DOMAIN}
|
# - Dozzle: https://dozzle.${DOMAIN}
|
||||||
@@ -13,43 +12,24 @@
|
|||||||
# - Netdata: https://netdata.${DOMAIN}
|
# - Netdata: https://netdata.${DOMAIN}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Dockge - Docker Compose Stack Manager (PRIMARY - preferred over Portainer)
|
dockerproxy:
|
||||||
# Access at: https://dockge.${DOMAIN}
|
image: tecnativa/docker-socket-proxy:latest
|
||||||
dockge:
|
container_name: dockerproxy
|
||||||
image: louislam/dockge:1
|
privileged: true
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.50'
|
|
||||||
memory: 256M
|
|
||||||
pids: 512
|
|
||||||
reservations:
|
|
||||||
cpus: '0.25'
|
|
||||||
memory: 128M
|
|
||||||
container_name: dockge
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
|
||||||
- homelab-network
|
|
||||||
- traefik-network
|
|
||||||
ports:
|
ports:
|
||||||
- "5001:5001" # Optional: direct access
|
- 2375:2375
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- /opt/stacks:/opt/stacks # Dockge manages stacks in this directory
|
|
||||||
- /opt/dockge/data:/app/data
|
|
||||||
environment:
|
environment:
|
||||||
- DOCKGE_STACKS_DIR=/opt/stacks
|
- CONTAINERS=1
|
||||||
- DOCKGE_ENABLE_CONSOLE=true
|
- SERVICES=1
|
||||||
|
- TASKS=1
|
||||||
|
- NETWORKS=1
|
||||||
|
- NODES=1
|
||||||
labels:
|
labels:
|
||||||
- "homelab.category=infrastructure"
|
- homelab.category=infrastructure
|
||||||
- "homelab.description=Docker Compose stack manager (PRIMARY)"
|
- homelab.description=Docker socket proxy for security
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.dockge.rule=Host(`dockge.${DOMAIN}`)"
|
|
||||||
- "traefik.http.routers.dockge.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.dockge.tls=true"
|
|
||||||
- "traefik.http.routers.dockge.middlewares=authelia@docker"
|
|
||||||
- "traefik.http.services.dockge.loadbalancer.server.port=5001"
|
|
||||||
- "x-dockge.url=https://dockge.${DOMAIN}"
|
|
||||||
|
|
||||||
# Pi-hole - Network-wide ad blocker and DNS server
|
# Pi-hole - Network-wide ad blocker and DNS server
|
||||||
# Access at: https://pihole.${DOMAIN}
|
# Access at: https://pihole.${DOMAIN}
|
||||||
@@ -160,28 +140,6 @@ services:
|
|||||||
- "traefik.http.routers.dozzle.middlewares=authelia@docker"
|
- "traefik.http.routers.dozzle.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.dozzle.loadbalancer.server.port=8080"
|
- "traefik.http.services.dozzle.loadbalancer.server.port=8080"
|
||||||
|
|
||||||
# Docker Proxy - Socket proxy for security
|
|
||||||
# Used by services that need Docker socket access
|
|
||||||
dockerproxy:
|
|
||||||
image: tecnativa/docker-socket-proxy:latest
|
|
||||||
container_name: dockerproxy
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- dockerproxy-network
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:2375:2375"
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
environment:
|
|
||||||
- CONTAINERS=1
|
|
||||||
- SERVICES=1
|
|
||||||
- TASKS=1
|
|
||||||
- NETWORKS=1
|
|
||||||
- NODES=1
|
|
||||||
labels:
|
|
||||||
- "homelab.category=infrastructure"
|
|
||||||
- "homelab.description=Docker socket proxy for security"
|
|
||||||
|
|
||||||
# Glances - System monitoring
|
# Glances - System monitoring
|
||||||
# Access at: https://glances.${DOMAIN}
|
# Access at: https://glances.${DOMAIN}
|
||||||
glances:
|
glances:
|
||||||
@@ -259,5 +217,3 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
traefik-network:
|
traefik-network:
|
||||||
external: true
|
external: true
|
||||||
dockerproxy-network:
|
|
||||||
external: true
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# AI-Homelab Deployment Script
|
# EZ-Homelab Deployment Script
|
||||||
# This script deploys the core infrastructure and Dockge
|
# This script deploys homelab services with flexible options
|
||||||
# Run after: 1) setup-homelab.sh and 2) editing .env file
|
# Run after: 1) setup-homelab.sh and 2) editing .env file
|
||||||
# Run as: ./deploy-homelab.sh
|
# Run as: ./deploy-homelab.sh
|
||||||
|
|
||||||
@@ -37,11 +37,11 @@ if [ "$EUID" -eq 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get script directory (AI-Homelab/scripts)
|
# Get script directory (EZ-Homelab/scripts)
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
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 "EZ-Homelab Deployment Script"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check if .env file exists
|
# Check if .env file exists
|
||||||
@@ -82,8 +82,105 @@ fi
|
|||||||
log_info "Using domain: $DOMAIN"
|
log_info "Using domain: $DOMAIN"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Deployment options menu
|
||||||
|
echo "=========================================="
|
||||||
|
echo " EZ-HOMELAB DEPLOYMENT OPTIONS"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Choose your deployment scenario:"
|
||||||
|
echo ""
|
||||||
|
echo "1) Full deployment (recommended for new servers)"
|
||||||
|
echo " - Deploy core infrastructure (Traefik, Authelia, etc.)"
|
||||||
|
echo " - Deploy infrastructure stack (Dockge, Pi-hole, etc.)"
|
||||||
|
echo " - Deploy dashboards (Homepage, Homarr)"
|
||||||
|
echo " - Setup all remaining stacks for Dockge"
|
||||||
|
echo ""
|
||||||
|
echo "2) Skip core stack (for existing homelab servers)"
|
||||||
|
echo " - Skip core infrastructure deployment"
|
||||||
|
echo " - Deploy infrastructure stack (Dockge, Pi-hole, etc.)"
|
||||||
|
echo " - Deploy dashboards (Homepage, Homarr)"
|
||||||
|
echo " - Setup all remaining stacks for Dockge"
|
||||||
|
echo ""
|
||||||
|
echo "3) Setup stacks only (no deployment)"
|
||||||
|
echo " - Setup all stacks in Dockge without deploying any"
|
||||||
|
echo " - Useful for preparing stacks for manual deployment"
|
||||||
|
echo ""
|
||||||
|
read -p "Enter your choice (1-3): " DEPLOY_CHOICE
|
||||||
|
|
||||||
|
case $DEPLOY_CHOICE in
|
||||||
|
1)
|
||||||
|
DEPLOY_CORE=true
|
||||||
|
DEPLOY_INFRASTRUCTURE=true
|
||||||
|
DEPLOY_DASHBOARDS=true
|
||||||
|
SETUP_STACKS=true
|
||||||
|
log_info "Selected: Full deployment"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
DEPLOY_CORE=false
|
||||||
|
DEPLOY_INFRASTRUCTURE=true
|
||||||
|
DEPLOY_DASHBOARDS=true
|
||||||
|
SETUP_STACKS=true
|
||||||
|
log_info "Selected: Skip core stack"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
DEPLOY_CORE=false
|
||||||
|
DEPLOY_INFRASTRUCTURE=false
|
||||||
|
DEPLOY_DASHBOARDS=false
|
||||||
|
SETUP_STACKS=true
|
||||||
|
log_info "Selected: Setup stacks only"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Invalid choice. Please run the script again."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Function to setup stacks without deploying them
|
||||||
|
setup_stacks_for_dockge() {
|
||||||
|
log_info "Setting up all stacks for Dockge..."
|
||||||
|
|
||||||
|
# List of stacks to setup
|
||||||
|
STACKS=("vpn" "media" "media-management" "monitoring" "productivity" "utilities" "alternatives" "homeassistant" "nextcloud")
|
||||||
|
|
||||||
|
for stack in "${STACKS[@]}"; do
|
||||||
|
STACK_DIR="/opt/stacks/$stack"
|
||||||
|
REPO_STACK_DIR="$REPO_DIR/docker-compose/$stack"
|
||||||
|
|
||||||
|
if [ -d "$REPO_STACK_DIR" ]; then
|
||||||
|
log_info "Setting up $stack stack..."
|
||||||
|
|
||||||
|
# Create stack directory
|
||||||
|
mkdir -p "$STACK_DIR"
|
||||||
|
|
||||||
|
# Copy docker-compose.yml
|
||||||
|
if [ -f "$REPO_STACK_DIR/docker-compose.yml" ]; then
|
||||||
|
cp "$REPO_STACK_DIR/docker-compose.yml" "$STACK_DIR/"
|
||||||
|
cp "$REPO_DIR/.env" "$STACK_DIR/.env"
|
||||||
|
|
||||||
|
# Copy any additional config directories
|
||||||
|
for config_dir in "$REPO_STACK_DIR"/*/; do
|
||||||
|
if [ -d "$config_dir" ] && [ "$(basename "$config_dir")" != "." ]; then
|
||||||
|
cp -r "$config_dir" "$STACK_DIR/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_success "$stack stack prepared for Dockge"
|
||||||
|
else
|
||||||
|
log_warning "$stack stack docker-compose.yml not found, skipping..."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warning "$stack stack directory not found in repo, skipping..."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_success "All stacks prepared for Dockge deployment"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
# Step 1: Create required directories
|
# Step 1: Create required directories
|
||||||
log_info "Step 1/5: Creating required directories..."
|
log_info "Step 1: Creating required directories..."
|
||||||
mkdir -p /opt/stacks/core
|
mkdir -p /opt/stacks/core
|
||||||
mkdir -p /opt/stacks/infrastructure
|
mkdir -p /opt/stacks/infrastructure
|
||||||
mkdir -p /opt/dockge/data
|
mkdir -p /opt/dockge/data
|
||||||
@@ -91,130 +188,145 @@ log_success "Directories created"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Step 2: Create Docker networks (if they don't exist)
|
# Step 2: Create Docker networks (if they don't exist)
|
||||||
log_info "Step 2/5: Creating Docker networks..."
|
log_info "Step 2: Creating Docker networks..."
|
||||||
docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists"
|
docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists"
|
||||||
docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists"
|
docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists"
|
||||||
docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists"
|
docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Step 3: Deploy core infrastructure (DuckDNS, Traefik, Authelia, Gluetun)
|
# Step 3: Deploy core infrastructure (DuckDNS, Traefik, Authelia, Gluetun)
|
||||||
log_info "Step 3/5: Deploying core infrastructure stack..."
|
if [ "$DEPLOY_CORE" = true ]; then
|
||||||
log_info " - DuckDNS (Dynamic DNS)"
|
log_info "Step 3: Deploying core infrastructure stack..."
|
||||||
log_info " - Traefik (Reverse Proxy with SSL)"
|
log_info " - DuckDNS (Dynamic DNS)"
|
||||||
log_info " - Authelia (Single Sign-On)"
|
log_info " - Traefik (Reverse Proxy with SSL)"
|
||||||
log_info " - Gluetun (VPN Client)"
|
log_info " - Authelia (Single Sign-On)"
|
||||||
echo ""
|
log_info " - Gluetun (VPN Client)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Copy core stack files with overwrite checks
|
# Copy core stack files with overwrite checks
|
||||||
if [ -f "/opt/stacks/core/docker-compose.yml" ]; then
|
if [ -f "/opt/stacks/core/docker-compose.yml" ]; then
|
||||||
log_warning "docker-compose.yml already exists in /opt/stacks/core/"
|
log_warning "docker-compose.yml already exists in /opt/stacks/core/"
|
||||||
log_info "Creating backup: docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)"
|
log_info "Creating backup: docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
cp /opt/stacks/core/docker-compose.yml /opt/stacks/core/docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)
|
cp /opt/stacks/core/docker-compose.yml /opt/stacks/core/docker-compose.yml.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
fi
|
fi
|
||||||
cp "$REPO_DIR/docker-compose/core/docker-compose.yml" /opt/stacks/core/docker-compose.yml
|
cp "$REPO_DIR/docker-compose/core/docker-compose.yml" /opt/stacks/core/docker-compose.yml
|
||||||
|
|
||||||
if [ -d "/opt/stacks/core/traefik" ]; then
|
if [ -d "/opt/stacks/core/traefik" ]; then
|
||||||
log_warning "Traefik configuration already exists in /opt/stacks/core/"
|
log_warning "Traefik configuration already exists in /opt/stacks/core/"
|
||||||
log_info "Creating backup: traefik.backup.$(date +%Y%m%d_%H%M%S)"
|
log_info "Creating backup: traefik.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
mv /opt/stacks/core/traefik /opt/stacks/core/traefik.backup.$(date +%Y%m%d_%H%M%S)
|
mv /opt/stacks/core/traefik /opt/stacks/core/traefik.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
fi
|
fi
|
||||||
cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/
|
cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/
|
||||||
|
|
||||||
if [ -d "/opt/stacks/core/authelia" ]; then
|
if [ -d "/opt/stacks/core/authelia" ]; then
|
||||||
log_warning "Authelia configuration already exists in /opt/stacks/core/"
|
log_warning "Authelia configuration already exists in /opt/stacks/core/"
|
||||||
log_info "Creating backup: authelia.backup.$(date +%Y%m%d_%H%M%S)"
|
log_info "Creating backup: authelia.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
mv /opt/stacks/core/authelia /opt/stacks/core/authelia.backup.$(date +%Y%m%d_%H%M%S)
|
mv /opt/stacks/core/authelia /opt/stacks/core/authelia.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
fi
|
fi
|
||||||
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
||||||
|
|
||||||
# Replace domain placeholders in Authelia config
|
# Replace domain placeholders in Authelia config
|
||||||
sed -i "s/your-domain.duckdns.org/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml
|
sed -i "s/your-domain.duckdns.org/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
|
||||||
if [ -f "/opt/stacks/core/.env" ]; then
|
if [ -f "/opt/stacks/core/.env" ]; then
|
||||||
log_warning ".env already exists in /opt/stacks/core/"
|
log_warning ".env already exists in /opt/stacks/core/"
|
||||||
log_info "Creating backup: .env.backup.$(date +%Y%m%d_%H%M%S)"
|
log_info "Creating backup: .env.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
cp /opt/stacks/core/.env /opt/stacks/core/.env.backup.$(date +%Y%m%d_%H%M%S)
|
cp /opt/stacks/core/.env /opt/stacks/core/.env.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
fi
|
fi
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
||||||
|
|
||||||
# Replace secret placeholders in Authelia config
|
# Replace secret placeholders in Authelia config
|
||||||
source /opt/stacks/core/.env
|
source /opt/stacks/core/.env
|
||||||
sed -i "s|\${AUTHELIA_JWT_SECRET}|${AUTHELIA_JWT_SECRET}|g" /opt/stacks/core/authelia/configuration.yml
|
sed -i "s|\${AUTHELIA_JWT_SECRET}|${AUTHELIA_JWT_SECRET}|g" /opt/stacks/core/authelia/configuration.yml
|
||||||
sed -i "s|\${AUTHELIA_SESSION_SECRET}|${AUTHELIA_SESSION_SECRET}|g" /opt/stacks/core/authelia/configuration.yml
|
sed -i "s|\${AUTHELIA_SESSION_SECRET}|${AUTHELIA_SESSION_SECRET}|g" /opt/stacks/core/authelia/configuration.yml
|
||||||
sed -i "s|\${AUTHELIA_STORAGE_ENCRYPTION_KEY}|${AUTHELIA_STORAGE_ENCRYPTION_KEY}|g" /opt/stacks/core/authelia/configuration.yml
|
sed -i "s|\${AUTHELIA_STORAGE_ENCRYPTION_KEY}|${AUTHELIA_STORAGE_ENCRYPTION_KEY}|g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
|
||||||
# Replace placeholders in Authelia users database
|
# Replace placeholders in Authelia users database
|
||||||
sed -i "s/admin/${AUTHELIA_ADMIN_USER}/g" /opt/stacks/core/authelia/users_database.yml
|
sed -i "s/admin/${AUTHELIA_ADMIN_USER}/g" /opt/stacks/core/authelia/users_database.yml
|
||||||
sed -i "s/admin@example.com/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/authelia/users_database.yml
|
sed -i "s/admin@example.com/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/authelia/users_database.yml
|
||||||
sed -i "s|\$argon2id\$v=19\$m=65536,t=3,p=4\$CHANGEME|${AUTHELIA_ADMIN_PASSWORD}|g" /opt/stacks/core/authelia/users_database.yml
|
sed -i "s|\$argon2id\$v=19\$m=65536,t=3,p=4\$CHANGEME|${AUTHELIA_ADMIN_PASSWORD}|g" /opt/stacks/core/authelia/users_database.yml
|
||||||
|
|
||||||
# Deploy core stack
|
# Deploy core stack
|
||||||
cd /opt/stacks/core
|
cd /opt/stacks/core
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
log_success "Core infrastructure deployed"
|
log_success "Core infrastructure deployed"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Wait for Traefik to be ready
|
# Wait for Traefik to be ready
|
||||||
log_info "Waiting for Traefik to initialize..."
|
log_info "Waiting for Traefik to initialize..."
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
# Check if Traefik is healthy
|
# Check if Traefik is healthy
|
||||||
if docker ps | grep -q "traefik.*Up"; then
|
if docker ps | grep -q "traefik.*Up"; then
|
||||||
log_success "Traefik is running"
|
log_success "Traefik is running"
|
||||||
else
|
else
|
||||||
log_warning "Traefik container check inconclusive, continuing..."
|
log_warning "Traefik container check inconclusive, continuing..."
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
log_info "Skipping core infrastructure deployment"
|
||||||
|
echo ""
|
||||||
fi
|
fi
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Step 4: Deploy infrastructure stack (Dockge and monitoring tools)
|
# Step 4: Deploy infrastructure stack (Dockge and monitoring tools)
|
||||||
log_info "Step 4/6: Deploying infrastructure stack..."
|
if [ "$DEPLOY_INFRASTRUCTURE" = true ]; then
|
||||||
log_info " - Dockge (Docker Compose Manager)"
|
log_info "Step 4: Deploying infrastructure stack..."
|
||||||
log_info " - Pi-hole (DNS Ad Blocker)"
|
log_info " - Dockge (Docker Compose Manager)"
|
||||||
log_info " - Watchtower (Container Updates)"
|
log_info " - Pi-hole (DNS Ad Blocker)"
|
||||||
log_info " - Dozzle (Log Viewer)"
|
log_info " - Watchtower (Container Updates)"
|
||||||
log_info " - Glances (System Monitor)"
|
log_info " - Dozzle (Log Viewer)"
|
||||||
log_info " - Docker Proxy (Security)"
|
log_info " - Glances (System Monitor)"
|
||||||
echo ""
|
log_info " - Docker Proxy (Security)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Copy infrastructure stack
|
# Copy infrastructure stack
|
||||||
cp "$REPO_DIR/docker-compose/infrastructure/docker-compose.yml" /opt/stacks/infrastructure/docker-compose.yml
|
cp "$REPO_DIR/docker-compose/infrastructure/docker-compose.yml" /opt/stacks/infrastructure/docker-compose.yml
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env
|
cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env
|
||||||
|
|
||||||
# Deploy infrastructure stack
|
# Deploy infrastructure stack
|
||||||
cd /opt/stacks/infrastructure
|
cd /opt/stacks/infrastructure
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
log_success "Infrastructure stack deployed"
|
log_success "Infrastructure stack deployed"
|
||||||
echo ""
|
echo ""
|
||||||
|
else
|
||||||
# Step 5: Deploy dashboard stack
|
log_info "Skipping infrastructure stack deployment"
|
||||||
log_info "Step 5/7: Deploying dashboard stack..."
|
echo ""
|
||||||
log_info " - Homepage (Application Dashboard)"
|
|
||||||
log_info " - Homarr (Modern Dashboard)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Create dashboards directory
|
|
||||||
mkdir -p /opt/stacks/dashboards
|
|
||||||
|
|
||||||
# Copy dashboards compose file
|
|
||||||
cp "$REPO_DIR/docker-compose/dashboards/docker-compose.yml" /opt/stacks/dashboards/docker-compose.yml
|
|
||||||
cp "$REPO_DIR/.env" /opt/stacks/dashboards/.env
|
|
||||||
|
|
||||||
# Copy homepage config
|
|
||||||
if [ -d "$REPO_DIR/docker-compose/dashboards/homepage" ]; then
|
|
||||||
cp -r "$REPO_DIR/docker-compose/dashboards/homepage" /opt/stacks/dashboards/
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Deploy dashboards stack
|
# Step 5: Deploy dashboard stack
|
||||||
cd /opt/stacks/dashboards
|
if [ "$DEPLOY_DASHBOARDS" = true ]; then
|
||||||
docker compose up -d
|
log_info "Step 5: Deploying dashboard stack..."
|
||||||
|
log_info " - Homepage (Application Dashboard)"
|
||||||
|
log_info " - Homarr (Modern Dashboard)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
log_success "Dashboard stack deployed"
|
# Create dashboards directory
|
||||||
echo ""
|
mkdir -p /opt/stacks/dashboards
|
||||||
|
|
||||||
|
# Copy dashboards compose file
|
||||||
|
cp "$REPO_DIR/docker-compose/dashboards/docker-compose.yml" /opt/stacks/dashboards/docker-compose.yml
|
||||||
|
cp "$REPO_DIR/.env" /opt/stacks/dashboards/.env
|
||||||
|
|
||||||
|
# Copy homepage config
|
||||||
|
if [ -d "$REPO_DIR/docker-compose/dashboards/homepage" ]; then
|
||||||
|
cp -r "$REPO_DIR/docker-compose/dashboards/homepage" /opt/stacks/dashboards/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy dashboards stack
|
||||||
|
cd /opt/stacks/dashboards
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
log_success "Dashboard stack deployed"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
log_info "Skipping dashboard stack deployment"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
# Step 6: Deploy Dokuwiki
|
# Step 6: Deploy Dokuwiki
|
||||||
log_info "Step 6/7: Deploying Dokuwiki wiki platform..."
|
log_info "Step 6: Deploying Dokuwiki wiki platform..."
|
||||||
log_info " - DokuWiki (File-based wiki with pre-configured content)"
|
log_info " - DokuWiki (File-based wiki with pre-configured content)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@@ -253,59 +365,12 @@ docker compose up -d
|
|||||||
log_success "Dokuwiki deployed with pre-configured content"
|
log_success "Dokuwiki deployed with pre-configured content"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Step 6: Wait for Dockge to be ready and open browser
|
# Step 7: Setup stacks for Dockge (if requested)
|
||||||
log_info "Step 6/6: Waiting for Dockge web UI to be ready..."
|
if [ "$SETUP_STACKS" = true ]; then
|
||||||
|
setup_stacks_for_dockge
|
||||||
DOCKGE_URL="https://dockge.${DOMAIN}"
|
|
||||||
MAX_WAIT=60 # Maximum wait time in seconds
|
|
||||||
WAITED=0
|
|
||||||
|
|
||||||
# Function to check if Dockge is accessible
|
|
||||||
check_dockge() {
|
|
||||||
# Try to connect to Dockge (ignore SSL cert warnings for self-signed during startup)
|
|
||||||
curl -k -s -o /dev/null -w "%{http_code}" "$DOCKGE_URL" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Wait for Dockge to respond
|
|
||||||
while [ $WAITED -lt $MAX_WAIT ]; do
|
|
||||||
HTTP_CODE=$(check_dockge)
|
|
||||||
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "401" ]; then
|
|
||||||
log_success "Dockge web UI is ready!"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo -n "."
|
|
||||||
sleep 2
|
|
||||||
WAITED=$((WAITED + 2))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [ $WAITED -ge $MAX_WAIT ]; then
|
|
||||||
log_warning "Dockge did not respond within ${MAX_WAIT} seconds"
|
|
||||||
log_info "It may still be starting up. Check manually at: $DOCKGE_URL"
|
|
||||||
else
|
|
||||||
# Try to open browser
|
|
||||||
log_info "Opening Dockge in your browser..."
|
|
||||||
|
|
||||||
# Detect and use available browser
|
|
||||||
if command -v xdg-open &> /dev/null; then
|
|
||||||
xdg-open "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
elif command -v gnome-open &> /dev/null; then
|
|
||||||
gnome-open "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
elif command -v firefox &> /dev/null; then
|
|
||||||
firefox "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
elif command -v google-chrome &> /dev/null; then
|
|
||||||
google-chrome "$DOCKGE_URL" &> /dev/null &
|
|
||||||
log_success "Browser opened"
|
|
||||||
else
|
|
||||||
log_warning "No browser detected. Please manually open: $DOCKGE_URL"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Deployment completed
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
log_success "Deployment completed successfully!"
|
log_success "Deployment completed successfully!"
|
||||||
|
|||||||
647
scripts/ez-homelab.sh
Executable file
647
scripts/ez-homelab.sh
Executable file
@@ -0,0 +1,647 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# EZ-Homelab Unified Setup & Deployment Script
|
||||||
|
# This script provides a guided setup and deployment experience
|
||||||
|
# Run as: ./ez-homelab.sh (will use sudo when needed)
|
||||||
|
|
||||||
|
set -e # Exit on error
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Log functions
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get script directory and repo directory
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
REPO_DIR="$( cd "$SCRIPT_DIR/.." && pwd )"
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
DOMAIN=""
|
||||||
|
SERVER_IP=""
|
||||||
|
ADMIN_USER=""
|
||||||
|
ADMIN_EMAIL=""
|
||||||
|
ADMIN_PASSWORD=""
|
||||||
|
DEPLOY_CORE=false
|
||||||
|
DEPLOY_INFRASTRUCTURE=false
|
||||||
|
DEPLOY_DASHBOARDS=false
|
||||||
|
SETUP_STACKS=false
|
||||||
|
|
||||||
|
# Load existing .env file if it exists
|
||||||
|
load_env_file() {
|
||||||
|
if [ -f "$REPO_DIR/.env" ]; then
|
||||||
|
log_info "Found existing .env file, loading current configuration..."
|
||||||
|
source "$REPO_DIR/.env"
|
||||||
|
|
||||||
|
# Show current values
|
||||||
|
echo ""
|
||||||
|
echo "Current configuration:"
|
||||||
|
echo " Domain: ${DOMAIN:-Not set}"
|
||||||
|
echo " Server IP: ${SERVER_IP:-Not set}"
|
||||||
|
echo " Admin User: ${AUTHELIA_ADMIN_USER:-Not set}"
|
||||||
|
echo " Admin Email: ${AUTHELIA_ADMIN_EMAIL:-Not set}"
|
||||||
|
echo " Timezone: ${TZ:-Not set}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_info "No existing .env file found. We'll create one during setup."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save configuration to .env file
|
||||||
|
save_env_file() {
|
||||||
|
log_info "Saving configuration to .env file..."
|
||||||
|
|
||||||
|
# Create .env file if it doesn't exist
|
||||||
|
if [ ! -f "$REPO_DIR/.env" ]; then
|
||||||
|
cp "$REPO_DIR/.env.example" "$REPO_DIR/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update values
|
||||||
|
sed -i "s%DOMAIN=.*%DOMAIN=$DOMAIN%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%SERVER_IP=.*%SERVER_IP=$SERVER_IP%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%TZ=.*%TZ=$TZ%" "$REPO_DIR/.env"
|
||||||
|
|
||||||
|
# Authelia settings (only if deploying core)
|
||||||
|
if [ "$DEPLOY_CORE" = true ]; then
|
||||||
|
# Ensure we have admin credentials
|
||||||
|
if [ -z "$ADMIN_USER" ]; then
|
||||||
|
ADMIN_USER="admin"
|
||||||
|
fi
|
||||||
|
if [ -z "$ADMIN_EMAIL" ]; then
|
||||||
|
ADMIN_EMAIL="${ADMIN_USER}@${DOMAIN}"
|
||||||
|
fi
|
||||||
|
if [ -z "$ADMIN_PASSWORD" ]; then
|
||||||
|
log_info "Using default admin password (changeme123) - please change this after setup!"
|
||||||
|
ADMIN_PASSWORD="changeme123"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AUTHELIA_JWT_SECRET" ]; then
|
||||||
|
AUTHELIA_JWT_SECRET=$(openssl rand -hex 64)
|
||||||
|
fi
|
||||||
|
if [ -z "$AUTHELIA_SESSION_SECRET" ]; then
|
||||||
|
AUTHELIA_SESSION_SECRET=$(openssl rand -hex 64)
|
||||||
|
fi
|
||||||
|
if [ -z "$AUTHELIA_STORAGE_ENCRYPTION_KEY" ]; then
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 64)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save Authelia settings to .env
|
||||||
|
sed -i "s%AUTHELIA_JWT_SECRET=.*%AUTHELIA_JWT_SECRET=$AUTHELIA_JWT_SECRET%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%AUTHELIA_SESSION_SECRET=.*%AUTHELIA_SESSION_SECRET=$AUTHELIA_SESSION_SECRET%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%AUTHELIA_STORAGE_ENCRYPTION_KEY=.*%AUTHELIA_STORAGE_ENCRYPTION_KEY=$AUTHELIA_STORAGE_ENCRYPTION_KEY%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%# AUTHELIA_ADMIN_USER=.*%AUTHELIA_ADMIN_USER=$ADMIN_USER%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%# AUTHELIA_ADMIN_EMAIL=.*%AUTHELIA_ADMIN_EMAIL=$ADMIN_EMAIL%" "$REPO_DIR/.env"
|
||||||
|
|
||||||
|
# Generate password hash if needed
|
||||||
|
if [ -z "$AUTHELIA_ADMIN_PASSWORD" ]; then
|
||||||
|
log_info "Generating Authelia password hash..."
|
||||||
|
# Pull Authelia image if needed
|
||||||
|
if ! docker images | grep -q authelia/authelia; then
|
||||||
|
docker pull authelia/authelia:latest > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
AUTHELIA_ADMIN_PASSWORD=$(docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password "$ADMIN_PASSWORD" 2>&1 | grep -o '\$argon2id.*')
|
||||||
|
if [ -z "$AUTHELIA_ADMIN_PASSWORD" ]; then
|
||||||
|
log_error "Failed to generate Authelia password hash. Please check that ADMIN_PASSWORD is set."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save password hash
|
||||||
|
sed -i "s%# AUTHELIA_ADMIN_PASSWORD=.*%AUTHELIA_ADMIN_PASSWORD=$AUTHELIA_ADMIN_PASSWORD%" "$REPO_DIR/.env"
|
||||||
|
sed -i "s%AUTHELIA_ADMIN_PASSWORD=.*%AUTHELIA_ADMIN_PASSWORD=$AUTHELIA_ADMIN_PASSWORD%" "$REPO_DIR/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Configuration saved to .env file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt for required values
|
||||||
|
prompt_for_values() {
|
||||||
|
echo ""
|
||||||
|
log_info "Please provide the following information:"
|
||||||
|
echo " (Press Enter without typing to keep the current/default value shown in brackets)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Domain
|
||||||
|
if [ -z "$DOMAIN" ]; then
|
||||||
|
read -p "Enter your domain (e.g., example.duckdns.org): " DOMAIN
|
||||||
|
while [ -z "$DOMAIN" ]; do
|
||||||
|
log_warning "Domain is required"
|
||||||
|
read -p "Enter your domain (e.g., example.duckdns.org): " DOMAIN
|
||||||
|
done
|
||||||
|
else
|
||||||
|
read -p "Domain [$DOMAIN] (press Enter to keep current): " input
|
||||||
|
[ -n "$input" ] && DOMAIN="$input"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Server IP
|
||||||
|
if [ -z "$SERVER_IP" ]; then
|
||||||
|
read -p "Enter your server IP address: " SERVER_IP
|
||||||
|
while [ -z "$SERVER_IP" ]; do
|
||||||
|
log_warning "Server IP is required"
|
||||||
|
read -p "Enter your server IP address: " SERVER_IP
|
||||||
|
done
|
||||||
|
else
|
||||||
|
read -p "Server IP [$SERVER_IP] (press Enter to keep current): " input
|
||||||
|
[ -n "$input" ] && SERVER_IP="$input"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Timezone
|
||||||
|
if [ -z "$TZ" ]; then
|
||||||
|
TZ="America/New_York"
|
||||||
|
fi
|
||||||
|
read -p "Timezone [$TZ] (press Enter to keep current): " input
|
||||||
|
[ -n "$input" ] && TZ="$input"
|
||||||
|
|
||||||
|
# Admin credentials (only if deploying core)
|
||||||
|
if [ "$DEPLOY_CORE" = true ]; then
|
||||||
|
echo ""
|
||||||
|
log_info "Authelia Admin Credentials:"
|
||||||
|
|
||||||
|
if [ -z "$ADMIN_USER" ]; then
|
||||||
|
ADMIN_USER="admin"
|
||||||
|
fi
|
||||||
|
read -p "Admin username [$ADMIN_USER] (press Enter to keep current): " input
|
||||||
|
[ -n "$input" ] && ADMIN_USER="$input"
|
||||||
|
|
||||||
|
if [ -z "$ADMIN_EMAIL" ]; then
|
||||||
|
ADMIN_EMAIL="${ADMIN_USER}@${DOMAIN}"
|
||||||
|
fi
|
||||||
|
read -p "Admin email [$ADMIN_EMAIL] (press Enter to keep current): " input
|
||||||
|
[ -n "$input" ] && ADMIN_EMAIL="$input"
|
||||||
|
|
||||||
|
if [ -z "$ADMIN_PASSWORD" ]; then
|
||||||
|
while [ -z "$ADMIN_PASSWORD" ]; do
|
||||||
|
read -s -p "Admin password (will be hashed): " ADMIN_PASSWORD
|
||||||
|
echo ""
|
||||||
|
if [ ${#ADMIN_PASSWORD} -lt 8 ]; then
|
||||||
|
log_warning "Password must be at least 8 characters"
|
||||||
|
ADMIN_PASSWORD=""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
log_info "Admin password already configured"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# System setup function (Docker, directories, etc.)
|
||||||
|
system_setup() {
|
||||||
|
log_info "Performing system setup..."
|
||||||
|
|
||||||
|
# Check if running as root for system setup
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
log_warning "System setup requires root privileges. Running with sudo..."
|
||||||
|
exec sudo "$0" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the actual user who invoked sudo
|
||||||
|
ACTUAL_USER=${SUDO_USER:-$USER}
|
||||||
|
|
||||||
|
# Step 1: System Update
|
||||||
|
log_info "Step 1/10: Updating system packages..."
|
||||||
|
apt-get update && apt-get upgrade -y
|
||||||
|
log_success "System updated successfully"
|
||||||
|
|
||||||
|
# Step 2: Install required packages
|
||||||
|
log_info "Step 2/10: Installing required packages..."
|
||||||
|
apt-get install -y curl wget git htop nano vim ufw fail2ban unattended-upgrades apt-listchanges
|
||||||
|
|
||||||
|
# Step 3: Install Docker
|
||||||
|
log_info "Step 3/10: Installing Docker..."
|
||||||
|
curl -fsSL https://get.docker.com | sh
|
||||||
|
usermod -aG docker "$ACTUAL_USER"
|
||||||
|
|
||||||
|
# Step 4: Install Docker Compose
|
||||||
|
log_info "Step 4/10: Installing Docker Compose..."
|
||||||
|
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
|
# Step 5: Configure UFW firewall
|
||||||
|
log_info "Step 5/10: Configuring firewall..."
|
||||||
|
ufw --force enable
|
||||||
|
ufw allow ssh
|
||||||
|
ufw allow 80
|
||||||
|
ufw allow 443
|
||||||
|
|
||||||
|
# Step 6: Configure automatic updates
|
||||||
|
log_info "Step 6/10: Configuring automatic updates..."
|
||||||
|
dpkg-reconfigure -f noninteractive unattended-upgrades
|
||||||
|
|
||||||
|
# Step 7: Create required directories
|
||||||
|
log_info "Step 7/10: Creating required directories..."
|
||||||
|
mkdir -p /opt/stacks/core
|
||||||
|
mkdir -p /opt/stacks/infrastructure
|
||||||
|
mkdir -p /opt/stacks/dashboards
|
||||||
|
mkdir -p /opt/dockge
|
||||||
|
|
||||||
|
# Step 8: Set proper ownership
|
||||||
|
log_info "Step 8/10: Setting directory ownership..."
|
||||||
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt/stacks
|
||||||
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt/dockge
|
||||||
|
|
||||||
|
# Step 9: Create Docker networks
|
||||||
|
log_info "Step 9/10: Creating Docker networks..."
|
||||||
|
docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists"
|
||||||
|
docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists"
|
||||||
|
docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists"
|
||||||
|
|
||||||
|
# Step 10: Generate SSH keys for Git (optional)
|
||||||
|
log_info "Step 10/10: SSH key setup (optional)..."
|
||||||
|
if [ ! -f "/home/$ACTUAL_USER/.ssh/id_rsa" ]; then
|
||||||
|
log_info "Generating SSH key for $ACTUAL_USER..."
|
||||||
|
sudo -u "$ACTUAL_USER" ssh-keygen -t rsa -b 4096 -f "/home/$ACTUAL_USER/.ssh/id_rsa" -N ""
|
||||||
|
log_info "SSH public key:"
|
||||||
|
cat "/home/$ACTUAL_USER/.ssh/id_rsa.pub"
|
||||||
|
echo ""
|
||||||
|
log_info "Add this key to your Git provider (GitHub, GitLab, etc.)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "System setup completed!"
|
||||||
|
echo ""
|
||||||
|
log_info "Please log out and back in for Docker group changes to take effect."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deployment function
|
||||||
|
perform_deployment() {
|
||||||
|
log_info "Starting deployment..."
|
||||||
|
|
||||||
|
# Switch back to regular user if we were running as root
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
ACTUAL_USER=${SUDO_USER:-$USER}
|
||||||
|
log_info "Switching to user $ACTUAL_USER for deployment..."
|
||||||
|
exec sudo -u "$ACTUAL_USER" "$0" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source the .env file
|
||||||
|
source "$REPO_DIR/.env"
|
||||||
|
|
||||||
|
# Step 1: Create required directories
|
||||||
|
log_info "Step 1: Creating required directories..."
|
||||||
|
mkdir -p /opt/stacks/core
|
||||||
|
mkdir -p /opt/stacks/infrastructure
|
||||||
|
mkdir -p /opt/stacks/dashboards
|
||||||
|
mkdir -p /opt/dockge
|
||||||
|
log_success "Directories created"
|
||||||
|
|
||||||
|
# Step 2: Create Docker networks (if they don't exist)
|
||||||
|
log_info "Step 2: Creating Docker networks..."
|
||||||
|
docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists"
|
||||||
|
docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists"
|
||||||
|
docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 3: Deploy Dockge (always deployed)
|
||||||
|
log_info "Step 3: Deploying Dockge stack manager..."
|
||||||
|
log_info " - Dockge (Docker Compose Manager)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy Dockge stack files
|
||||||
|
cp "$REPO_DIR/docker-compose/dockge/docker-compose.yml" /opt/dockge/docker-compose.yml
|
||||||
|
cp "$REPO_DIR/.env" /opt/dockge/.env
|
||||||
|
|
||||||
|
# Deploy Dockge stack
|
||||||
|
cd /opt/dockge
|
||||||
|
docker compose up -d
|
||||||
|
log_success "Dockge deployed"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Deploy core infrastructure
|
||||||
|
if [ "$DEPLOY_CORE" = true ]; then
|
||||||
|
log_info "Step 4: Deploying core infrastructure stack..."
|
||||||
|
log_info " - DuckDNS (Dynamic DNS)"
|
||||||
|
log_info " - Traefik (Reverse Proxy with SSL)"
|
||||||
|
log_info " - Authelia (Single Sign-On)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy core stack files
|
||||||
|
cp "$REPO_DIR/docker-compose/core/docker-compose.yml" /opt/stacks/core/docker-compose.yml
|
||||||
|
cp "$REPO_DIR/.env" /opt/stacks/core/.env
|
||||||
|
|
||||||
|
# Copy configs
|
||||||
|
if [ -d "/opt/stacks/core/traefik" ]; then
|
||||||
|
mv /opt/stacks/core/traefik /opt/stacks/core/traefik.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/
|
||||||
|
|
||||||
|
# Replace ACME email placeholder
|
||||||
|
sed -i "s/ACME_EMAIL_PLACEHOLDER/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/traefik/traefik.yml
|
||||||
|
|
||||||
|
# Replace domain placeholders in traefik dynamic configs
|
||||||
|
find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s/\${DOMAIN}/${DOMAIN}/g" {} \;
|
||||||
|
|
||||||
|
if [ -d "/opt/stacks/core/authelia" ]; then
|
||||||
|
mv /opt/stacks/core/authelia /opt/stacks/core/authelia.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/
|
||||||
|
|
||||||
|
# Replace domain placeholders
|
||||||
|
sed -i "s/your-domain.duckdns.org/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
|
||||||
|
# Replace secret placeholders
|
||||||
|
sed -i "s|\${AUTHELIA_JWT_SECRET}|${AUTHELIA_JWT_SECRET}|g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
sed -i "s|\${AUTHELIA_SESSION_SECRET}|${AUTHELIA_SESSION_SECRET}|g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
sed -i "s|\${AUTHELIA_STORAGE_ENCRYPTION_KEY}|${AUTHELIA_STORAGE_ENCRYPTION_KEY}|g" /opt/stacks/core/authelia/configuration.yml
|
||||||
|
sed -i "s/admin/${AUTHELIA_ADMIN_USER}/g" /opt/stacks/core/authelia/users_database.yml
|
||||||
|
sed -i "s/admin@example.com/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/authelia/users_database.yml
|
||||||
|
sed -i "s|\$argon2id\$v=19\$m=65536,t=3,p=4\$CHANGEME|${AUTHELIA_ADMIN_PASSWORD}|g" /opt/stacks/core/authelia/users_database.yml
|
||||||
|
|
||||||
|
# Deploy core stack
|
||||||
|
cd /opt/stacks/core
|
||||||
|
docker compose up -d
|
||||||
|
log_success "Core infrastructure deployed"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy infrastructure stack
|
||||||
|
if [ "$DEPLOY_INFRASTRUCTURE" = true ]; then
|
||||||
|
step_num=$([ "$DEPLOY_CORE" = true ] && echo "5" || echo "4")
|
||||||
|
log_info "Step $step_num: Deploying infrastructure stack..."
|
||||||
|
log_info " - Pi-hole (DNS Ad Blocker)"
|
||||||
|
log_info " - Watchtower (Container Updates)"
|
||||||
|
log_info " - Dozzle (Log Viewer)"
|
||||||
|
log_info " - Glances (System Monitor)"
|
||||||
|
log_info " - Docker Proxy (Security)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy infrastructure stack
|
||||||
|
cp "$REPO_DIR/docker-compose/infrastructure/docker-compose.yml" /opt/stacks/infrastructure/docker-compose.yml
|
||||||
|
cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env
|
||||||
|
|
||||||
|
# If core is not deployed, remove Authelia middleware references
|
||||||
|
if [ "$DEPLOY_CORE" = false ]; then
|
||||||
|
log_info "Core infrastructure not deployed - removing Authelia middleware references..."
|
||||||
|
sed -i '/middlewares=authelia@docker/d' /opt/stacks/infrastructure/docker-compose.yml
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy infrastructure stack
|
||||||
|
cd /opt/stacks/infrastructure
|
||||||
|
docker compose up -d
|
||||||
|
log_success "Infrastructure stack deployed"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy dashboard stack
|
||||||
|
if [ "$DEPLOY_DASHBOARDS" = true ]; then
|
||||||
|
if [ "$DEPLOY_CORE" = true ] && [ "$DEPLOY_INFRASTRUCTURE" = true ]; then
|
||||||
|
step_num=6
|
||||||
|
elif [ "$DEPLOY_CORE" = true ] || [ "$DEPLOY_INFRASTRUCTURE" = true ]; then
|
||||||
|
step_num=5
|
||||||
|
else
|
||||||
|
step_num=4
|
||||||
|
fi
|
||||||
|
log_info "Step $step_num: Deploying dashboard stack..."
|
||||||
|
log_info " - Homepage (Application Dashboard)"
|
||||||
|
log_info " - Homarr (Modern Dashboard)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create dashboards directory
|
||||||
|
mkdir -p /opt/stacks/dashboards
|
||||||
|
|
||||||
|
# Copy dashboards compose file
|
||||||
|
cp "$REPO_DIR/docker-compose/dashboards/docker-compose.yml" /opt/stacks/dashboards/docker-compose.yml
|
||||||
|
cp "$REPO_DIR/.env" /opt/stacks/dashboards/.env
|
||||||
|
|
||||||
|
# Copy homepage config
|
||||||
|
if [ -d "$REPO_DIR/docker-compose/dashboards/homepage" ]; then
|
||||||
|
cp -r "$REPO_DIR/docker-compose/dashboards/homepage" /opt/stacks/dashboards/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy dashboards stack
|
||||||
|
cd /opt/stacks/dashboards
|
||||||
|
docker compose up -d
|
||||||
|
log_success "Dashboard stack deployed"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup stacks for Dockge
|
||||||
|
if [ "$SETUP_STACKS" = true ]; then
|
||||||
|
setup_stacks_for_dockge
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy Dokuwiki (always deployed as it's part of the base setup)
|
||||||
|
deployed_count=3 # Dockge is always deployed
|
||||||
|
[ "$DEPLOY_CORE" = true ] && deployed_count=$((deployed_count + 1))
|
||||||
|
[ "$DEPLOY_INFRASTRUCTURE" = true ] && deployed_count=$((deployed_count + 1))
|
||||||
|
[ "$DEPLOY_DASHBOARDS" = true ] && deployed_count=$((deployed_count + 1))
|
||||||
|
step_num=$((deployed_count + 1))
|
||||||
|
log_info "Step $step_num: Deploying Dokuwiki wiki platform..."
|
||||||
|
log_info " - DokuWiki (File-based wiki with pre-configured content)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create Dokuwiki directory
|
||||||
|
mkdir -p /opt/stacks/dokuwiki/config
|
||||||
|
|
||||||
|
# Copy Dokuwiki compose file
|
||||||
|
cp "$REPO_DIR/config-templates/dokuwiki/docker-compose.yml" /opt/stacks/dokuwiki/docker-compose.yml
|
||||||
|
|
||||||
|
# Replace domain placeholders in Dokuwiki
|
||||||
|
sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/dokuwiki/docker-compose.yml
|
||||||
|
|
||||||
|
# Copy pre-configured Dokuwiki config, content, and keys
|
||||||
|
if [ -d "$REPO_DIR/config-templates/dokuwiki/conf" ]; then
|
||||||
|
cp -r "$REPO_DIR/config-templates/dokuwiki/conf" /opt/stacks/dokuwiki/config/
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$REPO_DIR/config-templates/dokuwiki/data" ]; then
|
||||||
|
cp -r "$REPO_DIR/config-templates/dokuwiki/data" /opt/stacks/dokuwiki/config/
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$REPO_DIR/config-templates/dokuwiki/keys" ]; then
|
||||||
|
cp -r "$REPO_DIR/config-templates/dokuwiki/keys" /opt/stacks/dokuwiki/config/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set proper ownership for Dokuwiki config
|
||||||
|
sudo chown -R 1000:1000 /opt/stacks/dokuwiki/config
|
||||||
|
|
||||||
|
# Deploy Dokuwiki
|
||||||
|
cd /opt/stacks/dokuwiki
|
||||||
|
docker compose up -d
|
||||||
|
log_success "Dokuwiki deployed with pre-configured content"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Setup stacks for Dockge function
|
||||||
|
setup_stacks_for_dockge() {
|
||||||
|
log_info "Setting up all stacks for Dockge..."
|
||||||
|
|
||||||
|
# List of stacks to setup
|
||||||
|
STACKS=("vpn" "media" "media-management" "monitoring" "productivity" "utilities" "alternatives" "homeassistant" "nextcloud")
|
||||||
|
|
||||||
|
for stack in "${STACKS[@]}"; do
|
||||||
|
STACK_DIR="/opt/stacks/$stack"
|
||||||
|
REPO_STACK_DIR="$REPO_DIR/docker-compose/$stack"
|
||||||
|
|
||||||
|
if [ -d "$REPO_STACK_DIR" ]; then
|
||||||
|
mkdir -p "$STACK_DIR"
|
||||||
|
if [ -f "$REPO_STACK_DIR/docker-compose.yml" ]; then
|
||||||
|
cp "$REPO_STACK_DIR/docker-compose.yml" "$STACK_DIR/docker-compose.yml"
|
||||||
|
cp "$REPO_DIR/.env" "$STACK_DIR/.env"
|
||||||
|
log_success "Prepared $stack stack for Dockge"
|
||||||
|
else
|
||||||
|
log_warning "$stack stack docker-compose.yml not found, skipping..."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warning "$stack stack directory not found in repo, skipping..."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_success "All stacks prepared for Dockge deployment"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main menu
|
||||||
|
show_main_menu() {
|
||||||
|
echo "=========================================="
|
||||||
|
echo " EZ-HOMELAB SETUP & DEPLOYMENT"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "What would you like to do?"
|
||||||
|
echo ""
|
||||||
|
echo "1) 🚀 Default Setup (Recommended)"
|
||||||
|
echo " - Deploy Dockge, core infrastructure, dashboards & monitoring"
|
||||||
|
echo " - All additional stacks prepared for Dockge"
|
||||||
|
echo ""
|
||||||
|
echo "2) 🏗️ Core Only"
|
||||||
|
echo " - Deploy Dockge and core infrastructure only"
|
||||||
|
echo " - All stacks prepared for Dockge"
|
||||||
|
echo ""
|
||||||
|
echo "3) 🔧 Infrastructure Only"
|
||||||
|
echo " - Deploy Dockge and monitoring tools"
|
||||||
|
echo " - Requires existing Traefik (from previous setup)"
|
||||||
|
echo " - Services accessible without authentication"
|
||||||
|
echo " - All stacks prepared for Dockge"
|
||||||
|
echo ""
|
||||||
|
echo "4) ❌ Exit"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main logic
|
||||||
|
main() {
|
||||||
|
log_info "EZ-Homelab Unified Setup & Deployment Script"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Load existing configuration
|
||||||
|
ENV_EXISTS=false
|
||||||
|
if load_env_file; then
|
||||||
|
ENV_EXISTS=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show main menu
|
||||||
|
show_main_menu
|
||||||
|
read -p "Choose an option (1-4): " MAIN_CHOICE
|
||||||
|
|
||||||
|
case $MAIN_CHOICE in
|
||||||
|
1)
|
||||||
|
log_info "Selected: Default Setup"
|
||||||
|
DEPLOY_CORE=true
|
||||||
|
DEPLOY_INFRASTRUCTURE=true
|
||||||
|
DEPLOY_DASHBOARDS=true
|
||||||
|
SETUP_STACKS=true
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
log_info "Selected: Core Only"
|
||||||
|
DEPLOY_CORE=true
|
||||||
|
DEPLOY_INFRASTRUCTURE=false
|
||||||
|
DEPLOY_DASHBOARDS=false
|
||||||
|
SETUP_STACKS=true
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
log_info "Selected: Infrastructure Only"
|
||||||
|
DEPLOY_CORE=false
|
||||||
|
DEPLOY_INFRASTRUCTURE=true
|
||||||
|
DEPLOY_DASHBOARDS=false
|
||||||
|
SETUP_STACKS=true
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
log_info "Exiting..."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Invalid choice. Please run the script again."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if system setup is needed
|
||||||
|
if [ "$EUID" -eq 0 ] || ! command -v docker &> /dev/null; then
|
||||||
|
log_info "Docker not found or running as root. Performing system setup first..."
|
||||||
|
system_setup "$@"
|
||||||
|
echo ""
|
||||||
|
log_info "System setup complete. Please log out and back in, then run this script again."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt for configuration values
|
||||||
|
prompt_for_values
|
||||||
|
|
||||||
|
# Save configuration
|
||||||
|
save_env_file
|
||||||
|
|
||||||
|
# Perform deployment
|
||||||
|
perform_deployment
|
||||||
|
|
||||||
|
# Show completion message
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
log_success "Setup and deployment completed successfully!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$DEPLOY_INFRASTRUCTURE" = true ]; then
|
||||||
|
log_info "Access your services:"
|
||||||
|
echo ""
|
||||||
|
echo " 🚀 Dockge: https://dockge.${DOMAIN}"
|
||||||
|
[ "$DEPLOY_CORE" = true ] && echo " 🔒 Authelia: https://auth.${DOMAIN}"
|
||||||
|
[ "$DEPLOY_CORE" = true ] && echo " 🔀 Traefik: https://traefik.${DOMAIN}"
|
||||||
|
echo " 📊 Homepage: https://home.${DOMAIN}"
|
||||||
|
echo " 🎯 Homarr: https://homarr.${DOMAIN}"
|
||||||
|
echo " 📖 Wiki: https://wiki.${DOMAIN}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Next steps:"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Access Dockge at https://dockge.${DOMAIN}"
|
||||||
|
if [ "$DEPLOY_CORE" = true ]; then
|
||||||
|
echo " (Use your Authelia credentials: ${AUTHELIA_ADMIN_USER})"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo " 2. Start additional stacks from Dockge's web UI"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Configure services via the AI assistant in VS Code"
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
log_info "For documentation, see: $REPO_DIR/docs/"
|
||||||
|
log_info "For troubleshooting, see: $REPO_DIR/docs/quick-reference.md"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user