From 9ac61658a45c1c0f594189cc9b5a34e4762c7992 Mon Sep 17 00:00:00 2001 From: Kelin Date: Wed, 28 Jan 2026 03:17:43 -0500 Subject: [PATCH 1/5] Fix placeholder replacement in deploy_core and deploy_dashboards functions - Add manual sed replacements for remaining , , placeholders - Ensure x-dockge URLs and other variables are properly substituted - Improve robustness of environment variable replacement --- scripts/ez-homelab.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/ez-homelab.sh b/scripts/ez-homelab.sh index 457ce9c..9efd8b0 100755 --- a/scripts/ez-homelab.sh +++ b/scripts/ez-homelab.sh @@ -594,6 +594,10 @@ deploy_core() { # Replace placeholders in core compose file replace_env_placeholders "/opt/stacks/core/docker-compose.yml" + # Manual replacement for x-dockge section (ensure all placeholders are replaced) + sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/core/docker-compose.yml + sed -i "s/\${SERVER_IP}/${SERVER_IP}/g" /opt/stacks/core/docker-compose.yml + # Deploy core stack cd /opt/stacks/core docker compose up -d @@ -666,6 +670,10 @@ deploy_dashboards() { # Replace placeholders in dashboards compose file replace_env_placeholders "/opt/stacks/dashboards/docker-compose.yml" + # Manual replacement for any remaining placeholders + sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/dashboards/docker-compose.yml + sed -i "s/\${TZ}/${TZ}/g" /opt/stacks/dashboards/docker-compose.yml + # Deploy dashboards stack cd /opt/stacks/dashboards docker compose up -d From d62ea7f3df166fa2d471945a3a2fbd73505692e3 Mon Sep 17 00:00:00 2001 From: Kelin Date: Wed, 28 Jan 2026 03:24:26 -0500 Subject: [PATCH 2/5] Update core docker-compose.yml to match working local configuration - Change sablier volumes to use ./shared-ca:/certs:ro (matches script generation) - Fix x-dockge URLs to use http:// for local access and correct variable syntax - Ensure consistency with local working setup --- docker-compose/core/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose/core/docker-compose.yml b/docker-compose/core/docker-compose.yml index 505fe20..a930ed0 100644 --- a/docker-compose/core/docker-compose.yml +++ b/docker-compose/core/docker-compose.yml @@ -119,7 +119,7 @@ services: - DOCKER_TLS_VERIFY=1 - DOCKER_CERT_PATH=/certs volumes: - - ./sablier-certs:/certs:ro + - ./shared-ca:/certs:ro ports: - 10000:10000 labels: @@ -134,6 +134,6 @@ networks: x-dockge: urls: - https://auth.${DOMAIN} - - https://${SERVER_IP}:9091 + - http://${SERVER_IP}:9091 - https://traefik.${DOMAIN} - - https://${SERVER_IP}:8080 \ No newline at end of file + - http://${SERVER_IP}:8080 \ No newline at end of file From a22b44fe0dc88bd4e1c5547a275ab05ca5e05752 Mon Sep 17 00:00:00 2001 From: Kelin Date: Wed, 28 Jan 2026 03:29:15 -0500 Subject: [PATCH 3/5] Update Traefik dynamic config templates to match working local configuration - Rename external-host-production.yml to local-host-production.yml - Update local-host-production.yml with complete service list from working setup, generalized with variables - Update sablier.yml with complete middleware list from working setup, generalized with variables - Ensure all and placeholders are properly templated for script deployment --- ...oduction.yml => local-host-production.yml} | 80 +++++++++++++++---- config-templates/traefik/dynamic/sablier.yml | 14 +++- 2 files changed, 78 insertions(+), 16 deletions(-) rename config-templates/traefik/dynamic/{external-host-production.yml => local-host-production.yml} (90%) diff --git a/config-templates/traefik/dynamic/external-host-production.yml b/config-templates/traefik/dynamic/local-host-production.yml similarity index 90% rename from config-templates/traefik/dynamic/external-host-production.yml rename to config-templates/traefik/dynamic/local-host-production.yml index deda057..7dfce7b 100644 --- a/config-templates/traefik/dynamic/external-host-production.yml +++ b/config-templates/traefik/dynamic/local-host-production.yml @@ -22,16 +22,16 @@ http: - sablier-${SERVER_HOSTNAME}-bookstack@file - authelia@docker - vaultwarden-${SERVER_HOSTNAME}: - rule: "Host(`vault.${DOMAIN}`)" + bitwarden-${SERVER_HOSTNAME}: + rule: "Host(`bitwarden.${DOMAIN}`)" entryPoints: - websecure - service: vaultwarden-${SERVER_HOSTNAME} + service: bitwarden-${SERVER_HOSTNAME} tls: certResolver: letsencrypt - # SSO disabled for browser extension and mobile app compatibility - # middlewares: - # - sablier-${SERVER_HOSTNAME}-vaultwarden@file + middlewares: + - sablier-${SERVER_HOSTNAME}-bitwarden@file + - authelia@docker calibre-web-${SERVER_HOSTNAME}: rule: "Host(`calibre.${DOMAIN}`)" @@ -56,7 +56,7 @@ http: - authelia@docker dockge-${SERVER_HOSTNAME}: - rule: "Host(`jarvis.${DOMAIN}`)" + rule: "Host(`${SERVER_HOSTNAME}.${DOMAIN}`)" entryPoints: - websecure service: dockge-${SERVER_HOSTNAME} @@ -108,6 +108,18 @@ http: - sablier-${SERVER_HOSTNAME}-duplicati@file - authelia@docker + ez-assistant: + rule: "Host(`assistant.${DOMAIN}`)" + entryPoints: + - websecure + tls: + certResolver: letsencrypt + middlewares: + - authelia@docker + # - sablier-${SERVER_HOSTNAME}-assistant@file + - ez-assistant-websocket + service: ez-assistant + formio-${SERVER_HOSTNAME}: rule: "Host(`formio.${DOMAIN}`)" entryPoints: @@ -131,7 +143,7 @@ http: - authelia@docker glances-${SERVER_HOSTNAME}: - rule: "Host(`glances.jarvis.${DOMAIN}`)" + rule: "Host(`glances.${SERVER_HOSTNAME}.${DOMAIN}`)" entryPoints: - websecure service: glances-${SERVER_HOSTNAME} @@ -142,7 +154,7 @@ http: - authelia@docker homepage-${SERVER_HOSTNAME}: - rule: "Host(`homepage.jarvis.${DOMAIN}`)" + rule: "Host(`homepage.${SERVER_HOSTNAME}.${DOMAIN}`)" entryPoints: - websecure service: homepage-${SERVER_HOSTNAME} @@ -261,7 +273,7 @@ http: - authelia@docker qbittorrent-${SERVER_HOSTNAME}: - rule: "Host(`torrents.${DOMAIN}`)" + rule: "Host(`qbit.${DOMAIN}`)" entryPoints: - websecure service: qbittorrent-${SERVER_HOSTNAME} @@ -293,6 +305,17 @@ http: - sablier-${SERVER_HOSTNAME}-unmanic@file - authelia@docker + vaultwarden-${SERVER_HOSTNAME}: + rule: "Host(`vault.${DOMAIN}`)" + entryPoints: + - websecure + service: vaultwarden-${SERVER_HOSTNAME} + tls: + certResolver: letsencrypt + # SSO disabled for browser extension and mobile app compatibility + middlewares: + - sablier-${SERVER_HOSTNAME}-vaultwarden@file + wordpress-${SERVER_HOSTNAME}: rule: "Host(`knot-u.${DOMAIN}`)" entryPoints: @@ -304,7 +327,6 @@ http: - sablier-${SERVER_HOSTNAME}-wordpress@file - authelia@file -# Arr Services (no SSO for media apps) jellyseerr-${SERVER_HOSTNAME}: rule: "Host(`jellyseerr.${DOMAIN}`)" @@ -384,7 +406,23 @@ http: - authelia@docker -# Service Definitions + + +# Middleware Definitions + middlewares: + ez-assistant-websocket: + headers: + accessControlAllowHeaders: + - "Connection" + - "Upgrade" + accessControlAllowMethods: + - "GET" + - "POST" + - "OPTIONS" + accessControlMaxAge: 86400 + + + services: backrest-${SERVER_HOSTNAME}: loadBalancer: @@ -392,10 +430,10 @@ http: - url: "http://192.168.4.11:9898" passHostHeader: true - vaultwarden-${SERVER_HOSTNAME}: + bitwarden-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8091" + - url: "http://192.168.4.11:8000" passHostHeader: true bookstack-${SERVER_HOSTNAME}: @@ -446,6 +484,12 @@ http: - url: "http://192.168.4.11:8200" passHostHeader: true + ez-assistant: + loadbalancer: + servers: + - url: "http://192.168.4.11:18789" # Internal IP of ${SERVER_HOSTNAME} server + passHostHeader: true + formio-${SERVER_HOSTNAME}: loadBalancer: servers: @@ -527,7 +571,7 @@ http: openwebui-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:3000" + - url: "http://192.168.4.11:3004" passHostHeader: true qbittorrent-${SERVER_HOSTNAME}: @@ -548,6 +592,12 @@ http: - url: "http://192.168.4.11:8889" passHostHeader: true + vaultwarden-${SERVER_HOSTNAME}: + loadBalancer: + servers: + - url: "http://192.168.4.11:8091" + passHostHeader: true + wordpress-${SERVER_HOSTNAME}: loadBalancer: servers: diff --git a/config-templates/traefik/dynamic/sablier.yml b/config-templates/traefik/dynamic/sablier.yml index a08b23c..a1e8e58 100644 --- a/config-templates/traefik/dynamic/sablier.yml +++ b/config-templates/traefik/dynamic/sablier.yml @@ -116,6 +116,18 @@ http: theme: ghost show-details-by-default: true + sablier-${SERVER_HOSTNAME}-assistant: + plugin: + sablier: + sablierUrl: http://sablier-service:10000 + group: ${SERVER_HOSTNAME}-assistant + sessionDuration: 30m + ignoreUserAgent: curl + dynamic: + displayName: EZ-Assistant + theme: ghost + show-details-by-default: true + sablier-${SERVER_HOSTNAME}-formio: plugin: sablier: @@ -232,7 +244,7 @@ http: sessionDuration: 5m ignoreUserAgent: curl dynamic: - displayName: MediaWiki + displayName: mediawiki theme: ghost show-details-by-default: true From 7c72b5fdc68bd26cb35f36a3b7e4b1ee28ef3687 Mon Sep 17 00:00:00 2001 From: Kelin Date: Wed, 28 Jan 2026 03:32:12 -0500 Subject: [PATCH 4/5] Replace hardcoded server IP with variable in Traefik local-host config - Change all hardcoded 192.168.4.11 IPs to for proper templating - Ensures config works for any server IP during deployment --- .../traefik/dynamic/local-host-production.yml | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/config-templates/traefik/dynamic/local-host-production.yml b/config-templates/traefik/dynamic/local-host-production.yml index 7dfce7b..1861f81 100644 --- a/config-templates/traefik/dynamic/local-host-production.yml +++ b/config-templates/traefik/dynamic/local-host-production.yml @@ -427,181 +427,181 @@ http: backrest-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:9898" + - url: "http://${SERVER_IP}:9898" passHostHeader: true bitwarden-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8000" + - url: "http://${SERVER_IP}:8000" passHostHeader: true bookstack-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:6875" + - url: "http://${SERVER_IP}:6875" passHostHeader: true calibre-web-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8083" + - url: "http://${SERVER_IP}:8083" passHostHeader: true code-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8079" + - url: "http://${SERVER_IP}:8079" passHostHeader: true dockge-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:5001" + - url: "http://${SERVER_IP}:5001" passHostHeader: true dockhand-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:3003" + - url: "http://${SERVER_IP}:3003" passHostHeader: true dokuwiki-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8087" + - url: "http://${SERVER_IP}:8087" passHostHeader: true dozzle-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8085" + - url: "http://${SERVER_IP}:8085" passHostHeader: true duplicati-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8200" + - url: "http://${SERVER_IP}:8200" passHostHeader: true ez-assistant: loadbalancer: servers: - - url: "http://192.168.4.11:18789" # Internal IP of ${SERVER_HOSTNAME} server + - url: "http://${SERVER_IP}:18789" # Internal IP of ${SERVER_HOSTNAME} server passHostHeader: true formio-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:3002" + - url: "http://${SERVER_IP}:3002" passHostHeader: true gitea-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:3010" + - url: "http://${SERVER_IP}:3010" passHostHeader: true glances-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:61208" + - url: "http://${SERVER_IP}:61208" passHostHeader: true homarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:7575" + - url: "http://${SERVER_IP}:7575" passHostHeader: true homepage-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:3000" + - url: "http://${SERVER_IP}:3000" passHostHeader: true jellyfin-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8096" + - url: "http://${SERVER_IP}:8096" passHostHeader: true jupyter-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8890" + - url: "http://${SERVER_IP}:8890" passHostHeader: true kopia-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:51515" + - url: "http://${SERVER_IP}:51515" passHostHeader: true mealie-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:9000" + - url: "http://${SERVER_IP}:9000" passHostHeader: true mediawiki-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8086" + - url: "http://${SERVER_IP}:8086" passHostHeader: true motioneye-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8081" + - url: "http://${SERVER_IP}:8081" passHostHeader: true nextcloud-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8089" + - url: "http://${SERVER_IP}:8089" passHostHeader: true openkm-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:18080" + - url: "http://${SERVER_IP}:18080" passHostHeader: true openwebui-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:3004" + - url: "http://${SERVER_IP}:3004" passHostHeader: true qbittorrent-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8081" + - url: "http://${SERVER_IP}:8081" passHostHeader: true tdarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8265" + - url: "http://${SERVER_IP}:8265" passHostHeader: true unmanic-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8889" + - url: "http://${SERVER_IP}:8889" passHostHeader: true vaultwarden-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8091" + - url: "http://${SERVER_IP}:8091" passHostHeader: true wordpress-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8088" + - url: "http://${SERVER_IP}:8088" passHostHeader: true # Arr Services @@ -609,41 +609,41 @@ http: jellyseerr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:5055" + - url: "http://${SERVER_IP}:5055" passHostHeader: true prowlarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:9696" + - url: "http://${SERVER_IP}:9696" passHostHeader: true radarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:7878" + - url: "http://${SERVER_IP}:7878" passHostHeader: true sonarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8989" + - url: "http://${SERVER_IP}:8989" passHostHeader: true lidarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8686" + - url: "http://${SERVER_IP}:8686" passHostHeader: true readarr-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8787" + - url: "http://${SERVER_IP}:8787" passHostHeader: true mylar3-${SERVER_HOSTNAME}: loadBalancer: servers: - - url: "http://192.168.4.11:8090" + - url: "http://${SERVER_IP}:8090" passHostHeader: true From db0004ecab03d5ccd487c99748d0c78eccc8cfd6 Mon Sep 17 00:00:00 2001 From: Kelin Date: Wed, 28 Jan 2026 04:22:09 -0500 Subject: [PATCH 5/5] Fix sed delimiter issues in ez-homelab.sh - Change sed commands to use | delimiter instead of / to prevent regex interpretation of variables - Fixes 'unknown option to s' errors when DOMAIN contains special characters like dots - Affects deploy_core, deploy_dashboards, and Traefik dynamic config processing --- scripts/ez-homelab.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/ez-homelab.sh b/scripts/ez-homelab.sh index 9efd8b0..d2887e9 100755 --- a/scripts/ez-homelab.sh +++ b/scripts/ez-homelab.sh @@ -566,8 +566,8 @@ deploy_core() { sed -i "s/ACME_EMAIL_PLACEHOLDER/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/traefik/traefik.yml # Replace domain placeholders in traefik dynamic configs - find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s/\${DOMAIN}/${DOMAIN}/g" {} \; - find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s/\${SERVER_HOSTNAME}/${SERVER_HOSTNAME}/g" {} \; + find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s|\${DOMAIN}|${DOMAIN}|g" {} \; + find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s|\${SERVER_HOSTNAME}|${SERVER_HOSTNAME}|g" {} \; if [ -d "/opt/stacks/core/authelia" ]; then mv /opt/stacks/core/authelia /opt/stacks/core/authelia.backup.$(date +%Y%m%d_%H%M%S) @@ -575,8 +575,8 @@ deploy_core() { cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/ # Replace domain placeholders - sed -i "s/your-domain.duckdns.org/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml - sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml + sed -i "s|your-domain.duckdns.org|${DOMAIN}|g" /opt/stacks/core/authelia/configuration.yml + sed -i "s|\${DOMAIN}|${DOMAIN}|g" /opt/stacks/core/authelia/configuration.yml # Replace secret placeholders sed -i "s|\${AUTHELIA_JWT_SECRET}|${AUTHELIA_JWT_SECRET}|g" /opt/stacks/core/authelia/configuration.yml @@ -595,8 +595,8 @@ deploy_core() { replace_env_placeholders "/opt/stacks/core/docker-compose.yml" # Manual replacement for x-dockge section (ensure all placeholders are replaced) - sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/core/docker-compose.yml - sed -i "s/\${SERVER_IP}/${SERVER_IP}/g" /opt/stacks/core/docker-compose.yml + sed -i "s|\${DOMAIN}|${DOMAIN}|g" /opt/stacks/core/docker-compose.yml + sed -i "s|\${SERVER_IP}|${SERVER_IP}|g" /opt/stacks/core/docker-compose.yml # Deploy core stack cd /opt/stacks/core @@ -671,8 +671,8 @@ deploy_dashboards() { replace_env_placeholders "/opt/stacks/dashboards/docker-compose.yml" # Manual replacement for any remaining placeholders - sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/dashboards/docker-compose.yml - sed -i "s/\${TZ}/${TZ}/g" /opt/stacks/dashboards/docker-compose.yml + sed -i "s|\${DOMAIN}|${DOMAIN}|g" /opt/stacks/dashboards/docker-compose.yml + sed -i "s|\${TZ}|${TZ}|g" /opt/stacks/dashboards/docker-compose.yml # Deploy dashboards stack cd /opt/stacks/dashboards