Replace personal URLs with placeholders and fix variable replacement logic
This commit is contained in:
@@ -60,7 +60,7 @@ ACME_EMAIL=${DEFAULT_EMAIL}
|
|||||||
ADMIN_EMAIL=${DEFAULT_EMAIL} # Used for admin user account
|
ADMIN_EMAIL=${DEFAULT_EMAIL} # Used for admin user account
|
||||||
AUTHELIA_ADMIN_USER=${DEFAULT_USER}
|
AUTHELIA_ADMIN_USER=${DEFAULT_USER}
|
||||||
AUTHELIA_ADMIN_EMAIL=${DEFAULT_EMAIL}
|
AUTHELIA_ADMIN_EMAIL=${DEFAULT_EMAIL}
|
||||||
AUTHELIA_ADMIN_PASSWORD=generate-with-openssl-rand-hex-64
|
AUTHELIA_ADMIN_PASSWORD_HASH=generate-with-openssl-rand-hex-64
|
||||||
|
|
||||||
# SMTP for Authelia Notifications
|
# SMTP for Authelia Notifications
|
||||||
SMTP_USERNAME=${SMTP_EMAIL_FROM}
|
SMTP_USERNAME=${SMTP_EMAIL_FROM}
|
||||||
|
|||||||
73
TASKS.md
Normal file
73
TASKS.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# EZ-Homelab Script Refactoring Tasks
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This document outlines the updated plan for refactoring `ez-homelab.sh` based on user requirements. Tasks are prioritized by impact and dependencies. All files are in the same repo. Dry-run output should be user-friendly.
|
||||||
|
|
||||||
|
## Task Categories
|
||||||
|
|
||||||
|
### 1. Menu and Workflow Updates (High Priority)
|
||||||
|
- **1.1: Create `install-prerequisites.sh`**
|
||||||
|
Extract `system_setup()` into a standalone script that must run as root/sudo. Update Option 1 to launch it with sudo if needed.
|
||||||
|
*Effort*: 2-3 hours. *Files*: New `install-prerequisites.sh`, modify `ez-homelab.sh`.
|
||||||
|
|
||||||
|
- **1.2: Update Menu Option 3 Prompts**
|
||||||
|
For Option 3, check if default values are valid. If yes, prompt to use defaults or not. If not, prompt for all REQUIRED_VARS to ensure easy deployment. Reword prompts to clarify REMOTE_SERVER_* vars are for core server cert copying.
|
||||||
|
*Effort*: 1-2 hours. *Files*: `ez-homelab.sh` (`validate_and_prompt_variables()`, `prompt_for_variable()`).
|
||||||
|
|
||||||
|
- **1.3: Implement Menu Option 4 (NVIDIA Installation)**
|
||||||
|
Use `nvidia-detect` to determine GPU and official installer. Install NVIDIA drivers and Container Toolkit. Handle no-GPU gracefully.
|
||||||
|
*Effort*: 3-4 hours. *Files*: `ez-homelab.sh` or `install-prerequisites.sh`.
|
||||||
|
|
||||||
|
### 2. Bug Fixes (High Priority)
|
||||||
|
- **2.1: Remove Hardcoded Values**
|
||||||
|
Replace "kelin", "kelinreij", etc., with variables like `${DOMAIN}`, `${SERVER_IP}` in completion messages and examples.
|
||||||
|
*Effort*: 1 hour. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
- **2.2: Fix HOMEPAGE_ALLOWED_HOSTS**
|
||||||
|
Instead of hardcoding port (3003), extract the proper port from the Homepage compose file. Ensure line is `HOMEPAGE_ALLOWED_HOSTS="homepage.${DOMAIN},${SERVER_IP}:<extracted_port>"`.
|
||||||
|
*Effort*: 30 minutes. *Files*: `ez-homelab.sh` (`save_env_file()`).
|
||||||
|
|
||||||
|
### 3. New Features and Enhancements (Medium Priority)
|
||||||
|
- **3.1: Add Argument Parsing**
|
||||||
|
Implement CLI args (e.g., `--deploy-core`, `--dry-run`, `--verbose`) using `getopts` to bypass menu.
|
||||||
|
*Effort*: 2-3 hours. *Files*: `ez-homelab.sh` (`main()`).
|
||||||
|
|
||||||
|
- **3.2: Add Dry-Run Mode**
|
||||||
|
`--dry-run` simulates deployment: validate configs, show actions, log verbosely without executing. Output user-friendly summaries.
|
||||||
|
*Effort*: 2 hours. *Files*: `ez-homelab.sh` (`perform_deployment()`).
|
||||||
|
|
||||||
|
- **3.3: Enhance Console Logging for Verbose Mode**
|
||||||
|
Update `log_*` functions to output to console when `VERBOSE=true`.
|
||||||
|
*Effort*: 1 hour. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
- **3.4: Improve Error Handling**
|
||||||
|
Remove `set -e`; log errors but continue where possible. Use `||` for non-critical failures.
|
||||||
|
*Effort*: 2 hours. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
### 4. TLS and Multi-Server Logic Refinements (Medium Priority)
|
||||||
|
- **4.1: Clarify Variable Usage**
|
||||||
|
Ensure prompts distinguish: `SERVER_IP` for local machine, `REMOTE_SERVER_*` for core server. `${DOMAIN}` prompted even for additional servers (needed for configs).
|
||||||
|
*Effort*: 1-2 hours. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
### 5. Function Organization and Code Quality (Low Priority)
|
||||||
|
- **5.1: Audit and Improve Placeholder/Env Functions**
|
||||||
|
Rename `replace_env_placeholders()` to `localize_yml_file()` and `enhance_placeholder_replacement()` to `localize_deployment()`. Add error aggregation in bulk function. Make single-file function robust (permissions, backups only for existing targets, no repo modifications). Add post-replacement validation for Traefik labels. Handle special characters in values (passwords, hashes).
|
||||||
|
*Effort*: 2-3 hours. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
- **5.2: Modularize Code with More Functions**
|
||||||
|
Break `main()` into `parse_args()`, `handle_menu_choice()`, `prepare_deployment()`. Extract repeated logic (env copying, dir creation).
|
||||||
|
*Effort*: 3-4 hours. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
- **5.3: Fix Deployment Flow**
|
||||||
|
Streamline `perform_deployment()`: consistent step numbering, better recovery, dry-run integration.
|
||||||
|
*Effort*: 1 hour. *Files*: `ez-homelab.sh`.
|
||||||
|
|
||||||
|
## Implementation Order
|
||||||
|
1. Start with Bug Fixes (2.1-2.2) and Menu Option 1 (1.1).
|
||||||
|
2. Then New Features (3.1-3.4) and Menu Options (1.2-1.3).
|
||||||
|
3. Refinements (4.1, 5.1-5.3).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Test after each task: interactive menu, args, dry-run, multi-server.
|
||||||
|
- Dependencies: NVIDIA tasks require `nvidia-detect`; dry-run depends on args.
|
||||||
|
- Risks: Error handling changes may mask issues; validate thoroughly.
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
users:
|
users:
|
||||||
${AUTHELIA_ADMIN_USER}:
|
${AUTHELIA_ADMIN_USER}:
|
||||||
displayname: "${AUTHELIA_ADMIN_USER}"
|
displayname: ${AUTHELIA_ADMIN_USER}
|
||||||
password: "${AUTHELIA_ADMIN_PASSWORD}"
|
password: ${AUTHELIA_ADMIN_PASSWORD_HASH}
|
||||||
email: "${AUTHELIA_ADMIN_EMAIL}"
|
email: ${AUTHELIA_ADMIN_EMAIL}
|
||||||
groups:
|
groups:
|
||||||
- admins
|
- admins
|
||||||
- users
|
- users
|
||||||
|
|||||||
@@ -7,29 +7,29 @@
|
|||||||
href: https://homepage.kelinreij.duckdns.org
|
href: https://homepage.kelinreij.duckdns.org
|
||||||
description: Hosted on Raspberry Pi
|
description: Hosted on Raspberry Pi
|
||||||
|
|
||||||
- Homepage - ${REMOTE_SERVER_HOSTNAME}:
|
- Homepage - your-remote-server :
|
||||||
icon: homepage.png
|
icon: homepage.png
|
||||||
href: https://homepage.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://homepage.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Application Dashboard
|
description: your-remote-server - Application Dashboard
|
||||||
|
|
||||||
- Homarr:
|
- Homarr:
|
||||||
icon: homarr.png
|
icon: homarr.png
|
||||||
href: https://homarr.kelinreij.duckdns.org
|
href: https://homarr.kelinreij.duckdns.org
|
||||||
description: Alternative Dashboard
|
description: Alternative Dashboard
|
||||||
|
|
||||||
- Homarr - ${REMOTE_SERVER_HOSTNAME}:
|
- Homarr - your-remote-server :
|
||||||
icon: homarr.png
|
icon: homarr.png
|
||||||
href: https://homarr.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://homarr.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Alternative Dashboard
|
description: your-remote-server - Alternative Dashboard
|
||||||
|
|
||||||
- Dockge - jasper:
|
- Dockge - jasper:
|
||||||
icon: dockge.png
|
icon: dockge.png
|
||||||
href: https://jasper.kelinreij.duckdns.org
|
href: https://jasper.kelinreij.duckdns.org
|
||||||
description: Main Server
|
description: Main Server
|
||||||
|
|
||||||
- Dockge - ${REMOTE_SERVER_HOSTNAME}:
|
- Dockge - your-remote-server :
|
||||||
icon: dockge.png
|
icon: dockge.png
|
||||||
href: https://${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://your-remote-server .kelinreij.duckdns.org
|
||||||
description: Raspberry Pi Authentication Server
|
description: Raspberry Pi Authentication Server
|
||||||
|
|
||||||
- Core:
|
- Core:
|
||||||
@@ -56,38 +56,38 @@
|
|||||||
|
|
||||||
- Dozzle:
|
- Dozzle:
|
||||||
icon: dozzle.png
|
icon: dozzle.png
|
||||||
href: https://dozzle.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://dozzle.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Real-time Log Viewer
|
description: your-remote-server - Real-time Log Viewer
|
||||||
|
|
||||||
- Glances - jasper:
|
- Glances - jasper:
|
||||||
icon: glances.png
|
icon: glances.png
|
||||||
href: https://glances.jasper.kelinreij.duckdns.org
|
href: https://glances.jasper.kelinreij.duckdns.org
|
||||||
description: jasper - System Monitoring
|
description: jasper - System Monitoring
|
||||||
|
|
||||||
- Glances - ${REMOTE_SERVER_HOSTNAME}:
|
- Glances - your-remote-server :
|
||||||
icon: glances.png
|
icon: glances.png
|
||||||
href: https://glances.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://glances.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - System Monitoring
|
description: your-remote-server - System Monitoring
|
||||||
|
|
||||||
- Uptime Kuma:
|
- Uptime Kuma:
|
||||||
icon: uptime-kuma.png
|
icon: uptime-kuma.png
|
||||||
href: https://uptime-kuma.kelinreij.duckdns.org
|
href: https://uptime-kuma.kelinreij.duckdns.org
|
||||||
description: Uptime Monitoring
|
description: Uptime Monitoring
|
||||||
|
|
||||||
- Grafana - ${REMOTE_SERVER_HOSTNAME}:
|
- Grafana - your-remote-server :
|
||||||
icon: grafana.png
|
icon: grafana.png
|
||||||
href: https://grafana.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://grafana.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Metrics Dashboard
|
description: your-remote-server - Metrics Dashboard
|
||||||
|
|
||||||
- Prometheus - ${REMOTE_SERVER_HOSTNAME}:
|
- Prometheus - your-remote-server :
|
||||||
icon: prometheus.png
|
icon: prometheus.png
|
||||||
href: https://prometheus.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://prometheus.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Metrics Collection
|
description: your-remote-server - Metrics Collection
|
||||||
|
|
||||||
- Uptime Kuma - ${REMOTE_SERVER_HOSTNAME}:
|
- Uptime Kuma - your-remote-server :
|
||||||
icon: uptime-kuma.png
|
icon: uptime-kuma.png
|
||||||
href: https://status.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://status.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Uptime Monitoring
|
description: your-remote-server - Uptime Monitoring
|
||||||
|
|
||||||
- Media:
|
- Media:
|
||||||
- Jellyfin:
|
- Jellyfin:
|
||||||
@@ -243,20 +243,20 @@
|
|||||||
href: https://backrest.kelinreij.duckdns.org
|
href: https://backrest.kelinreij.duckdns.org
|
||||||
description: Backup Solution
|
description: Backup Solution
|
||||||
|
|
||||||
- Backrest - ${REMOTE_SERVER_HOSTNAME}:
|
- Backrest - your-remote-server :
|
||||||
icon: mdi-backup-restore
|
icon: mdi-backup-restore
|
||||||
href: https://backrest.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://backrest.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Backup Solution
|
description: your-remote-server - Backup Solution
|
||||||
|
|
||||||
- Duplicati:
|
- Duplicati:
|
||||||
icon: duplicati.png
|
icon: duplicati.png
|
||||||
href: https://duplicati.kelinreij.duckdns.org
|
href: https://duplicati.kelinreij.duckdns.org
|
||||||
description: Backup Software
|
description: Backup Software
|
||||||
|
|
||||||
- Duplicati - ${REMOTE_SERVER_HOSTNAME}:
|
- Duplicati - your-remote-server :
|
||||||
icon: duplicati.png
|
icon: duplicati.png
|
||||||
href: https://duplicati.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://duplicati.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Backup Software
|
description: your-remote-server - Backup Software
|
||||||
|
|
||||||
- Metrics:
|
- Metrics:
|
||||||
- Grafana:
|
- Grafana:
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
# Alternative Services Stack
|
# Alternative Services Stack
|
||||||
# This stack contains alternative/optional services that are not deployed by default
|
# 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
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
@@ -10,8 +7,6 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
# Portainer - Docker management UI (Alternative to Dockge)
|
# Portainer - Docker management UI (Alternative to Dockge)
|
||||||
# Access at: https://portainer.kelinreij.duckdns.org
|
|
||||||
# NOTE: Dockge is the default Docker management UI. Deploy Portainer only if you prefer its interface
|
|
||||||
# Docker management interface should always run when deployed
|
# Docker management interface should always run when deployed
|
||||||
portainer:
|
portainer:
|
||||||
image: portainer/portainer-ce:2.19.4
|
image: portainer/portainer-ce:2.19.4
|
||||||
@@ -35,14 +30,14 @@ services:
|
|||||||
- "homelab.description=Docker container management UI (Alternative to Dockge)"
|
- "homelab.description=Docker container management UI (Alternative to Dockge)"
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.portainer.rule=Host(`portainer.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.portainer.entrypoints=websecure"
|
- "traefik.http.routers.portainer.entrypoints=websecure"
|
||||||
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.portainer.middlewares=authelia@docker"
|
- "traefik.http.routers.portainer.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||||
|
|
||||||
# Authentik - Alternative SSO/Identity Provider with Web UI
|
# Authentik - Alternative SSO/Identity Provider with Web UI
|
||||||
# Access at: https://authentik.kelinreij.duckdns.org
|
# Access at: https://authentik.${DOMAIN}
|
||||||
# NOTE: Authelia is the default SSO. Deploy Authentik only if you need a web UI for user management
|
# 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
|
# WARNING: Do not run both Authelia and Authentik at the same time
|
||||||
# SSO service should always run when deployed as alternative to Authelia
|
# SSO service should always run when deployed as alternative to Authelia
|
||||||
@@ -75,7 +70,7 @@ services:
|
|||||||
- "homelab.description=SSO/Identity provider with web UI (Alternative to Authelia)"
|
- "homelab.description=SSO/Identity provider with web UI (Alternative to Authelia)"
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.authentik.rule=Host(`authentik.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.authentik.rule=Host(`authentik.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.authentik.entrypoints=websecure"
|
- "traefik.http.routers.authentik.entrypoints=websecure"
|
||||||
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.authentik.middlewares=authelia@docker"
|
- "traefik.http.routers.authentik.middlewares=authelia@docker"
|
||||||
@@ -165,9 +160,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
# Plex Media Server - Alternative to Jellyfin
|
# Plex Media Server - Alternative to Jellyfin
|
||||||
# Access at: https://plex.yourdomain.duckdns.org
|
|
||||||
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
|
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
|
||||||
# Media server should always run when deployed as alternative to Jellyfin
|
|
||||||
plex:
|
plex:
|
||||||
image: plexinc/pms-docker:1.40.0.7998-f68041501
|
image: plexinc/pms-docker:1.40.0.7998-f68041501
|
||||||
container_name: plex
|
container_name: plex
|
||||||
@@ -214,12 +207,12 @@ services:
|
|||||||
# Traefik labels - NO Authelia for app access
|
# Traefik labels - NO Authelia for app access
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.plex.rule=Host(`plex.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.plex.rule=Host(`plex.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.plex.entrypoints=websecure"
|
- "traefik.http.routers.plex.entrypoints=websecure"
|
||||||
- "traefik.http.routers.plex.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.plex.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.services.plex.loadbalancer.server.port=32400"
|
- "traefik.http.services.plex.loadbalancer.server.port=32400"
|
||||||
- "x-dockge.url=https://plex.kelinreij.duckdns.org"
|
- "x-dockge.url=https://plex.${DOMAIN}"
|
||||||
- "x-dockge.url=https://plex.kelinreij.duckdns.org"
|
- "x-dockge.url=https://plex.${DOMAIN}"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
portainer-data:
|
portainer-data:
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
###############################################################
|
###############################################################
|
||||||
|
|
||||||
users:
|
users:
|
||||||
kelin:
|
${DEFAULT_USER}:
|
||||||
displayname: "Admin User"
|
displayname: "Admin User"
|
||||||
password: "$argon2id$v=19$m=65536,t=3,p=4$a+3pIrywP/li9wy9J6UkMA$+3THyJiAnS/gNYnLaYtlsRCaYfgnnxsUyGZ4D3xGnUg"
|
password: "${AUTHELIA_ADMIN_PASSWORD_HASH}"
|
||||||
email: kelinshomelab@gmail.com
|
email: ${DEFAULT_EMAIL}
|
||||||
groups:
|
groups:
|
||||||
- admins
|
- admins
|
||||||
- users
|
- users
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
# Core Infrastructure Services
|
# Core Infrastructure Services
|
||||||
# These services form the foundation of the homelab and should always be running
|
|
||||||
# Place in /opt/stacks/core/docker-compose.yml
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
@@ -50,11 +47,8 @@ services:
|
|||||||
# Service metadata
|
# Service metadata
|
||||||
- "homelab.category=core"
|
- "homelab.category=core"
|
||||||
- "homelab.description=Reverse proxy and SSL termination"
|
- "homelab.description=Reverse proxy and SSL termination"
|
||||||
# Traefik reverse proxy (comment/uncomment to disable/enable)
|
|
||||||
# If Traefik is on a remote server: these labels are NOT USED;
|
|
||||||
# configure external yml files in /traefik/dynamic folder instead.
|
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.traefik.rule=Host(`traefik.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.traefik.entrypoints=websecure"
|
- "traefik.http.routers.traefik.entrypoints=websecure"
|
||||||
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.traefik.middlewares=authelia@docker"
|
- "traefik.http.routers.traefik.middlewares=authelia@docker"
|
||||||
@@ -86,13 +80,13 @@ services:
|
|||||||
# If Traefik is on a remote server: these labels are NOT USED;
|
# If Traefik is on a remote server: these labels are NOT USED;
|
||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.authelia.rule=Host(`auth.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.authelia.entrypoints=websecure"
|
- "traefik.http.routers.authelia.entrypoints=websecure"
|
||||||
- "traefik.http.routers.authelia.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.authelia.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.authelia.service=authelia"
|
- "traefik.http.routers.authelia.service=authelia"
|
||||||
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
|
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
|
||||||
# Authelia forward auth middleware configuration
|
# Authelia forward auth middleware configuration
|
||||||
- "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.kelinreij.duckdns.org/"
|
- "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.${DOMAIN}/"
|
||||||
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=X-Secret"
|
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=X-Secret"
|
||||||
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
|
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
|
||||||
|
|
||||||
@@ -133,7 +127,7 @@ networks:
|
|||||||
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
- https://auth.kelinreij.duckdns.org
|
- https://auth.${DOMAIN}
|
||||||
- http://192.168.4.11:9091
|
- http://192.168.4.11:9091
|
||||||
- https://traefik.kelinreij.duckdns.org
|
- https://traefik.${DOMAIN}
|
||||||
- http://192.168.4.11:8080
|
- http://192.168.4.11:8080
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
# Dashboard Services
|
# Dashboard Services
|
||||||
# Homepage and Homarr for homelab dashboards
|
|
||||||
|
|
||||||
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
||||||
|
# RESTART POLICY GUIDE:
|
||||||
# Service Access URLs:
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - Homepage: https://homepage.${DOMAIN}
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
# - Homarr: https://homarr.${DOMAIN}
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Homepage - Default Application Dashboard
|
# Homepage - Default Application Dashboard
|
||||||
@@ -61,7 +59,6 @@ services:
|
|||||||
|
|
||||||
# Homarr - Modern dashboard
|
# Homarr - Modern dashboard
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
|
|
||||||
homarr:
|
homarr:
|
||||||
image: ghcr.io/ajnart/homarr:latest
|
image: ghcr.io/ajnart/homarr:latest
|
||||||
deploy:
|
deploy:
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
href: https://jasper.kelinreij.duckdns.org
|
href: https://jasper.kelinreij.duckdns.org
|
||||||
description: Main Server
|
description: Main Server
|
||||||
|
|
||||||
- Dockge - ${REMOTE_SERVER_HOSTNAME}:
|
- Dockge - your-remote-server :
|
||||||
icon: dockge.png
|
icon: dockge.png
|
||||||
href: https://${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://your-remote-server .kelinreij.duckdns.org
|
||||||
description: Raspberry Pi Authentication Server
|
description: Raspberry Pi Authentication Server
|
||||||
|
|
||||||
- Core:
|
- Core:
|
||||||
@@ -46,18 +46,18 @@
|
|||||||
|
|
||||||
- Dozzle:
|
- Dozzle:
|
||||||
icon: dozzle.png
|
icon: dozzle.png
|
||||||
href: https://dozzle.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://dozzle.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - Real-time Log Viewer
|
description: your-remote-server - Real-time Log Viewer
|
||||||
|
|
||||||
- Glances - jasper:
|
- Glances - jasper:
|
||||||
icon: glances.png
|
icon: glances.png
|
||||||
href: https://glances.jasper.kelinreij.duckdns.org
|
href: https://glances.jasper.kelinreij.duckdns.org
|
||||||
description: jasper - System Monitoring
|
description: jasper - System Monitoring
|
||||||
|
|
||||||
- Glances - ${REMOTE_SERVER_HOSTNAME}:
|
- Glances - your-remote-server :
|
||||||
icon: glances.png
|
icon: glances.png
|
||||||
href: https://glances.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org
|
href: https://glances.your-remote-server .kelinreij.duckdns.org
|
||||||
description: ${REMOTE_SERVER_HOSTNAME} - System Monitoring
|
description: your-remote-server - System Monitoring
|
||||||
|
|
||||||
- Uptime Kuma:
|
- Uptime Kuma:
|
||||||
icon: uptime-kuma.png
|
icon: uptime-kuma.png
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
# Dockge Stack
|
# Dockge Stack
|
||||||
# Docker Compose Stack Manager
|
|
||||||
# Place in /opt/dockge/docker-compose.yml
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
# - See individual service comments for specific reasoning
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
# Service Access URLs:
|
|
||||||
# - Dockge: https://dockge.kelinreij.duckdns.org
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Dockge - Docker Compose Stack Manager (PRIMARY - preferred over Portainer)
|
# Dockge - Docker Compose Stack Manager
|
||||||
# Access at: https://dockge.kelinreij.duckdns.org
|
|
||||||
# Stack management interface should always run for container management
|
# Stack management interface should always run for container management
|
||||||
dockge:
|
dockge:
|
||||||
image: louislam/dockge:1
|
image: louislam/dockge:1
|
||||||
@@ -51,7 +44,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.dockge.rule=Host(`dockge.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.dockge.rule=Host(`dockge.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.dockge.entrypoints=websecure"
|
- "traefik.http.routers.dockge.entrypoints=websecure"
|
||||||
- "traefik.http.routers.dockge.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.dockge.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.dockge.middlewares=authelia@docker"
|
- "traefik.http.routers.dockge.middlewares=authelia@docker"
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
# Home Assistant and IoT Services
|
# Home Assistant and IoT Services
|
||||||
# Home automation platform and related tools
|
# RESTART POLICY GUIDE:
|
||||||
# Place in /opt/stacks/homeassistant/docker-compose.yml
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
# Service Access URLs:
|
# - See individual service comments for specific reasoning
|
||||||
# - Home Assistant: https://ha.kelinreij.duckdns.org (configure via Traefik file provider - uses host network)
|
|
||||||
# - ESPHome: https://esphome.kelinreij.duckdns.org
|
|
||||||
# - Node-RED: https://nodered.kelinreij.duckdns.org
|
|
||||||
# - Mosquitto MQTT: mqtt://server-ip:1883 (no web UI)
|
|
||||||
# - Zigbee2MQTT: https://zigbee2mqtt.kelinreij.duckdns.org (requires USB adapter)
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Home Assistant - Home automation platform
|
# Home Assistant - Home automation platform
|
||||||
# Access at: https://ha.kelinreij.duckdns.org
|
|
||||||
# NOTE: No Authelia - HA has its own authentication
|
# NOTE: No Authelia - HA has its own authentication
|
||||||
homeassistant:
|
homeassistant:
|
||||||
image: ghcr.io/home-assistant/home-assistant:2024.1
|
image: ghcr.io/home-assistant/home-assistant:2024.1
|
||||||
@@ -40,7 +34,6 @@ services:
|
|||||||
# Use Traefik's file provider or external host routing
|
# Use Traefik's file provider or external host routing
|
||||||
|
|
||||||
# ESPHome - ESP8266/ESP32 firmware manager
|
# ESPHome - ESP8266/ESP32 firmware manager
|
||||||
# Access at: https://esphome.kelinreij.duckdns.org
|
|
||||||
esphome:
|
esphome:
|
||||||
image: ghcr.io/esphome/esphome:latest
|
image: ghcr.io/esphome/esphome:latest
|
||||||
deploy:
|
deploy:
|
||||||
@@ -77,14 +70,13 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.esphome.rule=Host(`esphome.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.esphome.rule=Host(`esphome.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.esphome.entrypoints=websecure"
|
- "traefik.http.routers.esphome.entrypoints=websecure"
|
||||||
- "traefik.http.routers.esphome.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.esphome.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.esphome.middlewares=authelia@docker"
|
- "traefik.http.routers.esphome.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.esphome.loadbalancer.server.port=6052"
|
- "traefik.http.services.esphome.loadbalancer.server.port=6052"
|
||||||
|
|
||||||
# TasmoAdmin - Tasmota device manager
|
# TasmoAdmin - Tasmota device manager
|
||||||
# Access at: https://tasmoadmin.kelinreij.duckdns.org
|
|
||||||
tasmoadmin:
|
tasmoadmin:
|
||||||
image: ghcr.io/tasmoadmin/tasmoadmin:latest
|
image: ghcr.io/tasmoadmin/tasmoadmin:latest
|
||||||
container_name: tasmoadmin
|
container_name: tasmoadmin
|
||||||
@@ -109,14 +101,13 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.tasmoadmin.rule=Host(`tasmoadmin.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.tasmoadmin.rule=Host(`tasmoadmin.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.tasmoadmin.entrypoints=websecure"
|
- "traefik.http.routers.tasmoadmin.entrypoints=websecure"
|
||||||
- "traefik.http.routers.tasmoadmin.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.tasmoadmin.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.tasmoadmin.middlewares=authelia@docker"
|
- "traefik.http.routers.tasmoadmin.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.tasmoadmin.loadbalancer.server.port=80"
|
- "traefik.http.services.tasmoadmin.loadbalancer.server.port=80"
|
||||||
|
|
||||||
# MotionEye - Video surveillance
|
# MotionEye - Video surveillance
|
||||||
# Access at: https://motioneye.kelinreij.duckdns.org
|
|
||||||
motioneye:
|
motioneye:
|
||||||
image: ccrisan/motioneye:master-amd64
|
image: ccrisan/motioneye:master-amd64
|
||||||
container_name: motioneye
|
container_name: motioneye
|
||||||
@@ -125,7 +116,7 @@ services:
|
|||||||
- homelab-network
|
- homelab-network
|
||||||
- traefik-network
|
- traefik-network
|
||||||
ports:
|
ports:
|
||||||
- "8765:8765" # Optional: direct access
|
- "8765:8765"
|
||||||
volumes:
|
volumes:
|
||||||
- ./$(basename $file .yml)/config:/etc/motioneye
|
- ./$(basename $file .yml)/config:/etc/motioneye
|
||||||
- /mnt/surveillance:/var/lib/motioneye # Large video files on separate drive
|
- /mnt/surveillance:/var/lib/motioneye # Large video files on separate drive
|
||||||
@@ -142,14 +133,13 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.motioneye.rule=Host(`motioneye.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.motioneye.rule=Host(`motioneye.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.motioneye.entrypoints=websecure"
|
- "traefik.http.routers.motioneye.entrypoints=websecure"
|
||||||
- "traefik.http.routers.motioneye.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.motioneye.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.motioneye.middlewares=authelia@docker"
|
- "traefik.http.routers.motioneye.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.motioneye.loadbalancer.server.port=8765"
|
- "traefik.http.services.motioneye.loadbalancer.server.port=8765"
|
||||||
|
|
||||||
# Node-RED - Flow-based automation (Home Assistant addon alternative)
|
# Node-RED - Flow-based automation (Home Assistant addon alternative)
|
||||||
# Access at: https://nodered.kelinreij.duckdns.org
|
|
||||||
nodered:
|
nodered:
|
||||||
image: nodered/node-red:latest
|
image: nodered/node-red:latest
|
||||||
deploy:
|
deploy:
|
||||||
@@ -183,7 +173,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.nodered.rule=Host(`nodered.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.nodered.rule=Host(`nodered.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.nodered.entrypoints=websecure"
|
- "traefik.http.routers.nodered.entrypoints=websecure"
|
||||||
- "traefik.http.routers.nodered.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.nodered.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.nodered.middlewares=authelia@docker"
|
- "traefik.http.routers.nodered.middlewares=authelia@docker"
|
||||||
@@ -209,7 +199,6 @@ services:
|
|||||||
- "homelab.description=MQTT message broker"
|
- "homelab.description=MQTT message broker"
|
||||||
|
|
||||||
# Zigbee2MQTT - Zigbee to MQTT bridge (DISABLED - requires USB adapter)
|
# Zigbee2MQTT - Zigbee to MQTT bridge (DISABLED - requires USB adapter)
|
||||||
# Access at: https://zigbee2mqtt.kelinreij.duckdns.org
|
|
||||||
# NOTE: Requires USB Zigbee adapter (e.g., ConBee II, Sonoff ZBDongle)
|
# NOTE: Requires USB Zigbee adapter (e.g., ConBee II, Sonoff ZBDongle)
|
||||||
# Uncomment after connecting adapter
|
# Uncomment after connecting adapter
|
||||||
# zigbee2mqtt:
|
# zigbee2mqtt:
|
||||||
@@ -233,7 +222,7 @@ services:
|
|||||||
# - "homelab.category=iot"
|
# - "homelab.category=iot"
|
||||||
# - "homelab.description=Zigbee to MQTT bridge"
|
# - "homelab.description=Zigbee to MQTT bridge"
|
||||||
# - "traefik.enable=true"
|
# - "traefik.enable=true"
|
||||||
# - "traefik.http.routers.zigbee2mqtt.rule=Host(`zigbee2mqtt.kelinreij.duckdns.org`)"
|
# - "traefik.http.routers.zigbee2mqtt.rule=Host(`zigbee2mqtt.${DOMAIN}`)"
|
||||||
# - "traefik.http.routers.zigbee2mqtt.entrypoints=websecure"
|
# - "traefik.http.routers.zigbee2mqtt.entrypoints=websecure"
|
||||||
# - "traefik.http.routers.zigbee2mqtt.tls.certresolver=letsencrypt"
|
# - "traefik.http.routers.zigbee2mqtt.tls.certresolver=letsencrypt"
|
||||||
# - "traefik.http.routers.zigbee2mqtt.middlewares=authelia@docker"
|
# - "traefik.http.routers.zigbee2mqtt.middlewares=authelia@docker"
|
||||||
@@ -248,15 +237,15 @@ networks:
|
|||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
# Proxied URLs (through Traefik)
|
# Proxied URLs (through Traefik)
|
||||||
- https://ha.kelinreij.duckdns.org
|
- https://ha.${DOMAIN}
|
||||||
- http://192.168.4.4:8123
|
- http://192.168.4.4:8123
|
||||||
- https://esphome.kelinreij.duckdns.org
|
- https://esphome.${DOMAIN}
|
||||||
- http://192.168.4.4:6052
|
- http://192.168.4.4:6052
|
||||||
- https://tasmoadmin.kelinreij.duckdns.org
|
- https://tasmoadmin.${DOMAIN}
|
||||||
- http://192.168.4.4:8084
|
- http://192.168.4.4:8084
|
||||||
- https://motioneye.kelinreij.duckdns.org
|
- https://motioneye.${DOMAIN}
|
||||||
- http://192.168.4.4:8765
|
- http://192.168.4.4:8765
|
||||||
- https://nodered.kelinreij.duckdns.org
|
- https://nodered.${DOMAIN}
|
||||||
- http://192.168.4.4:1880
|
- http://192.168.4.4:1880
|
||||||
- mqtt://192.168.4.4:1883
|
- mqtt://192.168.4.4:1883
|
||||||
- https://zigbee2mqtt.kelinreij.duckdns.org
|
- https://zigbee2mqtt.${DOMAIN}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
# Infrastructure Services
|
# Infrastructure Services
|
||||||
# Core services that other services depend on
|
|
||||||
# Place in /opt/stacks/infrastructure/docker-compose.yml
|
|
||||||
|
|
||||||
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
@@ -41,7 +37,6 @@ services:
|
|||||||
- homelab.description=Docker socket proxy for security
|
- homelab.description=Docker socket proxy for security
|
||||||
|
|
||||||
# Pi-hole - Network-wide ad blocker and DNS server
|
# Pi-hole - Network-wide ad blocker and DNS server
|
||||||
# Access at: https://pihole.kelinreij.duckdns.org
|
|
||||||
# DNS service must always run for network-wide ad blocking
|
# DNS service must always run for network-wide ad blocking
|
||||||
pihole:
|
pihole:
|
||||||
image: pihole/pihole:2024.01.0
|
image: pihole/pihole:2024.01.0
|
||||||
@@ -87,15 +82,13 @@ services:
|
|||||||
# - This prevents conflicts between Docker labels and file provider
|
# - This prevents conflicts between Docker labels and file provider
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.pihole.rule=Host(`pihole.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.pihole.rule=Host(`pihole.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.pihole.entrypoints=websecure"
|
- "traefik.http.routers.pihole.entrypoints=websecure"
|
||||||
- "traefik.http.routers.pihole.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.pihole.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.pihole.middlewares=authelia@docker"
|
- "traefik.http.routers.pihole.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.pihole.loadbalancer.server.port=80"
|
- "traefik.http.services.pihole.loadbalancer.server.port=80"
|
||||||
|
|
||||||
# Watchtower - Automatic container updates
|
# Watchtower - Automatic container updates
|
||||||
# Monitors and updates Docker containers to latest versions
|
|
||||||
# Runs daily at 4 AM
|
|
||||||
watchtower:
|
watchtower:
|
||||||
image: containrrr/watchtower:latest
|
image: containrrr/watchtower:latest
|
||||||
container_name: watchtower
|
container_name: watchtower
|
||||||
@@ -116,7 +109,6 @@ services:
|
|||||||
- "homelab.description=Automatic Docker container updates"
|
- "homelab.description=Automatic Docker container updates"
|
||||||
|
|
||||||
# Dozzle - Real-time Docker log viewer
|
# Dozzle - Real-time Docker log viewer
|
||||||
# Access at: https://dozzle.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
dozzle:
|
dozzle:
|
||||||
image: amir20/dozzle:latest
|
image: amir20/dozzle:latest
|
||||||
@@ -157,7 +149,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.dozzle.rule=Host(`dozzle.jasper.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.dozzle.rule=Host(`dozzle.jasper.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.dozzle.entrypoints=websecure"
|
- "traefik.http.routers.dozzle.entrypoints=websecure"
|
||||||
- "traefik.http.routers.dozzle.tls=true"
|
- "traefik.http.routers.dozzle.tls=true"
|
||||||
- "traefik.http.routers.dozzle.middlewares=authelia@docker"
|
- "traefik.http.routers.dozzle.middlewares=authelia@docker"
|
||||||
@@ -169,7 +161,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Glances - System monitoring
|
# Glances - System monitoring
|
||||||
# Access at: https://glances.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 30min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 30min inactivity
|
||||||
glances:
|
glances:
|
||||||
image: nicolargo/glances:latest-full
|
image: nicolargo/glances:latest-full
|
||||||
@@ -210,7 +201,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.glances.rule=Host(`glances.jasper.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.glances.rule=Host(`glances.jasper.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.glances.entrypoints=websecure"
|
- "traefik.http.routers.glances.entrypoints=websecure"
|
||||||
- "traefik.http.routers.glances.tls=true"
|
- "traefik.http.routers.glances.tls=true"
|
||||||
- "traefik.http.routers.glances.middlewares=authelia@docker"
|
- "traefik.http.routers.glances.middlewares=authelia@docker"
|
||||||
@@ -222,7 +213,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Code Server - VS Code in browser
|
# Code Server - VS Code in browser
|
||||||
# Access at: https://code.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 30min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 30min inactivity
|
||||||
code-server:
|
code-server:
|
||||||
image: lscr.io/linuxserver/code-server:latest
|
image: lscr.io/linuxserver/code-server:latest
|
||||||
@@ -267,7 +257,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.code-server.rule=Host(`code.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.code-server.rule=Host(`code.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.code-server.entrypoints=websecure"
|
- "traefik.http.routers.code-server.entrypoints=websecure"
|
||||||
- "traefik.http.routers.code-server.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.code-server.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.code-server.middlewares=authelia@docker"
|
- "traefik.http.routers.code-server.middlewares=authelia@docker"
|
||||||
@@ -280,13 +270,13 @@ services:
|
|||||||
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
- https://pihole.kelinreij.duckdns.org
|
- https://pihole.${DOMAIN}
|
||||||
- https://192.168.4.4:53
|
- https://192.168.4.4:53
|
||||||
- https://dozzle.kelinreij.duckdns.org
|
- https://dozzle.${DOMAIN}
|
||||||
- https://192.168.4.4:8085
|
- https://192.168.4.4:8085
|
||||||
- https://glances.kelinreij.duckdns.org
|
- https://glances.${DOMAIN}
|
||||||
- https://192.168.4.4:61208
|
- https://192.168.4.4:61208
|
||||||
- https://code.kelinreij.duckdns.org
|
- https://code.${DOMAIN}
|
||||||
- https://192.168.4.4:8079
|
- https://192.168.4.4:8079
|
||||||
- http://192.168.4.4:2375 # Docker Proxy
|
- http://192.168.4.4:2375 # Docker Proxy
|
||||||
- http://192.168.4.4:19999 # Netdata
|
- http://192.168.4.4:19999 # Netdata
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
# Media Management Services
|
# Media Management Services
|
||||||
# Content automation and library management (*arr apps, transcoders, etc.)
|
|
||||||
# Place in /opt/stacks/media-management/docker-compose.yml
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
# - See individual service comments for specific reasoning
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Sonarr - TV show automation
|
|
||||||
# Access at: https://sonarr.yourdomain.duckdns.org
|
|
||||||
sonarr:
|
sonarr:
|
||||||
|
# Sonarr - TV show management and automation
|
||||||
image: linuxserver/sonarr:4.0.0
|
image: linuxserver/sonarr:4.0.0
|
||||||
container_name: sonarr
|
container_name: sonarr
|
||||||
restart: no
|
restart: no
|
||||||
@@ -45,7 +41,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.sonarr.rule=Host(`sonarr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.sonarr.rule=Host(`sonarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.sonarr.entrypoints=websecure"
|
- "traefik.http.routers.sonarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.sonarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.sonarr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.sonarr.middlewares=authelia@docker"
|
- "traefik.http.routers.sonarr.middlewares=authelia@docker"
|
||||||
@@ -55,7 +51,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Radarr - Movie automation
|
# Radarr - Movie automation
|
||||||
# Access at: https://radarr.yourdomain.duckdns.org
|
|
||||||
radarr:
|
radarr:
|
||||||
image: linuxserver/radarr:5.2.6
|
image: linuxserver/radarr:5.2.6
|
||||||
container_name: radarr
|
container_name: radarr
|
||||||
@@ -91,7 +86,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.radarr.rule=Host(`radarr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.radarr.rule=Host(`radarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.radarr.entrypoints=websecure"
|
- "traefik.http.routers.radarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.radarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.radarr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.radarr.middlewares=authelia@docker"
|
- "traefik.http.routers.radarr.middlewares=authelia@docker"
|
||||||
@@ -135,7 +130,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.prowlarr.rule=Host(`prowlarr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.prowlarr.rule=Host(`prowlarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.prowlarr.entrypoints=websecure"
|
- "traefik.http.routers.prowlarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.prowlarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.prowlarr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.prowlarr.middlewares=authelia@docker"
|
- "traefik.http.routers.prowlarr.middlewares=authelia@docker"
|
||||||
@@ -145,7 +140,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Readarr - Ebook and audiobook management
|
# Readarr - Ebook and audiobook management
|
||||||
# Access at: https://readarr.kelinreij.duckdns.org
|
|
||||||
readarr:
|
readarr:
|
||||||
image: linuxserver/readarr:0.4.19-nightly
|
image: linuxserver/readarr:0.4.19-nightly
|
||||||
container_name: readarr
|
container_name: readarr
|
||||||
@@ -175,7 +169,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.readarr.rule=Host(`readarr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.readarr.rule=Host(`readarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.readarr.entrypoints=websecure"
|
- "traefik.http.routers.readarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.readarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.readarr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.readarr.middlewares=authelia@docker"
|
- "traefik.http.routers.readarr.middlewares=authelia@docker"
|
||||||
@@ -185,7 +179,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Lidarr - Music collection manager
|
# Lidarr - Music collection manager
|
||||||
# Access at: https://lidarr.kelinreij.duckdns.org
|
|
||||||
lidarr:
|
lidarr:
|
||||||
image: linuxserver/lidarr:2.0.7
|
image: linuxserver/lidarr:2.0.7
|
||||||
container_name: lidarr
|
container_name: lidarr
|
||||||
@@ -215,7 +208,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.lidarr.rule=Host(`lidarr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.lidarr.rule=Host(`lidarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.lidarr.entrypoints=websecure"
|
- "traefik.http.routers.lidarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.lidarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.lidarr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.lidarr.middlewares=authelia@docker"
|
- "traefik.http.routers.lidarr.middlewares=authelia@docker"
|
||||||
@@ -225,7 +218,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Lazy Librarian - Book manager
|
# Lazy Librarian - Book manager
|
||||||
# Access at: https://lazylibrarian.kelinreij.duckdns.org
|
|
||||||
lazylibrarian:
|
lazylibrarian:
|
||||||
image: linuxserver/lazylibrarian:latest
|
image: linuxserver/lazylibrarian:latest
|
||||||
container_name: lazylibrarian
|
container_name: lazylibrarian
|
||||||
@@ -256,7 +248,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.lazylibrarian.rule=Host(`lazylibrarian.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.lazylibrarian.rule=Host(`lazylibrarian.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.lazylibrarian.entrypoints=websecure"
|
- "traefik.http.routers.lazylibrarian.entrypoints=websecure"
|
||||||
- "traefik.http.routers.lazylibrarian.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.lazylibrarian.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.lazylibrarian.middlewares=authelia@docker"
|
- "traefik.http.routers.lazylibrarian.middlewares=authelia@docker"
|
||||||
@@ -266,7 +258,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Mylar3 - Comic book manager
|
# Mylar3 - Comic book manager
|
||||||
# Access at: https://mylar.kelinreij.duckdns.org
|
|
||||||
mylar3:
|
mylar3:
|
||||||
image: linuxserver/mylar3:latest
|
image: linuxserver/mylar3:latest
|
||||||
container_name: mylar3
|
container_name: mylar3
|
||||||
@@ -296,7 +287,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.mylar.rule=Host(`mylar.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.mylar.rule=Host(`mylar.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.mylar.entrypoints=websecure"
|
- "traefik.http.routers.mylar.entrypoints=websecure"
|
||||||
- "traefik.http.routers.mylar.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.mylar.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.mylar.middlewares=authelia@docker"
|
- "traefik.http.routers.mylar.middlewares=authelia@docker"
|
||||||
@@ -306,7 +297,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Jellyseerr - Request management for Jellyfin/Plex
|
# Jellyseerr - Request management for Jellyfin/Plex
|
||||||
# Access at: https://jellyseerr.kelinreij.duckdns.org
|
|
||||||
jellyseerr:
|
jellyseerr:
|
||||||
image: fallenbagel/jellyseerr:latest
|
image: fallenbagel/jellyseerr:latest
|
||||||
container_name: jellyseerr
|
container_name: jellyseerr
|
||||||
@@ -339,7 +329,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.jellyseerr.rule=Host(`jellyseerr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.jellyseerr.rule=Host(`jellyseerr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.jellyseerr.entrypoints=websecure"
|
- "traefik.http.routers.jellyseerr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.jellyseerr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.jellyseerr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.jellyseerr.middlewares=authelia@docker"
|
- "traefik.http.routers.jellyseerr.middlewares=authelia@docker"
|
||||||
@@ -368,21 +358,21 @@ services:
|
|||||||
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
- https://sonarr.kelinreij.duckdns.org
|
- https://sonarr.${DOMAIN}
|
||||||
- http://192.168.4.4:8989
|
- http://192.168.4.4:8989
|
||||||
- https://radarr.kelinreij.duckdns.org
|
- https://radarr.${DOMAIN}
|
||||||
- http://192.168.4.4:7878
|
- http://192.168.4.4:7878
|
||||||
- https://prowlarr.kelinreij.duckdns.org
|
- https://prowlarr.${DOMAIN}
|
||||||
- http://192.168.4.4:9696
|
- http://192.168.4.4:9696
|
||||||
- https://readarr.kelinreij.duckdns.org
|
- https://readarr.${DOMAIN}
|
||||||
- http://192.168.4.4:8787
|
- http://192.168.4.4:8787
|
||||||
- https://lidarr.kelinreij.duckdns.org
|
- https://lidarr.${DOMAIN}
|
||||||
- http://192.168.4.4:8686
|
- http://192.168.4.4:8686
|
||||||
- https://lazylibrarian.kelinreij.duckdns.org
|
- https://lazylibrarian.${DOMAIN}
|
||||||
- http://192.168.4.4:5299
|
- http://192.168.4.4:5299
|
||||||
- https://mylar.kelinreij.duckdns.org
|
- https://mylar.${DOMAIN}
|
||||||
- http://192.168.4.4:8090
|
- http://192.168.4.4:8090
|
||||||
- https://jellyseerr.kelinreij.duckdns.org
|
- https://jellyseerr.${DOMAIN}
|
||||||
- http://192.168.4.4:5055
|
- http://192.168.4.4:5055
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -1,22 +1,14 @@
|
|||||||
# Media Services
|
# Media Services
|
||||||
# Default Services for media management and streaming
|
|
||||||
# Place in /opt/stacks/media/docker-compose.yml
|
|
||||||
|
|
||||||
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
# - See individual service comments for specific reasoning
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
# Service Access URLs:
|
|
||||||
# - Jellyfin: https://jellyfin.kelinreij.duckdns.org (no SSO - app access)
|
|
||||||
# - Plex: https://plex.kelinreij.duckdns.org (no SSO - app access)
|
|
||||||
# - qBittorrent: https://qbit.kelinreij.duckdns.org (routed through Gluetun VPN)
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Jellyfin - Open-source media streaming server
|
# Jellyfin - Open-source media streaming server
|
||||||
# Access at: https://jellyfin.yourdomain.duckdns.org
|
|
||||||
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
|
# NOTE: No Authelia - allows app access from Roku, Fire TV, mobile, etc.
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
jellyfin:
|
jellyfin:
|
||||||
@@ -63,7 +55,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.jellyfin.entrypoints=websecure"
|
- "traefik.http.routers.jellyfin.entrypoints=websecure"
|
||||||
- "traefik.http.routers.jellyfin.tls=true"
|
- "traefik.http.routers.jellyfin.tls=true"
|
||||||
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
|
||||||
@@ -76,7 +68,7 @@ services:
|
|||||||
- "sablier.theme=hacker-terminal"
|
- "sablier.theme=hacker-terminal"
|
||||||
|
|
||||||
# Calibre-Web - Ebook reader and server
|
# Calibre-Web - Ebook reader and server
|
||||||
# Access at: https://calibre.kelinreij.duckdns.org
|
# Access at: https://calibre.${DOMAIN}
|
||||||
calibre-web:
|
calibre-web:
|
||||||
image: lscr.io/linuxserver/calibre-web:latest
|
image: lscr.io/linuxserver/calibre-web:latest
|
||||||
deploy:
|
deploy:
|
||||||
@@ -112,7 +104,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.calibre.rule=Host(`calibre.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.calibre.rule=Host(`calibre.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.calibre.entrypoints=websecure"
|
- "traefik.http.routers.calibre.entrypoints=websecure"
|
||||||
- "traefik.http.routers.calibre.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.calibre.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.calibre.middlewares=authelia@docker"
|
- "traefik.http.routers.calibre.middlewares=authelia@docker"
|
||||||
@@ -123,15 +115,12 @@ services:
|
|||||||
- "sablier.group=jasper-calibre-web"
|
- "sablier.group=jasper-calibre-web"
|
||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# DOCKGE URL CONFIGURATION
|
|
||||||
# ==========================================
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
# Proxied URLs (through Traefik)
|
# Proxied URLs (through Traefik)
|
||||||
- https://jellyfin.kelinreij.duckdns.org
|
- https://jellyfin.${DOMAIN}
|
||||||
- http://192.168.4.4:8096
|
- http://192.168.4.4:8096
|
||||||
- https://calibre.kelinreij.duckdns.org
|
- https://calibre.${DOMAIN}
|
||||||
- http://192.168.4.4:8083
|
- http://192.168.4.4:8083
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -1,25 +1,11 @@
|
|||||||
# Monitoring and Observability Services
|
# Monitoring and Observability Services
|
||||||
# Services for monitoring your homelab infrastructure
|
|
||||||
# Place in /opt/stacks/monitoring/docker-compose.yml
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
# - See individual service comments for specific reasoning
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
# Service Access URLs:
|
|
||||||
# - Prometheus: http://192.168.4.4:9090 (or configure Traefik)
|
|
||||||
# - Grafana: http://192.168.4.4:3000 (or configure Traefik)
|
|
||||||
# - Uptime Kuma: https://status.kelinreij.duckdns.org
|
|
||||||
# - Node Exporter: http://192.168.4.4:9100/metrics
|
|
||||||
# - cAdvisor: http://192.168.4.4:8082
|
|
||||||
# - Loki: http://192.168.4.4:3100
|
|
||||||
# NOTE: Prometheus, Grafana, Loki use ports because they need to be accessible to other services
|
|
||||||
# Add Traefik labels if you want https://prometheus.kelinreij.duckdns.org access
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Prometheus - Metrics collection and storage
|
# Prometheus - Metrics collection and storage
|
||||||
# Access at: http://192.168.4.4:9090
|
|
||||||
prometheus:
|
prometheus:
|
||||||
image: prom/prometheus:v2.48.1
|
image: prom/prometheus:v2.48.1
|
||||||
deploy:
|
deploy:
|
||||||
@@ -59,7 +45,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.prometheus.rule=Host(`prometheus.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.prometheus.rule=Host(`prometheus.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.prometheus.entrypoints=websecure"
|
- "traefik.http.routers.prometheus.entrypoints=websecure"
|
||||||
- "traefik.http.routers.prometheus.tls=true"
|
- "traefik.http.routers.prometheus.tls=true"
|
||||||
- "traefik.http.routers.prometheus.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.prometheus.tls.certresolver=letsencrypt"
|
||||||
@@ -67,7 +53,6 @@ services:
|
|||||||
- "traefik.http.services.prometheus.loadbalancer.server.port=9090"
|
- "traefik.http.services.prometheus.loadbalancer.server.port=9090"
|
||||||
|
|
||||||
# Grafana - Metrics visualization
|
# Grafana - Metrics visualization
|
||||||
# Access at: http://192.168.4.4:3000
|
|
||||||
# Default credentials: admin / admin (change on first login)
|
# Default credentials: admin / admin (change on first login)
|
||||||
grafana:
|
grafana:
|
||||||
image: grafana/grafana:10.2.3
|
image: grafana/grafana:10.2.3
|
||||||
@@ -93,7 +78,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
|
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
|
||||||
- GF_USERS_ALLOW_SIGN_UP=false
|
- GF_USERS_ALLOW_SIGN_UP=false
|
||||||
- GF_SERVER_ROOT_URL=https://grafana.kelinreij.duckdns.org
|
- GF_SERVER_ROOT_URL=https://grafana.${DOMAIN}
|
||||||
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource,grafana-piechart-panel
|
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource,grafana-piechart-panel
|
||||||
user: "1000:1000"
|
user: "1000:1000"
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -109,7 +94,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.grafana.rule=Host(`grafana.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.grafana.rule=Host(`grafana.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.grafana.entrypoints=websecure"
|
- "traefik.http.routers.grafana.entrypoints=websecure"
|
||||||
- "traefik.http.routers.grafana.tls=true"
|
- "traefik.http.routers.grafana.tls=true"
|
||||||
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
|
||||||
@@ -170,7 +155,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.cadvisor.rule=Host(`cadvisor.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.cadvisor.rule=Host(`cadvisor.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.cadvisor.entrypoints=websecure"
|
- "traefik.http.routers.cadvisor.entrypoints=websecure"
|
||||||
- "traefik.http.routers.cadvisor.tls=true"
|
- "traefik.http.routers.cadvisor.tls=true"
|
||||||
- "traefik.http.routers.cadvisor.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.cadvisor.tls.certresolver=letsencrypt"
|
||||||
@@ -178,7 +163,6 @@ services:
|
|||||||
- "traefik.http.services.cadvisor.loadbalancer.server.port=8080"
|
- "traefik.http.services.cadvisor.loadbalancer.server.port=8080"
|
||||||
|
|
||||||
# Uptime Kuma - Uptime monitoring
|
# Uptime Kuma - Uptime monitoring
|
||||||
# Access at: https://uptime-kuma.kelinreij.duckdns.org
|
|
||||||
uptime-kuma:
|
uptime-kuma:
|
||||||
image: louislam/uptime-kuma:1
|
image: louislam/uptime-kuma:1
|
||||||
deploy:
|
deploy:
|
||||||
@@ -211,7 +195,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime-kuma.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime-kuma.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
|
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
|
||||||
- "traefik.http.routers.uptime-kuma.tls=true"
|
- "traefik.http.routers.uptime-kuma.tls=true"
|
||||||
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
|
||||||
@@ -253,7 +237,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.loki.rule=Host(`loki.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.loki.rule=Host(`loki.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.loki.entrypoints=websecure"
|
- "traefik.http.routers.loki.entrypoints=websecure"
|
||||||
- "traefik.http.routers.loki.tls=true"
|
- "traefik.http.routers.loki.tls=true"
|
||||||
- "traefik.http.routers.loki.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.loki.tls.certresolver=letsencrypt"
|
||||||
@@ -300,7 +284,7 @@ x-dockge:
|
|||||||
# Proxied URLs (through Traefik)
|
# Proxied URLs (through Traefik)
|
||||||
- http://192.168.4.4:9090
|
- http://192.168.4.4:9090
|
||||||
- http://192.168.4.4:3000
|
- http://192.168.4.4:3000
|
||||||
- https://uptime-kuma.kelinreij.duckdns.org
|
- https://uptime-kuma.${DOMAIN}
|
||||||
- http://192.168.4.4:9100/metrics
|
- http://192.168.4.4:9100/metrics
|
||||||
- http://192.168.4.4:8082
|
- http://192.168.4.4:8082
|
||||||
- http://192.168.4.4:3100
|
- http://192.168.4.4:3100
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
# Productivity and Content Management Services
|
# Productivity and Content Management Services
|
||||||
# Place in /opt/stacks/productivity/docker-compose.yml
|
|
||||||
|
|
||||||
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
# SABLIER SESSION DURATION: Set to 5m for testing. Increase to 30m for production in config-templates/traefik/dynamic/sablier.yml
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
@@ -10,7 +7,6 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
# Nextcloud - File sync and collaboration
|
# Nextcloud - File sync and collaboration
|
||||||
# Access at: https://nextcloud.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
nextcloud:
|
nextcloud:
|
||||||
image: nextcloud:28
|
image: nextcloud:28
|
||||||
@@ -40,10 +36,10 @@ services:
|
|||||||
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
|
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
|
||||||
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
|
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
|
||||||
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
|
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
|
||||||
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.kelinreij.duckdns.org
|
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.${DOMAIN}
|
||||||
- TRUSTED_PROXIES=172.18.0.0/16
|
- TRUSTED_PROXIES=172.18.0.0/16
|
||||||
- OVERWRITEPROTOCOL=https
|
- OVERWRITEPROTOCOL=https
|
||||||
- OVERWRITEHOST=nextcloud.kelinreij.duckdns.org
|
- OVERWRITEHOST=nextcloud.${DOMAIN}
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost/status.php"]
|
test: ["CMD", "curl", "-f", "http://localhost/status.php"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -61,7 +57,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.nextcloud.rule=Host(`nextcloud.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.nextcloud.entrypoints=websecure"
|
- "traefik.http.routers.nextcloud.entrypoints=websecure"
|
||||||
- "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.nextcloud.middlewares=authelia@docker"
|
- "traefik.http.routers.nextcloud.middlewares=authelia@docker"
|
||||||
@@ -91,7 +87,6 @@ services:
|
|||||||
- "homelab.description=Nextcloud database"
|
- "homelab.description=Nextcloud database"
|
||||||
|
|
||||||
# Mealie - Recipe manager
|
# Mealie - Recipe manager
|
||||||
# Access at: https://mealie.kelinreij.duckdns.org
|
|
||||||
mealie:
|
mealie:
|
||||||
image: ghcr.io/mealie-recipes/mealie:latest
|
image: ghcr.io/mealie-recipes/mealie:latest
|
||||||
container_name: mealie
|
container_name: mealie
|
||||||
@@ -107,7 +102,7 @@ services:
|
|||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- TZ=America/New_York
|
- TZ=America/New_York
|
||||||
- BASE_URL=https://mealie.kelinreij.duckdns.org
|
- BASE_URL=https://mealie.${DOMAIN}
|
||||||
- DB_ENGINE=sqlite
|
- DB_ENGINE=sqlite
|
||||||
labels:
|
labels:
|
||||||
# TRAEFIK CONFIGURATION
|
# TRAEFIK CONFIGURATION
|
||||||
@@ -118,7 +113,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.mealie.rule=Host(`mealie.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.mealie.rule=Host(`mealie.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.mealie.entrypoints=websecure"
|
- "traefik.http.routers.mealie.entrypoints=websecure"
|
||||||
- "traefik.http.routers.mealie.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.mealie.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.mealie.middlewares=authelia@docker"
|
- "traefik.http.routers.mealie.middlewares=authelia@docker"
|
||||||
@@ -130,7 +125,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# WordPress - Blog/website platform
|
# WordPress - Blog/website platform
|
||||||
# Access at: https://blog.kelinreij.duckdns.org
|
|
||||||
wordpress:
|
wordpress:
|
||||||
image: wordpress:latest
|
image: wordpress:latest
|
||||||
container_name: wordpress
|
container_name: wordpress
|
||||||
@@ -164,7 +158,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.wordpress.rule=Host(`wordpress.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.wordpress.rule=Host(`wordpress.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.wordpress.entrypoints=websecure"
|
- "traefik.http.routers.wordpress.entrypoints=websecure"
|
||||||
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.wordpress.middlewares=authelia@docker"
|
- "traefik.http.routers.wordpress.middlewares=authelia@docker"
|
||||||
@@ -193,7 +187,6 @@ services:
|
|||||||
- "homelab.description=WordPress database"
|
- "homelab.description=WordPress database"
|
||||||
|
|
||||||
# Gitea - Self-hosted Git service
|
# Gitea - Self-hosted Git service
|
||||||
# Access at: https://git.kelinreij.duckdns.org
|
|
||||||
gitea:
|
gitea:
|
||||||
image: gitea/gitea:latest
|
image: gitea/gitea:latest
|
||||||
deploy:
|
deploy:
|
||||||
@@ -241,7 +234,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.gitea.rule=Host(`gitea.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.gitea.rule=Host(`gitea.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.gitea.entrypoints=websecure"
|
- "traefik.http.routers.gitea.entrypoints=websecure"
|
||||||
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.gitea.middlewares=authelia@docker"
|
- "traefik.http.routers.gitea.middlewares=authelia@docker"
|
||||||
@@ -270,7 +263,6 @@ services:
|
|||||||
|
|
||||||
|
|
||||||
# Jupyter Lab - Interactive computing notebooks
|
# Jupyter Lab - Interactive computing notebooks
|
||||||
# Access at: https://jupyter.kelinreij.duckdns.org
|
|
||||||
# Token displayed in logs on first start
|
# Token displayed in logs on first start
|
||||||
jupyter:
|
jupyter:
|
||||||
image: jupyter/scipy-notebook:latest
|
image: jupyter/scipy-notebook:latest
|
||||||
@@ -307,7 +299,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.jupyter.rule=Host(`jupyter.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.jupyter.rule=Host(`jupyter.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.jupyter.entrypoints=websecure"
|
- "traefik.http.routers.jupyter.entrypoints=websecure"
|
||||||
- "traefik.http.routers.jupyter.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.jupyter.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.jupyter.middlewares=authelia@docker"
|
- "traefik.http.routers.jupyter.middlewares=authelia@docker"
|
||||||
@@ -331,13 +323,13 @@ networks:
|
|||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
# Proxied URLs (through Traefik)
|
# Proxied URLs (through Traefik)
|
||||||
- https://nextcloud.kelinreij.duckdns.org
|
- https://nextcloud.${DOMAIN}
|
||||||
- https://192.168.4.4:8089
|
- https://192.168.4.4:8089
|
||||||
- https://mealie.kelinreij.duckdns.org
|
- https://mealie.${DOMAIN}
|
||||||
- https://192.168.4.4:9000
|
- https://192.168.4.4:9000
|
||||||
- https://wordpress.kelinreij.duckdns.org
|
- https://wordpress.${DOMAIN}
|
||||||
- https://192.168.4.4:8088
|
- https://192.168.4.4:8088
|
||||||
- https://gitea.kelinreij.duckdns.org
|
- https://gitea.${DOMAIN}
|
||||||
- https://192.168.4.4:3010
|
- https://192.168.4.4:3010
|
||||||
- https://jupyter.kelinreij.duckdns.org
|
- https://jupyter.${DOMAIN}
|
||||||
- https://192.168.4.4:8890
|
- https://192.168.4.4:8890
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
# Transcoder Services
|
||||||
|
# RESTART POLICY GUIDE:
|
||||||
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Tdarr Server - Distributed transcoding server
|
# Tdarr Server - Distributed transcoding server
|
||||||
# Access at: https://tdarr.kelinreij.duckdns.org
|
|
||||||
tdarr-server:
|
tdarr-server:
|
||||||
image: ghcr.io/haveagitgat/tdarr:latest
|
image: ghcr.io/haveagitgat/tdarr:latest
|
||||||
container_name: tdarr-server
|
container_name: tdarr-server
|
||||||
@@ -36,7 +41,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.tdarr.rule=Host(`tdarr.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.tdarr.rule=Host(`tdarr.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.tdarr.entrypoints=websecure"
|
- "traefik.http.routers.tdarr.entrypoints=websecure"
|
||||||
- "traefik.http.routers.tdarr.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.tdarr.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.tdarr.middlewares=authelia@docker"
|
- "traefik.http.routers.tdarr.middlewares=authelia@docker"
|
||||||
@@ -75,7 +80,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Unmanic - Another transcoding option
|
# Unmanic - Another transcoding option
|
||||||
# Access at: https://unmanic.kelinreij.duckdns.org
|
|
||||||
unmanic:
|
unmanic:
|
||||||
image: josh5/unmanic:latest
|
image: josh5/unmanic:latest
|
||||||
container_name: unmanic
|
container_name: unmanic
|
||||||
@@ -105,7 +109,7 @@ services:
|
|||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
- "traefik.http.routers.unmanic.rule=Host(`unmanic.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.unmanic.rule=Host(`unmanic.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.unmanic.entrypoints=websecure"
|
- "traefik.http.routers.unmanic.entrypoints=websecure"
|
||||||
- "traefik.http.routers.unmanic.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.unmanic.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.unmanic.middlewares=authelia@docker"
|
- "traefik.http.routers.unmanic.middlewares=authelia@docker"
|
||||||
@@ -122,7 +126,7 @@ networks:
|
|||||||
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
- https://tdarr.kelinreij.duckdns.org
|
- https://tdarr.${DOMAIN}
|
||||||
- http://192.168.4.4:8265
|
- http://192.168.4.4:8265
|
||||||
- https://unmanic.kelinreij.duckdns.org
|
- https://unmanic.${DOMAIN}
|
||||||
- http://192.168.4.4:8888
|
- http://192.168.4.4:8888
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
# Backup and Utility Services
|
# Backup and Utility Services
|
||||||
# Place in /opt/stacks/utilities/docker-compose.yml
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
@@ -8,7 +6,6 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
# Backrest - Backup solution for restic
|
# Backrest - Backup solution for restic
|
||||||
# Access at: https://backrest.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
backrest:
|
backrest:
|
||||||
image: garethgeorge/backrest:latest
|
image: garethgeorge/backrest:latest
|
||||||
@@ -44,7 +41,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.backrest.rule=Host(`backrest.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.backrest.rule=Host(`backrest.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.backrest.entrypoints=websecure"
|
- "traefik.http.routers.backrest.entrypoints=websecure"
|
||||||
- "traefik.http.routers.backrest.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.backrest.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.backrest.middlewares=authelia@docker"
|
- "traefik.http.routers.backrest.middlewares=authelia@docker"
|
||||||
@@ -56,7 +53,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# Duplicati - Backup solution
|
# Duplicati - Backup solution
|
||||||
# Access at: https://duplicati.kelinreij.duckdns.org
|
|
||||||
duplicati:
|
duplicati:
|
||||||
image: lscr.io/linuxserver/duplicati:2.0.7
|
image: lscr.io/linuxserver/duplicati:2.0.7
|
||||||
container_name: duplicati
|
container_name: duplicati
|
||||||
@@ -90,7 +86,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.duplicati.rule=Host(`duplicati.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.duplicati.rule=Host(`duplicati.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.duplicati.entrypoints=websecure"
|
- "traefik.http.routers.duplicati.entrypoints=websecure"
|
||||||
- "traefik.http.routers.duplicati.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.duplicati.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.duplicati.middlewares=authelia@docker"
|
- "traefik.http.routers.duplicati.middlewares=authelia@docker"
|
||||||
@@ -134,7 +130,7 @@ services:
|
|||||||
# Traefik labels
|
# Traefik labels
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.formio.rule=Host(`forms.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.formio.rule=Host(`forms.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.formio.entrypoints=websecure"
|
- "traefik.http.routers.formio.entrypoints=websecure"
|
||||||
- "traefik.http.routers.formio.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.formio.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.formio.middlewares=authelia@docker"
|
- "traefik.http.routers.formio.middlewares=authelia@docker"
|
||||||
@@ -156,7 +152,6 @@ services:
|
|||||||
- "homelab.description=Form.io database"
|
- "homelab.description=Form.io database"
|
||||||
|
|
||||||
# Bitwarden (Vaultwarden) - Password manager
|
# Bitwarden (Vaultwarden) - Password manager
|
||||||
# Access at: https://vault.kelinreij.duckdns.org
|
|
||||||
# Note: SSO disabled for browser extension and mobile app compatibility
|
# Note: SSO disabled for browser extension and mobile app compatibility
|
||||||
|
|
||||||
vaultwarden:
|
vaultwarden:
|
||||||
@@ -171,7 +166,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./vaultwarden/data:/data
|
- ./vaultwarden/data:/data
|
||||||
environment:
|
environment:
|
||||||
- DOMAIN=https://vault.kelinreij.duckdns.org
|
- DOMAIN=https://vault.${DOMAIN}
|
||||||
- SIGNUPS_ALLOWED=${BITWARDEN_SIGNUPS_ALLOWED}
|
- SIGNUPS_ALLOWED=${BITWARDEN_SIGNUPS_ALLOWED}
|
||||||
- INVITATIONS_ALLOWED=${BITWARDEN_INVITATIONS_ALLOWED}
|
- INVITATIONS_ALLOWED=${BITWARDEN_INVITATIONS_ALLOWED}
|
||||||
- ADMIN_TOKEN=${BITWARDEN_ADMIN_TOKEN}
|
- ADMIN_TOKEN=${BITWARDEN_ADMIN_TOKEN}
|
||||||
@@ -198,7 +193,7 @@ services:
|
|||||||
# If Traefik is on a remote server: these labels are NOT USED;
|
# If Traefik is on a remote server: these labels are NOT USED;
|
||||||
# configure external yml files in /traefik/dynamic folder instead.
|
# configure external yml files in /traefik/dynamic folder instead.
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.vaultwarden.rule=Host(`vault.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.vaultwarden.rule=Host(`vault.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
|
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
|
||||||
- "traefik.http.routers.vaultwarden.tls=true"
|
- "traefik.http.routers.vaultwarden.tls=true"
|
||||||
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
|
||||||
@@ -238,11 +233,11 @@ networks:
|
|||||||
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
- https://backrest.kelinreij.duckdns.org
|
- https://backrest.${DOMAIN}
|
||||||
- https://192.168.4.4:9898
|
- https://192.168.4.4:9898
|
||||||
- https://duplicati.kelinreij.duckdns.org
|
- https://duplicati.${DOMAIN}
|
||||||
- https://192.168.4.4:8200
|
- https://192.168.4.4:8200
|
||||||
- https://forms.kelinreij.duckdns.org
|
- https://forms.${DOMAIN}
|
||||||
- https://192.168.4.4:3002
|
- https://192.168.4.4:3002
|
||||||
- https://vault.kelinreij.duckdns.org
|
- https://vault.${DOMAIN}
|
||||||
- https://192.168.4.4:8091
|
- https://192.168.4.4:8091
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
# VPN Stack
|
# VPN Stack
|
||||||
# VPN client and VPN-routed download clients
|
|
||||||
# Place in /opt/stacks/vpn/docker-compose.yml
|
|
||||||
|
|
||||||
# RESTART POLICY GUIDE:
|
# RESTART POLICY GUIDE:
|
||||||
# - unless-stopped: Core infrastructure services that should always run
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
# - no: Services with Sablier lazy loading (start on-demand)
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
@@ -10,7 +7,6 @@
|
|||||||
services:
|
services:
|
||||||
# Gluetun - VPN client (Surfshark)
|
# Gluetun - VPN client (Surfshark)
|
||||||
# Routes download clients through VPN for security
|
# Routes download clients through VPN for security
|
||||||
# VPN service should always run to maintain secure connections
|
|
||||||
gluetun:
|
gluetun:
|
||||||
image: qmcgaw/gluetun:latest
|
image: qmcgaw/gluetun:latest
|
||||||
container_name: gluetun
|
container_name: gluetun
|
||||||
@@ -47,7 +43,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.qbittorrent.rule=Host(`qbit.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.qbittorrent.rule=Host(`qbit.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.qbittorrent.entrypoints=websecure"
|
- "traefik.http.routers.qbittorrent.entrypoints=websecure"
|
||||||
- "traefik.http.routers.qbittorrent.tls=true"
|
- "traefik.http.routers.qbittorrent.tls=true"
|
||||||
- "traefik.http.routers.qbittorrent.middlewares=authelia@docker"
|
- "traefik.http.routers.qbittorrent.middlewares=authelia@docker"
|
||||||
@@ -59,7 +55,6 @@ services:
|
|||||||
- "sablier.sessionDuration=1h"
|
- "sablier.sessionDuration=1h"
|
||||||
|
|
||||||
# qBittorrent - Torrent client
|
# qBittorrent - Torrent client
|
||||||
# Routes through Gluetun VPN
|
|
||||||
qbittorrent:
|
qbittorrent:
|
||||||
image: lscr.io/linuxserver/qbittorrent:latest
|
image: lscr.io/linuxserver/qbittorrent:latest
|
||||||
deploy:
|
deploy:
|
||||||
@@ -93,5 +88,5 @@ networks:
|
|||||||
|
|
||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
- https://qbit.kelinreij.duckdns.org
|
- https://qbit.${DOMAIN}
|
||||||
- https://192.168.4.4:8081
|
- https://192.168.4.4:8081
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
|
# Wiki Services
|
||||||
|
# RESTART POLICY GUIDE:
|
||||||
|
# - unless-stopped: Core infrastructure services that should always run
|
||||||
|
# - no: Services with Sablier lazy loading (start on-demand)
|
||||||
|
# - See individual service comments for specific reasoning
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# DokuWiki - Wiki without database
|
# DokuWiki - Wiki without database
|
||||||
# Access at: https://wiki.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
dokuwiki:
|
dokuwiki:
|
||||||
image: lscr.io/linuxserver/dokuwiki:latest
|
image: lscr.io/linuxserver/dokuwiki:latest
|
||||||
@@ -26,7 +31,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.dokuwiki.rule=Host(`dokuwiki.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.dokuwiki.rule=Host(`dokuwiki.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.dokuwiki.entrypoints=websecure"
|
- "traefik.http.routers.dokuwiki.entrypoints=websecure"
|
||||||
- "traefik.http.routers.dokuwiki.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.dokuwiki.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.dokuwiki.middlewares=authelia@docker"
|
- "traefik.http.routers.dokuwiki.middlewares=authelia@docker"
|
||||||
@@ -38,7 +43,6 @@ services:
|
|||||||
- "sablier.start-on-demand=true"
|
- "sablier.start-on-demand=true"
|
||||||
|
|
||||||
# BookStack - Documentation platform
|
# BookStack - Documentation platform
|
||||||
# Access at: https://docs.kelinreij.duckdns.org
|
|
||||||
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
# Uses Sablier lazy loading - starts on-demand, stops after 5min inactivity
|
||||||
bookstack:
|
bookstack:
|
||||||
image: lscr.io/linuxserver/bookstack:latest
|
image: lscr.io/linuxserver/bookstack:latest
|
||||||
@@ -54,7 +58,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- APP_URL=https://bookstack.kelinreij.duckdns.org
|
- APP_URL=https://bookstack.${DOMAIN}
|
||||||
- DB_HOST=bookstack-db
|
- DB_HOST=bookstack-db
|
||||||
- DB_PORT=3306
|
- DB_PORT=3306
|
||||||
- DB_DATABASE=bookstack
|
- DB_DATABASE=bookstack
|
||||||
@@ -78,7 +82,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.bookstack.rule=Host(`bookstack.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.bookstack.rule=Host(`bookstack.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.bookstack.entrypoints=websecure"
|
- "traefik.http.routers.bookstack.entrypoints=websecure"
|
||||||
- "traefik.http.routers.bookstack.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.bookstack.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.bookstack.middlewares=authelia@docker"
|
- "traefik.http.routers.bookstack.middlewares=authelia@docker"
|
||||||
@@ -107,7 +111,6 @@ services:
|
|||||||
- "homelab.description=BookStack database"
|
- "homelab.description=BookStack database"
|
||||||
|
|
||||||
# MediaWiki - Wiki platform
|
# MediaWiki - Wiki platform
|
||||||
# Access at: https://mediawiki.kelinreij.duckdns.org
|
|
||||||
mediawiki:
|
mediawiki:
|
||||||
image: mediawiki:latest
|
image: mediawiki:latest
|
||||||
container_name: mediawiki
|
container_name: mediawiki
|
||||||
@@ -142,7 +145,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=traefik-network"
|
- "traefik.docker.network=traefik-network"
|
||||||
# Router configuration
|
# Router configuration
|
||||||
- "traefik.http.routers.mediawiki.rule=Host(`mediawiki.kelinreij.duckdns.org`)"
|
- "traefik.http.routers.mediawiki.rule=Host(`mediawiki.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.mediawiki.entrypoints=websecure"
|
- "traefik.http.routers.mediawiki.entrypoints=websecure"
|
||||||
- "traefik.http.routers.mediawiki.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.mediawiki.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.mediawiki.middlewares=authelia@docker"
|
- "traefik.http.routers.mediawiki.middlewares=authelia@docker"
|
||||||
@@ -183,9 +186,9 @@ networks:
|
|||||||
x-dockge:
|
x-dockge:
|
||||||
urls:
|
urls:
|
||||||
# Proxied URLs (through Traefik)
|
# Proxied URLs (through Traefik)
|
||||||
- https://bookstack.kelinreij.duckdns.org
|
- https://bookstack.${DOMAIN}
|
||||||
- https://192.168.4.4:6875
|
- https://192.168.4.4:6875
|
||||||
- https://dokuwiki.kelinreij.duckdns.org
|
- https://dokuwiki.${DOMAIN}
|
||||||
- https://192.168.4.4:8087
|
- https://192.168.4.4:8087
|
||||||
- https://mediawiki.kelinreij.duckdns.org
|
- https://mediawiki.${DOMAIN}
|
||||||
- https://192.168.4.4:8086
|
- https://192.168.4.4:8086
|
||||||
|
|||||||
146
ez-homelab-script-updates.md
Normal file
146
ez-homelab-script-updates.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
I've reviewed the full script and compared it against the show_main_menu() function and the overall logic flow. The script appears to be a comprehensive setup and deployment tool for the EZ-Homelab project, but there are several inconsistencies, bugs, and areas that need updating to align with the new menu options and intended behavior. Below is a detailed audit, focusing on the menu-related changes and broader issues.
|
||||||
|
|
||||||
|
1. Menu Structure and Logic Alignment
|
||||||
|
Current Menu Options (from show_main_menu()):
|
||||||
|
Install Prerequisites
|
||||||
|
Deploy Core Server
|
||||||
|
Deploy Additional Server
|
||||||
|
Install NVIDIA Drivers
|
||||||
|
The menu is clean and logical, with clear separation between system setup, core deployment, additional server setup, and optional NVIDIA support.
|
||||||
|
Code Handling:
|
||||||
|
Option 1: Sets FORCE_SYSTEM_SETUP=true, skips deployment, calls system_setup(), and exits. This is consistent.
|
||||||
|
Option 2: Sets DEPLOY_CORE=true, DEPLOY_INFRASTRUCTURE=true, DEPLOY_DASHBOARDS=true, SETUP_STACKS=true. Deploys everything for a core server. Consistent.
|
||||||
|
Option 3: Sets DEPLOY_CORE=false, DEPLOY_INFRASTRUCTURE=true, DEPLOY_DASHBOARDS=false, SETUP_STACKS=true. Deploys infrastructure and stacks (no core/dashboards) for additional servers. This makes sense for multi-server setups.
|
||||||
|
Option 4: Sets INSTALL_NVIDIA=true, skips deployment, logs a warning ("not yet implemented"), and exits. Placeholder only.
|
||||||
|
Issues:
|
||||||
|
The menu assumes Option 3 ("Deploy Additional Server") is for multi-server scenarios, but the script still prompts for all REQUIRED_VARS (e.g., DOMAIN, SERVER_IP) even for additional servers. For additional servers, some variables (like DOMAIN if it's core-specific) might not be needed, or SERVER_IP should be clarified as the additional server's IP. The code doesn't differentiate prompts based on deployment type, which could confuse users.
|
||||||
|
No command-line argument parsing (e.g., --debug, --verbose, or direct flags for deployment modes). The script logs args but doesn't use them, forcing interactive menu use.
|
||||||
|
The menu loop (while true) correctly handles invalid inputs and quits, but there's no way to bypass the menu (e.g., via env vars or args) for automated/scripted runs.
|
||||||
|
2. Bugs and Inconsistencies
|
||||||
|
Hardcoded Values in Completion Message:
|
||||||
|
Lines ~1520-1530: The success message hardcodes URLs like https://dockge.kelinreij.duckdns.org and https://homepage.kelinreij.duckdns.org. These should use ${DOMAIN} and ${SERVER_IP} for dynamic replacement. This is a critical bug as it won't work for other users/domains.
|
||||||
|
Double Setting of HOMEPAGE_ALLOWED_HOSTS:
|
||||||
|
In save_env_file() (lines ~630-633 and ~647-649): First sets HOMEPAGE_ALLOWED_HOSTS="${HOMEPAGE_SUBDOMAIN:-homepage}.${DOMAIN},${SERVER_IP}:3003", then overwrites it with HOMEPAGE_ALLOWED_HOSTS="homepage.${DOMAIN},${SERVER_IP}:3003". The second line ignores HOMEPAGE_SUBDOMAIN, causing inconsistency. Remove or merge the logic.
|
||||||
|
NVIDIA Driver Installation:
|
||||||
|
Option 4 is a placeholder with no implementation. The script should either implement basic NVIDIA setup (e.g., install drivers, CUDA) or remove the option to avoid user confusion.
|
||||||
|
Environment Variable Handling:
|
||||||
|
DEBUG and VERBOSE are set from env but not from command-line args. Add arg parsing (e.g., using getopts) to allow --debug, --verbose, etc.
|
||||||
|
In load_env_file_safely(), it exports vars but doesn't handle arrays or complex values well. No major issues, but could be improved for security (e.g., avoid logging secrets even in debug).
|
||||||
|
TLS and Multi-Server Logic:
|
||||||
|
For Option 3, setup_multi_server_tls() is called, which prompts for CORE_SERVER_IP if unset. This is good, but the script assumes SERVER_IP is the additional server's IP for TLS certs. Clarify in prompts or docs.
|
||||||
|
REQUIRED_VARS includes DOMAIN, but for additional servers, the domain might be inherited from core. The code doesn't skip prompts intelligently.
|
||||||
|
3. Code Structure and Messiness
|
||||||
|
Function Organization:
|
||||||
|
The script is long (1542 lines) and could be modularized (e.g., extract menu logic, validation, and deployment into separate functions or files). main() is bloated with inline logic.
|
||||||
|
Repeated patterns (e.g., copying .env files, replacing placeholders, chowning dirs) could be refactored into reusable functions.
|
||||||
|
Error Handling and Logging:
|
||||||
|
Good use of log_* functions, but debug_log only writes to file if DEBUG=true. Add console debug output for VERBOSE=true.
|
||||||
|
set -e is good, but some commands (e.g., docker network create) use || to ignore failures, which is inconsistent.
|
||||||
|
Deployment Flow:
|
||||||
|
After menu selection, the flow is: check system, prompt vars, save, validate, deploy. For Option 1/4, it exits early, which is fine. But for Options 2/3, it always runs enhance_placeholder_replacement() and validate_secrets(), even if not deploying core. validate_secrets() skips if DEPLOY_CORE=false, which is correct.
|
||||||
|
perform_deployment() has dynamic step numbering, but it's minor.
|
||||||
|
Security and Best Practices:
|
||||||
|
Uses sudo appropriately, but ACTUAL_USER logic is solid.
|
||||||
|
Secrets (e.g., passwords) are handled carefully, but AUTHELIA_ADMIN_PASSWORD generation could fail silently if Docker isn't available.
|
||||||
|
No input sanitization beyond basic validation in validate_variable().
|
||||||
|
4. Recommendations for Updates
|
||||||
|
Align with Menu Changes:
|
||||||
|
Update prompts in validate_and_prompt_variables() to skip irrelevant vars for Option 3 (e.g., don't prompt for DOMAIN if deploying additional server, or clarify it's for core).
|
||||||
|
Add command-line support: Parse args to set flags directly (e.g., .[ez-homelab.sh](http://_vscodecontentref_/1) --deploy-core --verbose to bypass menu).
|
||||||
|
Implement Option 4: Add NVIDIA driver installation logic in system_setup() or a new function (check GPU, install drivers, reboot if needed).
|
||||||
|
Fix Bugs:
|
||||||
|
Replace hardcoded URLs with variables (e.g., https://dockge.${DOMAIN}).
|
||||||
|
Fix HOMEPAGE_ALLOWED_HOSTS duplication.
|
||||||
|
Add validation to ensure DOMAIN and SERVER_IP are set before deployment.
|
||||||
|
General Cleanup:
|
||||||
|
Break main() into smaller functions (e.g., handle_menu_choice(), prepare_deployment()).
|
||||||
|
Add a --help option and better error messages.
|
||||||
|
Test multi-server flow thoroughly, as setup_multi_server_tls() has complex SSH logic.
|
||||||
|
Testing:
|
||||||
|
The script lacks automated tests. Add checks for each menu path (e.g., dry-run mode).
|
||||||
|
Validate YAML syntax in perform_deployment() before deploying.
|
||||||
|
Overall, the script is functional but messy, with the menu changes exposing inconsistencies in variable prompting and hardcoded values. The core logic aligns with the menu, but updates are needed for robustness, especially for multi-server setups. If you provide more details on what specific "menu changes" broke, I can refine this audit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Overview of Requested Changes
|
||||||
|
Based on your notes, this is a comprehensive refactoring of ez-homelab.sh to improve usability, fix bugs, and enhance functionality. The changes focus on modularizing the script, improving user experience (e.g., better prompts, error handling), and adding new features like argument parsing and dry-run mode. Key themes include separating concerns (e.g., moving system setup to a new script), clarifying multi-server logic, and making the script more robust and user-friendly.
|
||||||
|
|
||||||
|
Scope and Impact:
|
||||||
|
|
||||||
|
Lines of Code: The script is ~1542 lines; changes will add/modify ~200-400 lines, potentially splitting into multiple files.
|
||||||
|
Breaking Changes: Menu Option 1 now requires a separate script; variable prompts and logic for Option 3/multi-server will change; hardcoded values will be removed.
|
||||||
|
Testing Needs: High—test all menu options, argument parsing, dry-run, error scenarios, and multi-server TLS. Validate on different systems (with/without NVIDIA, core vs. additional servers).
|
||||||
|
Dependencies: Requires nvidia-detect (for Option 4), access to NVIDIA repos, and ensuring the new install-prerequisites.sh works across distros.
|
||||||
|
Estimated Effort: 10-15 hours for implementation, plus testing. Prioritize bugs and core menu fixes first.
|
||||||
|
Risks: Error handling changes could mask real issues; multi-server logic is complex—ensure backward compatibility.
|
||||||
|
The tasks are broken down into categories below, with priorities (High/Medium/Low) based on impact and dependencies.
|
||||||
|
|
||||||
|
Task Breakdown
|
||||||
|
1. Menu and Workflow Updates (High Priority - Core User Experience)
|
||||||
|
Task 1.1: Create install-prerequisites.sh
|
||||||
|
Extract system_setup() and related logic into a new standalone script. Ensure it must run as root/sudo. Update Option 1 in ez-homelab.sh to detect if running as root; if not, use sudo to launch the new script. Handle cases where sudo isn't available (e.g., log error and exit).
|
||||||
|
Effort: 2-3 hours. Dependencies: None. Files: New install-prerequisites.sh, modify ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 1.2: Update Menu Option 3 Prompts
|
||||||
|
Ensure Option 3 prompts for all REQUIRED_VARS (no skipping). Reword prompts to clarify that variables like DOMAIN and SERVER_IP are for the local (additional) server, while REMOTE_SERVER_* vars are for the core server (e.g., "Enter the IP of the core server where Traefik is running"). Keep existing variable names but improve descriptions.
|
||||||
|
Effort: 1-2 hours. Dependencies: Review .env.example for REMOTE_SERVER context. Files: Modify validate_and_prompt_variables() and prompt_for_variable() in ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 1.3: Implement Menu Option 4 (NVIDIA Installation)
|
||||||
|
Use nvidia-detect to identify the GPU and determine the official NVIDIA installer. Install NVIDIA drivers and the NVIDIA Container Toolkit (for Docker GPU support). Handle errors gracefully (e.g., if no GPU detected, skip). Integrate into system_setup() or a new function.
|
||||||
|
Effort: 3-4 hours. Dependencies: Install nvidia-detect if needed; test on systems with/without NVIDIA GPUs. Files: Modify/add functions in ez-homelab.sh or install-prerequisites.sh.
|
||||||
|
|
||||||
|
2. Bug Fixes (High Priority - Prevents User Errors)
|
||||||
|
Task 2.1: Remove Hardcoded Values
|
||||||
|
Replace all instances of "kelin", "kelinreij", "kelin-casa", etc., with appropriate variables (e.g., ${DOMAIN}, ${SERVER_IP}). Update completion messages, URLs, and any examples.
|
||||||
|
Effort: 1 hour. Dependencies: Search the script for these strings. Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 2.2: Fix HOMEPAGE_ALLOWED_HOSTS
|
||||||
|
Remove HOMEPAGE_SUBDOMAIN references. Ensure the line is exactly HOMEPAGE_ALLOWED_HOSTS="homepage.${DOMAIN},${SERVER_IP}:3003" (no duplication or overwrites).
|
||||||
|
Effort: 30 minutes. Dependencies: Check save_env_file(). Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
3. New Features and Enhancements (Medium Priority - Improves Script Flexibility)
|
||||||
|
Task 3.1: Add Argument Parsing
|
||||||
|
Implement command-line argument support (e.g., using getopts or argparse if Bash allows). Support flags like --deploy-core, --deploy-additional, --install-nvidia, --dry-run, --verbose, --debug, --help. Allow bypassing the menu for automated runs.
|
||||||
|
Effort: 2-3 hours. Dependencies: None. Files: Add to main() in ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 3.2: Add Dry-Run Mode
|
||||||
|
Implement --dry-run flag: Simulate deployment without making changes (e.g., validate configs, show what would be done, but don't run docker compose up). Log actions verbosely.
|
||||||
|
Effort: 2 hours. Dependencies: Argument parsing. Files: Modify perform_deployment() and related functions in ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 3.3: Enhance Console Logging for Verbose Mode
|
||||||
|
Update log_* functions to output to console when VERBOSE=true (not just debug file). Ensure debug logs go to both console and file.
|
||||||
|
Effort: 1 hour. Dependencies: None. Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 3.4: Improve Error Handling
|
||||||
|
Change set -e to allow continuation on errors (log warnings/errors but proceed). Use || consistently for non-critical failures (e.g., network creation). Add try-catch-like logic where possible.
|
||||||
|
Effort: 2 hours. Dependencies: Review all exit 1 points. Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
4. TLS and Multi-Server Logic Refinements (Medium Priority - Complex but Critical for Multi-Server)
|
||||||
|
Task 4.1: Clarify Variable Usage
|
||||||
|
Update prompts and logic to distinguish: IP_ADDRESS/SERVER_IP for local machine; REMOTE_SERVER_* for core server. Ensure ${DOMAIN} is prompted even for additional servers (as it's needed for configs). Reword setup_multi_server_tls() to emphasize cert copying from core.
|
||||||
|
Effort: 1-2 hours. Dependencies: Task 1.2. Files: ez-homelab.sh.
|
||||||
|
5. Function Organization and Code Quality (Low Priority - Refactoring for Maintainability)
|
||||||
|
Task 5.1: Audit and Improve Placeholder/Env Functions
|
||||||
|
Review replace_env_placeholders() and enhance_placeholder_replacement() for quality (e.g., handle edge cases, improve performance). Refactor if needed.
|
||||||
|
Effort: 1-2 hours. Dependencies: None. Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 5.2: Modularize Code with More Functions
|
||||||
|
Break main() into smaller functions (e.g., parse_args(), handle_menu_choice(), prepare_deployment()). Extract repeated logic (e.g., env copying, dir creation) into helpers.
|
||||||
|
Effort: 3-4 hours. Dependencies: All other tasks. Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
Task 5.3: Fix Deployment Flow
|
||||||
|
Streamline perform_deployment(): Ensure consistent step numbering, better error recovery, and integration with dry-run.
|
||||||
|
Effort: 1 hour. Dependencies: Tasks 3.2, 3.4. Files: ez-homelab.sh.
|
||||||
|
|
||||||
|
Implementation Order and Next Steps
|
||||||
|
Start with High-Priority Tasks: Bugs (2.1-2.2) and Menu Option 1 (1.1) to fix immediate issues.
|
||||||
|
Then Core Features: Argument parsing (3.1), dry-run (3.2), and Option 4 (1.3).
|
||||||
|
Refinements: Error handling (3.4), logging (3.3), and multi-server (4.1).
|
||||||
|
Cleanup: Function organization (5.x) last, as it depends on other changes.
|
||||||
|
Testing Plan: After each task, test interactively and with args. Use dry-run for validation.
|
||||||
|
Questions for You: Confirm if install-prerequisites.sh should be in the same repo. Any specific NVIDIA installer versions to target? Should dry-run output be machine-readable (e.g., JSON)?
|
||||||
|
Let me know if you'd like me to start implementing any tasks or need more details!
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# EZ-Homelab Unified Setup & Deployment Script
|
# EZ-Homelab Unified Setup & Deployment Script
|
||||||
|
|
||||||
# Two step process required for first-time setup:
|
# Removed set -e to allow graceful error handling
|
||||||
# Run 'sudo ./ez-homelab.sh' to install Docker and perform system setup
|
|
||||||
# Run './ez-homelab.sh' to deploy stacks after initial setup
|
|
||||||
set -e # Exit on error
|
|
||||||
|
|
||||||
# Debug logging configuration
|
# Debug logging configuration
|
||||||
DEBUG=${DEBUG:-false}
|
DEBUG=${DEBUG:-false}
|
||||||
@@ -95,25 +92,46 @@ load_env_file_safely() {
|
|||||||
|
|
||||||
debug_log "Env file loaded successfully"
|
debug_log "Env file loaded successfully"
|
||||||
}
|
}
|
||||||
replace_env_placeholders() {
|
load_env_file() {
|
||||||
|
load_env_file_safely "$REPO_DIR/.env"
|
||||||
|
}
|
||||||
|
localize_yml_file() {
|
||||||
local file_path="$1"
|
local file_path="$1"
|
||||||
local fail_on_missing="${2:-false}" # New parameter to control failure behavior
|
local fail_on_missing="${2:-false}" # New parameter to control failure behavior
|
||||||
local missing_vars=""
|
local missing_vars=""
|
||||||
local replaced_count=0
|
local replaced_count=0
|
||||||
|
|
||||||
debug_log "replace_env_placeholders called for file: $file_path, fail_on_missing: $fail_on_missing"
|
debug_log "localize_yml_file called for file: $file_path, fail_on_missing: $fail_on_missing"
|
||||||
|
|
||||||
if [ ! -f "$file_path" ]; then
|
if [ ! -f "$file_path" ]; then
|
||||||
log_warning "File $file_path does not exist, skipping placeholder replacement"
|
log_warning "File $file_path does not exist, skipping YAML localization"
|
||||||
debug_log "File $file_path does not exist"
|
debug_log "File $file_path does not exist"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if file is writable
|
||||||
|
if [ ! -w "$file_path" ]; then
|
||||||
|
log_error "File $file_path is not writable, cannot localize"
|
||||||
|
debug_log "Permission denied for $file_path"
|
||||||
|
if [ "$fail_on_missing" = true ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup only if target file already exists (not for repo sources)
|
||||||
|
if [[ "$file_path" != "$REPO_DIR"* ]] && [ -f "$file_path" ]; then
|
||||||
|
cp "$file_path" "$file_path.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
|
debug_log "Backed up $file_path"
|
||||||
|
fi
|
||||||
|
|
||||||
# Find all ${VAR} patterns in the file
|
# Find all ${VAR} patterns in the file
|
||||||
local vars=$(grep -o '\${[^}]*}' "$file_path" | sed 's/\${//' | sed 's/}//' | sort | uniq)
|
local vars=$(grep -o '\${[^}]*}' "$file_path" | sed 's/\${//' | sed 's/}//' | sort | uniq)
|
||||||
debug_log "Found variables to replace: $vars"
|
debug_log "Found variables to replace: $vars"
|
||||||
|
|
||||||
for var in $vars; do
|
for var in $vars; do
|
||||||
|
# Trim whitespace from variable name
|
||||||
|
var=$(echo "$var" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
# Skip derived variables that should not be replaced
|
# Skip derived variables that should not be replaced
|
||||||
case "$var" in
|
case "$var" in
|
||||||
"ACME_EMAIL"|"AUTHELIA_ADMIN_EMAIL"|"SMTP_USERNAME"|"SMTP_PASSWORD")
|
"ACME_EMAIL"|"AUTHELIA_ADMIN_EMAIL"|"SMTP_USERNAME"|"SMTP_PASSWORD")
|
||||||
@@ -122,23 +140,46 @@ replace_env_placeholders() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [ -z "${!var:-}" ]; then
|
if [ -z "${!var+x}" ]; then
|
||||||
log_warning "Environment variable $var not found in .env file"
|
log_warning "Environment variable $var not found in .env file"
|
||||||
debug_log "Missing variable: $var"
|
debug_log "Missing variable: $var"
|
||||||
missing_vars="$missing_vars $var"
|
missing_vars="$missing_vars $var"
|
||||||
else
|
else
|
||||||
# Replace ${VAR} with the value
|
# Replace ${VAR} with the value, handling special characters
|
||||||
|
local escaped_value=$(printf '%s\n' "${!var}" | sed 's/[[\.*^$()+?{|]/\\&/g')
|
||||||
debug_log "Replacing \${$var} with value: [HIDDEN]" # Don't log actual secrets
|
debug_log "Replacing \${$var} with value: [HIDDEN]" # Don't log actual secrets
|
||||||
sed -i "s|\${$var}|${!var}|g" "$file_path"
|
sed -i "s|\${[ \t]*${var}[ \t]*}|${escaped_value}|g" "$file_path"
|
||||||
replaced_count=$((replaced_count + 1))
|
replaced_count=$((replaced_count + 1))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
debug_log "Replaced $replaced_count variables in $file_path"
|
debug_log "Replaced $replaced_count variables in $file_path"
|
||||||
|
|
||||||
|
# Post-replacement validation: check for remaining ${VAR} (except skipped)
|
||||||
|
local remaining_vars=$(grep -v '^[[:space:]]*#' "$file_path" | grep -o '\${[^}]*}' | sed 's/\${//' | sed 's/}//' | sort | uniq)
|
||||||
|
local invalid_remaining=""
|
||||||
|
for rvar in $remaining_vars; do
|
||||||
|
rvar=$(echo "$rvar" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
|
case "$rvar" in
|
||||||
|
"ACME_EMAIL"|"AUTHELIA_ADMIN_EMAIL"|"SMTP_USERNAME"|"SMTP_PASSWORD")
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
invalid_remaining="$invalid_remaining $rvar"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [ -n "$invalid_remaining" ]; then
|
||||||
|
log_error "Failed to replace variables in $file_path: $invalid_remaining"
|
||||||
|
debug_log "Unreplaced variables: $invalid_remaining"
|
||||||
|
if [ "$fail_on_missing" = true ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Handle missing variables
|
# Handle missing variables
|
||||||
if [ -n "$missing_vars" ]; then
|
if [ -n "$missing_vars" ]; then
|
||||||
MISSING_VARS_SUMMARY="${MISSING_VARS_SUMMARY}${missing_vars}"
|
GLOBAL_MISSING_VARS="${GLOBAL_MISSING_VARS}${missing_vars}"
|
||||||
if [ "$fail_on_missing" = true ]; then
|
if [ "$fail_on_missing" = true ]; then
|
||||||
log_error "Critical environment variables missing: $missing_vars"
|
log_error "Critical environment variables missing: $missing_vars"
|
||||||
debug_log "Failing deployment due to missing critical variables: $missing_vars"
|
debug_log "Failing deployment due to missing critical variables: $missing_vars"
|
||||||
@@ -148,17 +189,18 @@ replace_env_placeholders() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Enhanced placeholder replacement for all configuration files
|
# Enhanced placeholder replacement for all configuration files
|
||||||
enhance_placeholder_replacement() {
|
localize_deployment() {
|
||||||
log_info "Starting enhanced placeholder replacement..."
|
log_info "Starting deployment localization..."
|
||||||
|
|
||||||
local processed_files=0
|
local processed_files=0
|
||||||
|
GLOBAL_MISSING_VARS=""
|
||||||
|
|
||||||
# Process docker-compose files
|
# Process docker-compose files
|
||||||
if [ -d "$REPO_DIR/docker-compose" ]; then
|
if [ -d "$REPO_DIR/docker-compose" ]; then
|
||||||
while IFS= read -r -d '' file_path; do
|
while IFS= read -r -d '' file_path; do
|
||||||
if [ -f "$file_path" ]; then
|
if [ -f "$file_path" ]; then
|
||||||
debug_log "Processing docker-compose file: $file_path"
|
debug_log "Processing docker-compose file: $file_path"
|
||||||
replace_env_placeholders "$file_path" false
|
localize_yml_file "$file_path" false
|
||||||
processed_files=$((processed_files + 1))
|
processed_files=$((processed_files + 1))
|
||||||
fi
|
fi
|
||||||
done < <(find "$REPO_DIR/docker-compose" -name "*.yml" -o -name "*.yaml" -print0 2>/dev/null)
|
done < <(find "$REPO_DIR/docker-compose" -name "*.yml" -o -name "*.yaml" -print0 2>/dev/null)
|
||||||
@@ -169,14 +211,20 @@ enhance_placeholder_replacement() {
|
|||||||
while IFS= read -r -d '' file_path; do
|
while IFS= read -r -d '' file_path; do
|
||||||
if [ -f "$file_path" ]; then
|
if [ -f "$file_path" ]; then
|
||||||
debug_log "Processing config template file: $file_path"
|
debug_log "Processing config template file: $file_path"
|
||||||
replace_env_placeholders "$file_path" false
|
localize_yml_file "$file_path" false
|
||||||
processed_files=$((processed_files + 1))
|
processed_files=$((processed_files + 1))
|
||||||
fi
|
fi
|
||||||
done < <(find "$REPO_DIR/config-templates" -name "*.yml" -o -name "*.yaml" -print0 2>/dev/null)
|
done < <(find "$REPO_DIR/config-templates" -name "*.yml" -o -name "*.yaml" -print0 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_success "Enhanced placeholder replacement completed - processed $processed_files files"
|
log_success "Deployment localization completed - processed $processed_files files"
|
||||||
debug_log "Enhanced replacement completed for $processed_files files"
|
debug_log "Localization completed for $processed_files files"
|
||||||
|
|
||||||
|
# Report aggregated missing variables
|
||||||
|
if [ -n "$GLOBAL_MISSING_VARS" ]; then
|
||||||
|
log_warning "Aggregated missing environment variables across all files: $GLOBAL_MISSING_VARS"
|
||||||
|
debug_log "Global missing vars: $GLOBAL_MISSING_VARS"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to generate shared CA for multi-server TLS
|
# Function to generate shared CA for multi-server TLS
|
||||||
@@ -627,9 +675,12 @@ save_env_file() {
|
|||||||
|
|
||||||
# Update HOMEPAGE_ALLOWED_HOSTS dynamically
|
# Update HOMEPAGE_ALLOWED_HOSTS dynamically
|
||||||
if [ -n "${DOMAIN:-}" ] && [ -n "${SERVER_IP:-}" ]; then
|
if [ -n "${DOMAIN:-}" ] && [ -n "${SERVER_IP:-}" ]; then
|
||||||
# Allow user to specify homepage subdomain
|
# Extract Homepage port from compose file
|
||||||
HOMEPAGE_SUBDOMAIN="${HOMEPAGE_SUBDOMAIN:-homepage}"
|
HOMEPAGE_PORT=$(grep -A1 'ports:' "$REPO_DIR/docker-compose/dashboards/docker-compose.yml" | grep -o '"[0-9]*:3000"' | cut -d'"' -f2 | cut -d: -f1)
|
||||||
HOMEPAGE_ALLOWED_HOSTS="${HOMEPAGE_SUBDOMAIN}.${DOMAIN},${SERVER_IP}:3003"
|
if [ -z "$HOMEPAGE_PORT" ]; then
|
||||||
|
HOMEPAGE_PORT=3003 # Fallback
|
||||||
|
fi
|
||||||
|
HOMEPAGE_ALLOWED_HOSTS="homepage.${DOMAIN},${SERVER_IP}:${HOMEPAGE_PORT}"
|
||||||
sudo -u "$ACTUAL_USER" sed -i "s|HOMEPAGE_ALLOWED_HOSTS=.*|HOMEPAGE_ALLOWED_HOSTS=$HOMEPAGE_ALLOWED_HOSTS|" "$REPO_DIR/.env"
|
sudo -u "$ACTUAL_USER" sed -i "s|HOMEPAGE_ALLOWED_HOSTS=.*|HOMEPAGE_ALLOWED_HOSTS=$HOMEPAGE_ALLOWED_HOSTS|" "$REPO_DIR/.env"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -669,28 +720,24 @@ save_env_file() {
|
|||||||
sudo -u "$ACTUAL_USER" sed -i "s%AUTHELIA_ADMIN_EMAIL=.*%AUTHELIA_ADMIN_EMAIL=$ADMIN_EMAIL%" "$REPO_DIR/.env"
|
sudo -u "$ACTUAL_USER" sed -i "s%AUTHELIA_ADMIN_EMAIL=.*%AUTHELIA_ADMIN_EMAIL=$ADMIN_EMAIL%" "$REPO_DIR/.env"
|
||||||
|
|
||||||
# Generate password hash if needed
|
# Generate password hash if needed
|
||||||
if [ -z "$AUTHELIA_ADMIN_PASSWORD" ]; then
|
if [ -z "$AUTHELIA_ADMIN_PASSWORD_HASH" ]; then
|
||||||
log_info "Generating Authelia password hash..."
|
log_info "Generating Authelia password hash..."
|
||||||
# Pull Authelia image if needed
|
# Pull Authelia image if needed
|
||||||
if ! docker images | grep -q authelia/authelia; then
|
if ! docker images | grep -q authelia/authelia; then
|
||||||
docker pull authelia/authelia:latest > /dev/null 2>&1
|
docker pull authelia/authelia:latest > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
AUTHELIA_ADMIN_PASSWORD=$(docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password "$ADMIN_PASSWORD" 2>&1 | grep -o '\$argon2id.*')
|
AUTHELIA_ADMIN_PASSWORD_HASH=$(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
|
if [ -z "$AUTHELIA_ADMIN_PASSWORD_HASH" ]; then
|
||||||
log_error "Failed to generate Authelia password hash. Please check that ADMIN_PASSWORD is set."
|
log_error "Failed to generate Authelia password hash. Please check that ADMIN_PASSWORD is set."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Save password hash
|
# Save password hash
|
||||||
sudo -u "$ACTUAL_USER" sed -i "s%# AUTHELIA_ADMIN_PASSWORD=.*%AUTHELIA_ADMIN_PASSWORD=\"$AUTHELIA_ADMIN_PASSWORD\"%" "$REPO_DIR/.env"
|
sudo -u "$ACTUAL_USER" sed -i "s%# AUTHELIA_ADMIN_PASSWORD_HASH=.*%AUTHELIA_ADMIN_PASSWORD_HASH=\"$AUTHELIA_ADMIN_PASSWORD_HASH\"%" "$REPO_DIR/.env"
|
||||||
sudo -u "$ACTUAL_USER" sed -i "s%AUTHELIA_ADMIN_PASSWORD=.*%AUTHELIA_ADMIN_PASSWORD=\"$AUTHELIA_ADMIN_PASSWORD\"%" "$REPO_DIR/.env"
|
sudo -u "$ACTUAL_USER" sed -i "s%AUTHELIA_ADMIN_PASSWORD_HASH=.*%AUTHELIA_ADMIN_PASSWORD_HASH=\"$AUTHELIA_ADMIN_PASSWORD_HASH\"%" "$REPO_DIR/.env"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update HOMEPAGE_ALLOWED_HOSTS with expanded values
|
|
||||||
HOMEPAGE_ALLOWED_HOSTS="homepage.${DOMAIN},${SERVER_IP}:3003"
|
|
||||||
sudo -u "$ACTUAL_USER" sed -i "s|HOMEPAGE_ALLOWED_HOSTS=.*|HOMEPAGE_ALLOWED_HOSTS=$HOMEPAGE_ALLOWED_HOSTS|" "$REPO_DIR/.env"
|
|
||||||
|
|
||||||
debug_log "Configuration saved to .env file"
|
debug_log "Configuration saved to .env file"
|
||||||
log_success "Configuration saved to .env file"
|
log_success "Configuration saved to .env file"
|
||||||
}
|
}
|
||||||
@@ -725,9 +772,9 @@ validate_secrets() {
|
|||||||
debug_log "AUTHELIA_STORAGE_ENCRYPTION_KEY is missing"
|
debug_log "AUTHELIA_STORAGE_ENCRYPTION_KEY is missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "${AUTHELIA_ADMIN_PASSWORD:-}" ]; then
|
if [ -z "${AUTHELIA_ADMIN_PASSWORD_HASH:-}" ]; then
|
||||||
missing_secrets="$missing_secrets AUTHELIA_ADMIN_PASSWORD"
|
missing_secrets="$missing_secrets AUTHELIA_ADMIN_PASSWORD_HASH"
|
||||||
debug_log "AUTHELIA_ADMIN_PASSWORD is missing"
|
debug_log "AUTHELIA_ADMIN_PASSWORD_HASH is missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check other required variables
|
# Check other required variables
|
||||||
@@ -752,100 +799,42 @@ validate_secrets() {
|
|||||||
debug_log "Secret validation passed"
|
debug_log "Secret validation passed"
|
||||||
}
|
}
|
||||||
|
|
||||||
# System setup function (Docker, directories, etc.)
|
# Install NVIDIA drivers function
|
||||||
system_setup() {
|
install_nvidia() {
|
||||||
log_info "Performing system setup..."
|
log_info "Installing NVIDIA drivers and Docker support..."
|
||||||
|
|
||||||
# Check if running as root for system setup
|
# Check if running as root
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
log_warning "System setup requires root privileges. Running with sudo..."
|
log_warning "NVIDIA installation requires root privileges. Running with sudo..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the actual user who invoked sudo
|
# Check for NVIDIA GPU
|
||||||
ACTUAL_USER=${SUDO_USER:-$USER}
|
if ! lspci | grep -i nvidia > /dev/null; then
|
||||||
|
log_warning "No NVIDIA GPU detected. Skipping NVIDIA driver installation."
|
||||||
# Step 1: System Update
|
return
|
||||||
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 sshpass
|
|
||||||
|
|
||||||
# Step 3: Install Docker
|
|
||||||
log_info "Step 3/10: Installing Docker..."
|
|
||||||
if command -v docker &> /dev/null && docker --version &> /dev/null; then
|
|
||||||
log_success "Docker is already installed ($(docker --version))"
|
|
||||||
# Check if user is in docker group
|
|
||||||
if ! groups "$ACTUAL_USER" | grep -q docker; then
|
|
||||||
log_info "Adding $ACTUAL_USER to docker group..."
|
|
||||||
usermod -aG docker "$ACTUAL_USER"
|
|
||||||
NEEDS_LOGOUT=true
|
|
||||||
fi
|
|
||||||
# Check if Docker service is running
|
|
||||||
if ! systemctl is-active --quiet docker; then
|
|
||||||
log_warning "Docker service is not running, starting it..."
|
|
||||||
systemctl start docker
|
|
||||||
systemctl enable docker
|
|
||||||
log_success "Docker service started and enabled"
|
|
||||||
else
|
|
||||||
log_info "Docker service is already running"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
curl -fsSL https://get.docker.com | sh
|
|
||||||
usermod -aG docker "$ACTUAL_USER"
|
|
||||||
NEEDS_LOGOUT=true
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 4: Install Docker Compose
|
# Add NVIDIA repository
|
||||||
log_info "Step 4/10: Installing Docker Compose..."
|
log_info "Adding NVIDIA repository..."
|
||||||
if command -v docker-compose &> /dev/null && docker-compose --version &> /dev/null; then
|
apt-get update
|
||||||
log_success "Docker Compose is already installed ($(docker-compose --version))"
|
apt-get install -y software-properties-common
|
||||||
else
|
add-apt-repository -y ppa:graphics-drivers/ppa
|
||||||
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
apt-get update
|
||||||
chmod +x /usr/local/bin/docker-compose
|
|
||||||
log_success "Docker Compose installed ($(docker-compose --version))"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 5: Generate shared CA for multi-server TLS
|
# Install NVIDIA drivers (latest)
|
||||||
log_info "Step 5/10: Generating shared CA certificate for multi-server TLS..."
|
log_info "Installing NVIDIA drivers..."
|
||||||
generate_shared_ca
|
apt-get install -y nvidia-driver-470 # Adjust version as needed
|
||||||
|
|
||||||
# Step 6: Configure Docker TLS
|
# Install NVIDIA Docker support
|
||||||
log_info "Step 6/10: Configuring Docker TLS..."
|
log_info "Installing NVIDIA Docker support..."
|
||||||
setup_docker_tls
|
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
|
||||||
|
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | apt-key add -
|
||||||
|
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | tee /etc/apt/sources.list.d/nvidia-docker.list
|
||||||
|
apt-get update && apt-get install -y nvidia-docker2
|
||||||
|
systemctl restart docker
|
||||||
|
|
||||||
# Step 7: Configure UFW firewall
|
log_success "NVIDIA drivers and Docker support installed. A reboot may be required."
|
||||||
log_info "Step 7/10: Configuring firewall..."
|
|
||||||
ufw --force enable
|
|
||||||
ufw allow ssh
|
|
||||||
ufw allow 80
|
|
||||||
ufw allow 443
|
|
||||||
ufw allow 2376/tcp # Docker TLS port
|
|
||||||
log_success "Firewall configured"
|
|
||||||
|
|
||||||
# Step 8: Configure automatic updates
|
|
||||||
log_info "Step 8/10: Configuring automatic updates..."
|
|
||||||
dpkg-reconfigure -f noninteractive unattended-upgrades
|
|
||||||
|
|
||||||
# Step 10: Create Docker networks
|
|
||||||
log_info "Step 10/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 9: Set proper ownership
|
|
||||||
log_info "Step 9/10: Setting directory ownership..."
|
|
||||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt
|
|
||||||
|
|
||||||
log_success "System setup completed!"
|
|
||||||
echo ""
|
|
||||||
if [ "$NEEDS_LOGOUT" = true ]; then
|
|
||||||
log_info "Please log out and back in for Docker group changes to take effect."
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Deploy Dockge function
|
# Deploy Dockge function
|
||||||
@@ -861,17 +850,17 @@ deploy_dockge() {
|
|||||||
sudo chown "$ACTUAL_USER:$ACTUAL_USER" /opt/dockge/.env
|
sudo chown "$ACTUAL_USER:$ACTUAL_USER" /opt/dockge/.env
|
||||||
|
|
||||||
# Remove sensitive variables from dockge .env (Dockge doesn't need them)
|
# Remove sensitive variables from dockge .env (Dockge doesn't need them)
|
||||||
sed -i '/^AUTHELIA_ADMIN_PASSWORD=/d' /opt/dockge/.env
|
sed -i '/^AUTHELIA_ADMIN_PASSWORD_HASH=/d' /opt/dockge/.env
|
||||||
sed -i '/^AUTHELIA_JWT_SECRET=/d' /opt/dockge/.env
|
sed -i '/^AUTHELIA_JWT_SECRET=/d' /opt/dockge/.env
|
||||||
sed -i '/^AUTHELIA_SESSION_SECRET=/d' /opt/dockge/.env
|
sed -i '/^AUTHELIA_SESSION_SECRET=/d' /opt/dockge/.env
|
||||||
sed -i '/^AUTHELIA_STORAGE_ENCRYPTION_KEY=/d' /opt/dockge/.env
|
sed -i '/^AUTHELIA_STORAGE_ENCRYPTION_KEY=/d' /opt/dockge/.env
|
||||||
|
|
||||||
# Replace placeholders in Dockge compose file
|
# Replace placeholders in Dockge compose file
|
||||||
replace_env_placeholders "/opt/dockge/docker-compose.yml"
|
localize_yml_file "/opt/dockge/docker-compose.yml"
|
||||||
|
|
||||||
# Deploy Dockge stack
|
# Deploy Dockge stack
|
||||||
cd /opt/dockge
|
cd /opt/dockge
|
||||||
docker compose up -d
|
run_cmd docker compose up -d || true
|
||||||
log_success "Dockge deployed"
|
log_success "Dockge deployed"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
@@ -908,7 +897,7 @@ deploy_core() {
|
|||||||
sed -i '/^HOMEPAGE_VAR_/d' /opt/stacks/core/.env
|
sed -i '/^HOMEPAGE_VAR_/d' /opt/stacks/core/.env
|
||||||
|
|
||||||
# Replace placeholders in core compose file (fail on missing critical vars)
|
# Replace placeholders in core compose file (fail on missing critical vars)
|
||||||
replace_env_placeholders "/opt/stacks/core/docker-compose.yml" true
|
localize_yml_file "/opt/stacks/core/docker-compose.yml" true
|
||||||
|
|
||||||
# Copy and configure Traefik config
|
# Copy and configure Traefik config
|
||||||
debug_log "Setting up Traefik configuration"
|
debug_log "Setting up Traefik configuration"
|
||||||
@@ -943,9 +932,9 @@ deploy_core() {
|
|||||||
find /opt/stacks/core/traefik -name "*.yml" -type f | while read -r config_file; do
|
find /opt/stacks/core/traefik -name "*.yml" -type f | while read -r config_file; do
|
||||||
# Don't fail on missing variables for external host files (they're optional)
|
# Don't fail on missing variables for external host files (they're optional)
|
||||||
if [[ "$config_file" == *external-host* ]]; then
|
if [[ "$config_file" == *external-host* ]]; then
|
||||||
replace_env_placeholders "$config_file" false
|
localize_yml_file "$config_file" false
|
||||||
else
|
else
|
||||||
replace_env_placeholders "$config_file" true
|
localize_yml_file "$config_file" true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -966,7 +955,7 @@ deploy_core() {
|
|||||||
# Replace all placeholders in Authelia config files
|
# Replace all placeholders in Authelia config files
|
||||||
debug_log "Replacing placeholders in Authelia config files"
|
debug_log "Replacing placeholders in Authelia config files"
|
||||||
find /opt/stacks/core/authelia -name "*.yml" -type f | while read -r config_file; do
|
find /opt/stacks/core/authelia -name "*.yml" -type f | while read -r config_file; do
|
||||||
replace_env_placeholders "$config_file" true
|
localize_yml_file "$config_file" true
|
||||||
done
|
done
|
||||||
|
|
||||||
# Remove invalid session.cookies section from Authelia config (not supported in v4.37.5)
|
# Remove invalid session.cookies section from Authelia config (not supported in v4.37.5)
|
||||||
@@ -988,7 +977,7 @@ deploy_core() {
|
|||||||
# Deploy core stack
|
# Deploy core stack
|
||||||
debug_log "Deploying core stack with docker compose"
|
debug_log "Deploying core stack with docker compose"
|
||||||
cd /opt/stacks/core
|
cd /opt/stacks/core
|
||||||
docker compose up -d
|
run_cmd docker compose up -d || true
|
||||||
log_success "Core infrastructure deployed"
|
log_success "Core infrastructure deployed"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
@@ -1026,7 +1015,7 @@ deploy_infrastructure() {
|
|||||||
sed -i '/^HOMEPAGE_VAR_/d' /opt/stacks/infrastructure/.env
|
sed -i '/^HOMEPAGE_VAR_/d' /opt/stacks/infrastructure/.env
|
||||||
|
|
||||||
# Replace placeholders in infrastructure compose file
|
# Replace placeholders in infrastructure compose file
|
||||||
replace_env_placeholders "/opt/stacks/infrastructure/docker-compose.yml"
|
localize_yml_file "/opt/stacks/infrastructure/docker-compose.yml"
|
||||||
|
|
||||||
# Copy any additional config directories
|
# Copy any additional config directories
|
||||||
for config_dir in "$REPO_DIR/docker-compose/infrastructure"/*/; do
|
for config_dir in "$REPO_DIR/docker-compose/infrastructure"/*/; do
|
||||||
@@ -1042,11 +1031,11 @@ deploy_infrastructure() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Replace placeholders in infrastructure compose file
|
# Replace placeholders in infrastructure compose file
|
||||||
replace_env_placeholders "/opt/stacks/infrastructure/docker-compose.yml"
|
localize_yml_file "/opt/stacks/infrastructure/docker-compose.yml"
|
||||||
|
|
||||||
# Deploy infrastructure stack
|
# Deploy infrastructure stack
|
||||||
cd /opt/stacks/infrastructure
|
cd /opt/stacks/infrastructure
|
||||||
docker compose up -d
|
run_cmd docker compose up -d || true
|
||||||
log_success "Infrastructure stack deployed"
|
log_success "Infrastructure stack deployed"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
@@ -1082,7 +1071,7 @@ deploy_dashboards() {
|
|||||||
sed -i '/^FORMIO_/d' /opt/stacks/dashboards/.env
|
sed -i '/^FORMIO_/d' /opt/stacks/dashboards/.env
|
||||||
|
|
||||||
# Replace placeholders in dashboards compose file
|
# Replace placeholders in dashboards compose file
|
||||||
replace_env_placeholders "/opt/stacks/dashboards/docker-compose.yml"
|
localize_yml_file "/opt/stacks/dashboards/docker-compose.yml"
|
||||||
|
|
||||||
# Copy homepage config
|
# Copy homepage config
|
||||||
if [ -d "$REPO_DIR/docker-compose/dashboards/homepage" ]; then
|
if [ -d "$REPO_DIR/docker-compose/dashboards/homepage" ]; then
|
||||||
@@ -1091,7 +1080,7 @@ deploy_dashboards() {
|
|||||||
|
|
||||||
# Replace placeholders in homepage config files
|
# Replace placeholders in homepage config files
|
||||||
find /opt/stacks/dashboards/homepage -name "*.yaml" -type f | while read -r config_file; do
|
find /opt/stacks/dashboards/homepage -name "*.yaml" -type f | while read -r config_file; do
|
||||||
replace_env_placeholders "$config_file"
|
localize_yml_file "$config_file"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Remove remote server entries from homepage services for single-server setup
|
# Remove remote server entries from homepage services for single-server setup
|
||||||
@@ -1102,7 +1091,7 @@ deploy_dashboards() {
|
|||||||
|
|
||||||
# Process template files and rename them
|
# Process template files and rename them
|
||||||
find /opt/stacks/dashboards/homepage -name "*.template" -type f | while read -r template_file; do
|
find /opt/stacks/dashboards/homepage -name "*.template" -type f | while read -r template_file; do
|
||||||
replace_env_placeholders "$template_file"
|
localize_yml_file "$template_file"
|
||||||
# Rename template file to remove .template extension
|
# Rename template file to remove .template extension
|
||||||
new_file="${template_file%.template}"
|
new_file="${template_file%.template}"
|
||||||
mv "$template_file" "$new_file"
|
mv "$template_file" "$new_file"
|
||||||
@@ -1111,11 +1100,11 @@ deploy_dashboards() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Replace placeholders in dashboards compose file
|
# Replace placeholders in dashboards compose file
|
||||||
replace_env_placeholders "/opt/stacks/dashboards/docker-compose.yml"
|
localize_yml_file "/opt/stacks/dashboards/docker-compose.yml"
|
||||||
|
|
||||||
# Deploy dashboards stack
|
# Deploy dashboards stack
|
||||||
cd /opt/stacks/dashboards
|
cd /opt/stacks/dashboards
|
||||||
docker compose up -d
|
run_cmd docker compose up -d || true
|
||||||
log_success "Dashboard stack deployed"
|
log_success "Dashboard stack deployed"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
@@ -1126,7 +1115,7 @@ perform_deployment() {
|
|||||||
log_info "Starting deployment..."
|
log_info "Starting deployment..."
|
||||||
|
|
||||||
# Initialize missing vars summary
|
# Initialize missing vars summary
|
||||||
MISSING_VARS_SUMMARY=""
|
GLOBAL_MISSING_VARS=""
|
||||||
TLS_ISSUES_SUMMARY=""
|
TLS_ISSUES_SUMMARY=""
|
||||||
|
|
||||||
# Switch back to regular user if we were running as root
|
# Switch back to regular user if we were running as root
|
||||||
@@ -1142,6 +1131,22 @@ perform_deployment() {
|
|||||||
load_env_file_safely "$REPO_DIR/.env"
|
load_env_file_safely "$REPO_DIR/.env"
|
||||||
debug_log "Environment loaded, DOMAIN=$DOMAIN, SERVER_IP=$SERVER_IP"
|
debug_log "Environment loaded, DOMAIN=$DOMAIN, SERVER_IP=$SERVER_IP"
|
||||||
|
|
||||||
|
# Generate Authelia password hash if needed
|
||||||
|
if [ "$AUTHELIA_ADMIN_PASSWORD_HASH" = "generate-with-openssl-rand-hex-64" ] || [ -z "$AUTHELIA_ADMIN_PASSWORD_HASH" ]; then
|
||||||
|
log_info "Generating Authelia password hash..."
|
||||||
|
if ! docker images | grep -q authelia/authelia; then
|
||||||
|
docker pull authelia/authelia:latest > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
AUTHELIA_ADMIN_PASSWORD_HASH=$(docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password "$DEFAULT_PASSWORD" 2>&1 | grep -o '\$argon2id.*')
|
||||||
|
if [ -z "$AUTHELIA_ADMIN_PASSWORD_HASH" ]; then
|
||||||
|
log_error "Failed to generate Authelia password hash."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Save it back to .env
|
||||||
|
sed -i "s%AUTHELIA_ADMIN_PASSWORD_HASH=.*%AUTHELIA_ADMIN_PASSWORD_HASH=\"$AUTHELIA_ADMIN_PASSWORD_HASH\"%" "$REPO_DIR/.env"
|
||||||
|
log_success "Authelia password hash generated and saved"
|
||||||
|
fi
|
||||||
|
|
||||||
# Step 1: Create required directories
|
# Step 1: Create required directories
|
||||||
log_info "Step 1: Creating required directories..."
|
log_info "Step 1: Creating required directories..."
|
||||||
sudo mkdir -p /opt/stacks/core || { log_error "Failed to create /opt/stacks/core"; exit 1; }
|
sudo mkdir -p /opt/stacks/core || { log_error "Failed to create /opt/stacks/core"; exit 1; }
|
||||||
@@ -1196,9 +1201,9 @@ perform_deployment() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Report any missing variables
|
# Report any missing variables
|
||||||
if [ -n "$MISSING_VARS_SUMMARY" ]; then
|
if [ -n "$GLOBAL_MISSING_VARS" ]; then
|
||||||
log_warning "The following environment variables were missing and may cause issues:"
|
log_warning "The following environment variables were missing and may cause issues:"
|
||||||
echo "$MISSING_VARS_SUMMARY"
|
echo "$GLOBAL_MISSING_VARS"
|
||||||
log_info "Please update your .env file and redeploy affected stacks."
|
log_info "Please update your .env file and redeploy affected stacks."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1275,7 +1280,7 @@ setup_stacks_for_dockge() {
|
|||||||
sudo chown "$ACTUAL_USER:$ACTUAL_USER" "$STACK_DIR/.env"
|
sudo chown "$ACTUAL_USER:$ACTUAL_USER" "$STACK_DIR/.env"
|
||||||
|
|
||||||
# Remove sensitive/unnecessary variables from stack .env
|
# Remove sensitive/unnecessary variables from stack .env
|
||||||
sed -i '/^AUTHELIA_ADMIN_PASSWORD=/d' "$STACK_DIR/.env"
|
sed -i '/^AUTHELIA_ADMIN_PASSWORD_HASH=/d' "$STACK_DIR/.env"
|
||||||
sed -i '/^AUTHELIA_JWT_SECRET=/d' "$STACK_DIR/.env"
|
sed -i '/^AUTHELIA_JWT_SECRET=/d' "$STACK_DIR/.env"
|
||||||
sed -i '/^AUTHELIA_SESSION_SECRET=/d' "$STACK_DIR/.env"
|
sed -i '/^AUTHELIA_SESSION_SECRET=/d' "$STACK_DIR/.env"
|
||||||
sed -i '/^AUTHELIA_STORAGE_ENCRYPTION_KEY=/d' "$STACK_DIR/.env"
|
sed -i '/^AUTHELIA_STORAGE_ENCRYPTION_KEY=/d' "$STACK_DIR/.env"
|
||||||
@@ -1300,13 +1305,18 @@ setup_stacks_for_dockge() {
|
|||||||
sed -i '/^HOMEPAGE_VAR_/d' "$STACK_DIR/.env"
|
sed -i '/^HOMEPAGE_VAR_/d' "$STACK_DIR/.env"
|
||||||
|
|
||||||
# Replace placeholders in the compose file
|
# Replace placeholders in the compose file
|
||||||
replace_env_placeholders "$STACK_DIR/docker-compose.yml"
|
localize_yml_file "$STACK_DIR/docker-compose.yml"
|
||||||
|
|
||||||
# Copy any additional config directories
|
# Copy any additional config directories
|
||||||
for config_dir in "$REPO_STACK_DIR"/*/; do
|
for config_dir in "$REPO_STACK_DIR"/*/; do
|
||||||
if [ -d "$config_dir" ] && [ "$(basename "$config_dir")" != "." ]; then
|
if [ -d "$config_dir" ] && [ "$(basename "$config_dir")" != "." ]; then
|
||||||
cp -r "$config_dir" "$STACK_DIR/"
|
cp -r "$config_dir" "$STACK_DIR/"
|
||||||
sudo chown -R "$ACTUAL_USER:$ACTUAL_USER" "$STACK_DIR/$(basename "$config_dir")"
|
sudo chown -R "$ACTUAL_USER:$ACTUAL_USER" "$STACK_DIR/$(basename "$config_dir")"
|
||||||
|
|
||||||
|
# Replace placeholders in config files
|
||||||
|
find "$STACK_DIR/$(basename "$config_dir")" -name "*.yml" -o -name "*.yaml" | while read -r config_file; do
|
||||||
|
localize_yml_file "$config_file"
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -1340,12 +1350,226 @@ show_main_menu() {
|
|||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Show help function
|
||||||
|
show_help() {
|
||||||
|
echo "EZ-Homelab Setup & Deployment Script"
|
||||||
|
echo ""
|
||||||
|
echo "Usage: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo " -d, --dry-run Enable dry-run mode (show commands without executing)"
|
||||||
|
echo " -c, --config FILE Specify configuration file (default: .env)"
|
||||||
|
echo " -t, --test Run in test mode (validate configs without deploying)"
|
||||||
|
echo " -v, --validate-only Only validate configuration and exit"
|
||||||
|
echo " --verbose Enable verbose console logging"
|
||||||
|
echo ""
|
||||||
|
echo "If no options are provided, the interactive menu will be shown."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate configuration function
|
||||||
|
validate_configuration() {
|
||||||
|
log_info "Validating configuration..."
|
||||||
|
|
||||||
|
# Check if .env file exists
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
log_error "Configuration file .env not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load and check required environment variables
|
||||||
|
if ! load_env_file; then
|
||||||
|
log_error "Failed to load .env file."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for critical variables
|
||||||
|
local required_vars=("DOMAIN" "SERVER_IP" "DUCKDNS_TOKEN" "AUTHELIA_ADMIN_PASSWORD_HASH")
|
||||||
|
local missing_vars=()
|
||||||
|
for var in "${required_vars[@]}"; do
|
||||||
|
if [ -z "${!var}" ]; then
|
||||||
|
missing_vars+=("$var")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#missing_vars[@]} -gt 0 ]; then
|
||||||
|
log_error "Missing required environment variables: ${missing_vars[*]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check Docker Compose files syntax
|
||||||
|
log_info "Checking Docker Compose file syntax..."
|
||||||
|
if command -v docker-compose &> /dev/null; then
|
||||||
|
if ! docker-compose -f docker-compose/core/docker-compose.yml config -q; then
|
||||||
|
log_error "Invalid syntax in core docker-compose.yml"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_success "Core docker-compose.yml syntax is valid"
|
||||||
|
else
|
||||||
|
log_warning "docker-compose not available for syntax check"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check network connectivity (basic)
|
||||||
|
log_info "Checking network connectivity..."
|
||||||
|
if ! ping -c 1 google.com &> /dev/null; then
|
||||||
|
log_warning "No internet connectivity detected"
|
||||||
|
else
|
||||||
|
log_success "Internet connectivity confirmed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Configuration validation completed successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command line arguments function
|
||||||
|
parse_args() {
|
||||||
|
DRY_RUN=false
|
||||||
|
CONFIG_FILE=".env"
|
||||||
|
TEST_MODE=false
|
||||||
|
VALIDATE_ONLY=false
|
||||||
|
VERBOSE=false
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-d|--dry-run)
|
||||||
|
DRY_RUN=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-c|--config)
|
||||||
|
CONFIG_FILE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-t|--test)
|
||||||
|
TEST_MODE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-v|--validate-only)
|
||||||
|
VALIDATE_ONLY=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--verbose)
|
||||||
|
VERBOSE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare deployment environment
|
||||||
|
prepare_deployment() {
|
||||||
|
# Handle special menu options
|
||||||
|
if [ "$FORCE_SYSTEM_SETUP" = true ]; then
|
||||||
|
log_info "Installing prerequisites..."
|
||||||
|
# Run the prerequisites script as root
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
./scripts/install-prerequisites.sh
|
||||||
|
else
|
||||||
|
sudo ./scripts/install-prerequisites.sh
|
||||||
|
fi
|
||||||
|
log_success "Prerequisites installed successfully."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$INSTALL_NVIDIA" = true ]; then
|
||||||
|
log_info "Installing NVIDIA drivers..."
|
||||||
|
install_nvidia
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if system setup is needed
|
||||||
|
# Only run system setup if Docker is not installed OR if running as root and Docker setup hasn't been done
|
||||||
|
DOCKER_INSTALLED=false
|
||||||
|
if command -v docker &> /dev/null && docker --version &> /dev/null; then
|
||||||
|
DOCKER_INSTALLED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if current user is in docker group (or if we're root and will add them)
|
||||||
|
USER_IN_DOCKER_GROUP=false
|
||||||
|
if groups "$USER" 2>/dev/null | grep -q docker; then
|
||||||
|
USER_IN_DOCKER_GROUP=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
# Running as root - check if we need to do system setup
|
||||||
|
if [ "$DOCKER_INSTALLED" = false ] || [ "$USER_IN_DOCKER_GROUP" = false ]; then
|
||||||
|
log_info "Docker not fully installed or user not in docker group. Performing system setup..."
|
||||||
|
./scripts/install-prerequisites.sh
|
||||||
|
echo ""
|
||||||
|
log_info "System setup complete. Please log out and back in, then run this script again."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
log_info "Docker is already installed and user is in docker group. Skipping system setup."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Not running as root
|
||||||
|
if [ "$DOCKER_INSTALLED" = false ]; then
|
||||||
|
log_error "Docker is not installed. Please run this script with sudo to perform system setup."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$USER_IN_DOCKER_GROUP" = false ]; then
|
||||||
|
log_error "Current user is not in the docker group. Please log out and back in, or run with sudo to fix group membership."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure required directories exist
|
||||||
|
log_info "Ensuring required directories exist..."
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
ACTUAL_USER=${SUDO_USER:-$USER}
|
||||||
|
mkdir -p /opt/stacks /opt/dockge
|
||||||
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt
|
||||||
|
else
|
||||||
|
mkdir -p /opt/stacks /opt/dockge
|
||||||
|
fi
|
||||||
|
log_success "Directories prepared"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run command function (handles dry-run and test modes)
|
||||||
|
run_cmd() {
|
||||||
|
if [ "$DRY_RUN" = true ] || [ "$TEST_MODE" = true ]; then
|
||||||
|
echo "[DRY-RUN/TEST] $@"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if "$@"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Command failed: $@"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Main logic
|
# Main logic
|
||||||
main() {
|
main() {
|
||||||
debug_log "main() called with arguments: $@"
|
debug_log "main() called with arguments: $@"
|
||||||
log_info "EZ-Homelab Unified Setup & Deployment Script"
|
log_info "EZ-Homelab Unified Setup & Deployment Script"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = true ]; then
|
||||||
|
log_info "Dry-run mode enabled. Commands will be displayed but not executed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$VALIDATE_ONLY" = true ]; then
|
||||||
|
log_info "Validation mode enabled. Checking configuration..."
|
||||||
|
validate_configuration
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TEST_MODE" = true ]; then
|
||||||
|
log_info "Test mode enabled. Will validate and simulate deployment."
|
||||||
|
fi
|
||||||
|
|
||||||
# Load existing configuration
|
# Load existing configuration
|
||||||
ENV_EXISTS=false
|
ENV_EXISTS=false
|
||||||
if load_env_file; then
|
if load_env_file; then
|
||||||
@@ -1381,6 +1605,18 @@ main() {
|
|||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
log_info "Selected: Deploy Additional Server"
|
log_info "Selected: Deploy Additional Server"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ IMPORTANT: Deploying an additional server requires an existing core server to be already deployed."
|
||||||
|
echo "The core server provides essential services like Traefik, Authelia, and shared TLS certificates."
|
||||||
|
echo ""
|
||||||
|
read -p "Do you have an existing core server deployed? (y/N): " -n 1 -r
|
||||||
|
echo ""
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Returning to main menu. Please deploy a core server first using Option 2."
|
||||||
|
echo ""
|
||||||
|
sleep 2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
DEPLOY_CORE=false
|
DEPLOY_CORE=false
|
||||||
DEPLOY_INFRASTRUCTURE=true
|
DEPLOY_INFRASTRUCTURE=true
|
||||||
DEPLOY_DASHBOARDS=false
|
DEPLOY_DASHBOARDS=false
|
||||||
@@ -1411,71 +1647,8 @@ main() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Handle special menu options
|
# Prepare deployment environment
|
||||||
if [ "$FORCE_SYSTEM_SETUP" = true ]; then
|
prepare_deployment
|
||||||
log_info "Installing prerequisites..."
|
|
||||||
system_setup "$@"
|
|
||||||
log_success "Prerequisites installed successfully."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$INSTALL_NVIDIA" = true ]; then
|
|
||||||
log_info "Installing NVIDIA drivers..."
|
|
||||||
# TODO: Implement NVIDIA driver installation
|
|
||||||
log_warning "NVIDIA driver installation not yet implemented."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if system setup is needed
|
|
||||||
# Only run system setup if Docker is not installed OR if running as root and Docker setup hasn't been done
|
|
||||||
DOCKER_INSTALLED=false
|
|
||||||
if command -v docker &> /dev/null && docker --version &> /dev/null; then
|
|
||||||
DOCKER_INSTALLED=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if current user is in docker group (or if we're root and will add them)
|
|
||||||
USER_IN_DOCKER_GROUP=false
|
|
||||||
if groups "$USER" 2>/dev/null | grep -q docker; then
|
|
||||||
USER_IN_DOCKER_GROUP=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$EUID" -eq 0 ]; then
|
|
||||||
# Running as root - check if we need to do system setup
|
|
||||||
if [ "$DOCKER_INSTALLED" = false ] || [ "$USER_IN_DOCKER_GROUP" = false ]; then
|
|
||||||
log_info "Docker not fully installed or user not in docker group. Performing system setup..."
|
|
||||||
system_setup "$@"
|
|
||||||
echo ""
|
|
||||||
log_info "System setup complete. Please log out and back in, then run this script again."
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
log_info "Docker is already installed and user is in docker group. Skipping system setup."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# Not running as root
|
|
||||||
if [ "$DOCKER_INSTALLED" = false ]; then
|
|
||||||
log_error "Docker is not installed. Please run this script with sudo to perform system setup."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ "$USER_IN_DOCKER_GROUP" = false ]; then
|
|
||||||
log_error "Current user is not in the docker group. Please log out and back in, or run with sudo to fix group membership."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure required directories exist
|
|
||||||
log_info "Ensuring required directories exist..."
|
|
||||||
if [ "$EUID" -eq 0 ]; then
|
|
||||||
mkdir -p /opt/stacks/core
|
|
||||||
mkdir -p /opt/stacks/infrastructure
|
|
||||||
mkdir -p /opt/stacks/dashboards
|
|
||||||
mkdir -p /opt/dockge
|
|
||||||
else
|
|
||||||
sudo mkdir -p /opt/stacks/core
|
|
||||||
sudo mkdir -p /opt/stacks/infrastructure
|
|
||||||
sudo mkdir -p /opt/stacks/dashboards
|
|
||||||
sudo mkdir -p /opt/dockge
|
|
||||||
fi
|
|
||||||
log_success "Directories ready"
|
|
||||||
|
|
||||||
# Prompt for configuration values
|
# Prompt for configuration values
|
||||||
validate_and_prompt_variables
|
validate_and_prompt_variables
|
||||||
@@ -1484,7 +1657,7 @@ main() {
|
|||||||
save_env_file
|
save_env_file
|
||||||
|
|
||||||
# Perform enhanced placeholder replacement across all config files
|
# Perform enhanced placeholder replacement across all config files
|
||||||
enhance_placeholder_replacement
|
localize_deployment
|
||||||
|
|
||||||
# Validate secrets for core deployment
|
# Validate secrets for core deployment
|
||||||
validate_secrets
|
validate_secrets
|
||||||
@@ -1498,25 +1671,25 @@ main() {
|
|||||||
echo "║ Deployment Complete! ║"
|
echo "║ Deployment Complete! ║"
|
||||||
echo "║ SSL Certificates may take a few minutes to be issued. ║"
|
echo "║ SSL Certificates may take a few minutes to be issued. ║"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
echo "║ https://dockge.kelinreij.duckdns.org ║"
|
echo "║ https://dockge.${DOMAIN} ║"
|
||||||
echo "║ http://192.168.4.4:5001 ║"
|
echo "║ http://${SERVER_IP}:5001 ║"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
echo "║ https://homepage.kelinreij.duckdns.org ║"
|
echo "║ https://homepage.${DOMAIN} ║"
|
||||||
echo "║ http://192.168.4.4:3003 ║"
|
echo "║ http://${SERVER_IP}:3003 ║"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
echo "╚═════════════════════════════════════════════════════════════╝"
|
echo "╚═════════════════════════════════════════════════════════════╝"
|
||||||
|
|
||||||
# Show consolidated warnings if any
|
# Show consolidated warnings if any
|
||||||
if [ -n "$MISSING_VARS_SUMMARY" ] || [ -n "$TLS_ISSUES_SUMMARY" ]; then
|
if [ -n "$GLOBAL_MISSING_VARS" ] || [ -n "$TLS_ISSUES_SUMMARY" ]; then
|
||||||
echo "╔═════════════════════════════════════════════════════════════╗"
|
echo "╔═════════════════════════════════════════════════════════════╗"
|
||||||
echo "║ ⚠️ WARNING ⚠️ ║"
|
echo "║ ⚠️ WARNING ⚠️ ║"
|
||||||
echo "║ The following variables were not defined ║"
|
echo "║ The following variables were not defined ║"
|
||||||
echo "║ If something isn't working as expected check these first ║"
|
echo "║ If something isn't working as expected check these first ║"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
|
|
||||||
if [ -n "$MISSING_VARS_SUMMARY" ]; then
|
if [ -n "$GLOBAL_MISSING_VARS" ]; then
|
||||||
log_warning "Missing Environment Variables:"
|
log_warning "Missing Environment Variables:"
|
||||||
echo "$MISSING_VARS_SUMMARY"
|
echo "$GLOBAL_MISSING_VARS"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
197
scripts/install-prerequisites.sh
Executable file
197
scripts/install-prerequisites.sh
Executable file
@@ -0,0 +1,197 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# EZ-Homelab Prerequisites Installation Script
|
||||||
|
|
||||||
|
# This script must be run as root or with sudo
|
||||||
|
# It performs system setup for Docker, networking, and security
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to generate shared CA for multi-server TLS
|
||||||
|
generate_shared_ca() {
|
||||||
|
local ca_dir="/opt/stacks/core/shared-ca"
|
||||||
|
mkdir -p "$ca_dir"
|
||||||
|
openssl genrsa -out "$ca_dir/ca-key.pem" 4096
|
||||||
|
openssl req -new -x509 -days 365 -key "$ca_dir/ca-key.pem" -sha256 -out "$ca_dir/ca.pem" -subj "/C=US/ST=State/L=City/O=Homelab/CN=Homelab-CA"
|
||||||
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$ca_dir"
|
||||||
|
log_success "Shared CA generated"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Setup Docker TLS function
|
||||||
|
setup_docker_tls() {
|
||||||
|
local TLS_DIR="/home/$ACTUAL_USER/EZ-Homelab/docker-tls"
|
||||||
|
|
||||||
|
# Create TLS directory
|
||||||
|
mkdir -p "$TLS_DIR"
|
||||||
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$TLS_DIR"
|
||||||
|
|
||||||
|
# Use shared CA if available, otherwise generate local CA
|
||||||
|
if [ -f "/opt/stacks/core/shared-ca/ca.pem" ] && [ -f "/opt/stacks/core/shared-ca/ca-key.pem" ]; then
|
||||||
|
log_info "Using shared CA certificate for Docker TLS..."
|
||||||
|
cp "/opt/stacks/core/shared-ca/ca.pem" "$TLS_DIR/ca.pem"
|
||||||
|
cp "/opt/stacks/core/shared-ca/ca-key.pem" "$TLS_DIR/ca-key.pem"
|
||||||
|
else
|
||||||
|
log_info "Generating local CA certificate for Docker TLS..."
|
||||||
|
# Generate CA
|
||||||
|
openssl genrsa -out "$TLS_DIR/ca-key.pem" 4096
|
||||||
|
openssl req -new -x509 -days 365 -key "$TLS_DIR/ca-key.pem" -sha256 -out "$TLS_DIR/ca.pem" -subj "/C=US/ST=State/L=City/O=Organization/CN=Docker-CA"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate server key and cert
|
||||||
|
openssl genrsa -out "$TLS_DIR/server-key.pem" 4096
|
||||||
|
openssl req -subj "/CN=$SERVER_IP" -new -key "$TLS_DIR/server-key.pem" -out "$TLS_DIR/server.csr"
|
||||||
|
echo "subjectAltName = DNS:$SERVER_IP,IP:$SERVER_IP,IP:127.0.0.1" > "$TLS_DIR/extfile.cnf"
|
||||||
|
openssl x509 -req -days 365 -in "$TLS_DIR/server.csr" -CA "$TLS_DIR/ca.pem" -CAkey "$TLS_DIR/ca-key.pem" -CAcreateserial -out "$TLS_DIR/server-cert.pem" -extfile "$TLS_DIR/extfile.cnf"
|
||||||
|
|
||||||
|
# Generate client key and cert
|
||||||
|
openssl genrsa -out "$TLS_DIR/client-key.pem" 4096
|
||||||
|
openssl req -subj "/CN=client" -new -key "$TLS_DIR/client-key.pem" -out "$TLS_DIR/client.csr"
|
||||||
|
openssl x509 -req -days 365 -in "$TLS_DIR/client.csr" -CA "$TLS_DIR/ca.pem" -CAkey "$TLS_DIR/ca-key.pem" -CAcreateserial -out "$TLS_DIR/client-cert.pem"
|
||||||
|
|
||||||
|
# Configure Docker daemon
|
||||||
|
tee /etc/docker/daemon.json > /dev/null <<EOF
|
||||||
|
{
|
||||||
|
"tls": true,
|
||||||
|
"tlsverify": true,
|
||||||
|
"tlscacert": "$TLS_DIR/ca.pem",
|
||||||
|
"tlscert": "$TLS_DIR/server-cert.pem",
|
||||||
|
"tlskey": "$TLS_DIR/server-key.pem"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update systemd service
|
||||||
|
sed -i 's|-H fd://|-H fd:// -H tcp://0.0.0.0:2376|' /lib/systemd/system/docker.service
|
||||||
|
|
||||||
|
# Reload and restart Docker
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart docker
|
||||||
|
|
||||||
|
log_success "Docker TLS configured on port 2376"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main system setup function
|
||||||
|
system_setup() {
|
||||||
|
log_info "Performing system setup..."
|
||||||
|
|
||||||
|
# Check if running as root for system setup
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
log_error "This script must be run as root or with sudo."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the actual user who invoked sudo
|
||||||
|
ACTUAL_USER=${SUDO_USER:-$USER}
|
||||||
|
|
||||||
|
# Get SERVER_IP from environment or prompt
|
||||||
|
if [ -z "$SERVER_IP" ]; then
|
||||||
|
read -p "Enter the server IP address: " SERVER_IP
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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 sshpass
|
||||||
|
|
||||||
|
# Step 3: Install Docker
|
||||||
|
log_info "Step 3/10: Installing Docker..."
|
||||||
|
if command -v docker &> /dev/null && docker --version &> /dev/null; then
|
||||||
|
log_success "Docker is already installed ($(docker --version))"
|
||||||
|
# Check if user is in docker group
|
||||||
|
if ! groups "$ACTUAL_USER" 2>/dev/null | grep -q docker; then
|
||||||
|
log_info "Adding $ACTUAL_USER to docker group..."
|
||||||
|
usermod -aG docker "$ACTUAL_USER"
|
||||||
|
NEEDS_LOGOUT=true
|
||||||
|
fi
|
||||||
|
# Check if Docker service is running
|
||||||
|
if ! systemctl is-active --quiet docker; then
|
||||||
|
log_warning "Docker service is not running, starting it..."
|
||||||
|
systemctl start docker
|
||||||
|
systemctl enable docker
|
||||||
|
log_success "Docker service started and enabled"
|
||||||
|
else
|
||||||
|
log_info "Docker service is already running"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
curl -fsSL https://get.docker.com | sh
|
||||||
|
usermod -aG docker "$ACTUAL_USER"
|
||||||
|
NEEDS_LOGOUT=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Install Docker Compose
|
||||||
|
log_info "Step 4/10: Installing Docker Compose..."
|
||||||
|
if command -v docker-compose &> /dev/null && docker-compose --version &> /dev/null; then
|
||||||
|
log_success "Docker Compose is already installed ($(docker-compose --version))"
|
||||||
|
else
|
||||||
|
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
|
||||||
|
log_success "Docker Compose installed ($(docker-compose --version))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 5: Generate shared CA for multi-server TLS
|
||||||
|
log_info "Step 5/10: Generating shared CA certificate for multi-server TLS..."
|
||||||
|
generate_shared_ca
|
||||||
|
|
||||||
|
# Step 6: Configure Docker TLS
|
||||||
|
log_info "Step 6/10: Configuring Docker TLS..."
|
||||||
|
setup_docker_tls
|
||||||
|
|
||||||
|
# Step 7: Configure UFW firewall
|
||||||
|
log_info "Step 7/10: Configuring firewall..."
|
||||||
|
ufw --force enable
|
||||||
|
ufw allow ssh
|
||||||
|
ufw allow 80
|
||||||
|
ufw allow 443
|
||||||
|
ufw allow 2376/tcp # Docker TLS port
|
||||||
|
log_success "Firewall configured"
|
||||||
|
|
||||||
|
# Step 8: Configure automatic updates
|
||||||
|
log_info "Step 8/10: Configuring automatic updates..."
|
||||||
|
dpkg-reconfigure -f noninteractive unattended-upgrades
|
||||||
|
|
||||||
|
# Step 10: Create Docker networks
|
||||||
|
log_info "Step 10/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 9: Set proper ownership
|
||||||
|
log_info "Step 9/10: Setting directory ownership..."
|
||||||
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt
|
||||||
|
|
||||||
|
log_success "System setup completed!"
|
||||||
|
echo ""
|
||||||
|
if [ "$NEEDS_LOGOUT" = true ]; then
|
||||||
|
log_info "Please log out and back in for Docker group changes to take effect."
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run the setup
|
||||||
|
system_setup "$@"
|
||||||
Reference in New Issue
Block a user