From 09eff1e397c05f2d4f5ab2fe0ca44bc8ff1b6aaa Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 18:02:08 +0000 Subject: [PATCH 1/5] Fix Traccar install: add PostgreSQL database and autoheal Traccar's docker image no longer bundles the H2 driver, so the generated traccar.xml (org.h2.Driver / jdbc:h2:...) failed at startup with no working database. Add a postgres:15-alpine db container with a healthcheck, point traccar.xml/POSTGRES_* at it via .env, and gate traccar's startup on db being healthy. Also add a willfarrell/autoheal container scoped to just the traccar container (via the autoheal=true label) that restarts it if its own TCP healthcheck on 8082 fails. Reruns reuse the existing DB_PASS from .env instead of generating a new one, since the postgres volume keeps the original password from its first init. --- services/traccar.sh | 75 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/services/traccar.sh b/services/traccar.sh index e33a731..5db6323 100644 --- a/services/traccar.sh +++ b/services/traccar.sh @@ -193,6 +193,8 @@ install_traccar() { if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Traccar would:" echo " - Create $TRACCAR_DIR with docker-compose.yml + config/traccar.xml" + echo " - Deploy a PostgreSQL database container (Traccar no longer ships H2)" + echo " - Deploy an autoheal container that restarts Traccar if its healthcheck fails" echo " - Expose port 8082 (web) and 5000-5150 (device protocols)" echo " - Default login: admin@admin.com / admin (change immediately!)" echo " - Offer a Caddy reverse proxy and to start the container" @@ -203,6 +205,17 @@ install_traccar() { ensure_docker_dir_ownership "$TRACCAR_DIR" cd "$TRACCAR_DIR" || return 1 + # Reuse an existing DB password across reruns instead of generating a new + # one — the Postgres volume keeps the original password from its first + # init, so overwriting .env with a fresh one would lock Traccar out. + local DB_PASS="" + if [ -f ".env" ]; then + DB_PASS=$(grep '^POSTGRES_PASSWORD=' .env | cut -d= -f2-) + fi + [ -n "$DB_PASS" ] || DB_PASS=$(generate_password 32) + + local TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" + local _CADDY_NET_BLOCK="" if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_BLOCK=" networks: @@ -224,11 +237,36 @@ networks: name: traccar services: + db: + image: postgres:15-alpine + container_name: traccar-db + hostname: traccar-db + restart: unless-stopped + env_file: .env + volumes: + - ./db:/var/lib/postgresql/data +${_CADDY_NET_BLOCK} healthcheck: + test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER} -d \${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + traccar: image: traccar/traccar:latest container_name: traccar hostname: traccar restart: unless-stopped + depends_on: + db: + condition: service_healthy + labels: + - "autoheal=true" + healthcheck: + test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/127.0.0.1/8082' || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 60s volumes: - ./logs:/opt/traccar/logs:rw - ./data:/opt/traccar/data:rw @@ -237,22 +275,43 @@ services: - "8082:8082" - "5000-5150:5000-5150" - "5000-5150:5000-5150/udp" -${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} +${_CADDY_NET_BLOCK} + autoheal: + image: willfarrell/autoheal:latest + container_name: traccar-autoheal + restart: unless-stopped + environment: + - AUTOHEAL_CONTAINER_LABEL=autoheal + volumes: + - /var/run/docker.sock:/var/run/docker.sock +${_CADDY_NET_SECTION} TRACCAR_COMPOSE - mkdir -p logs data config + cat > .env << TRACCAR_ENV +TZ=$TZ_VAL +CADDY_NET=$SITE_CADDY_NET - cat > config/traccar.xml << 'TRACCAR_XML' +# PostgreSQL — backs Traccar's database (Traccar's docker image no longer +# bundles the H2 driver, so a real database is required). +POSTGRES_DB=traccar +POSTGRES_USER=traccar +POSTGRES_PASSWORD=$DB_PASS +TRACCAR_ENV + chmod 600 .env + + mkdir -p logs data config db + + cat > config/traccar.xml << TRACCAR_XML ./conf/default.xml - org.h2.Driver - jdbc:h2:/opt/traccar/data/database - sa - + org.postgresql.Driver + jdbc:postgresql://traccar-db:5432/traccar?sslmode=disable + traccar + $DB_PASS TRACCAR_XML @@ -272,6 +331,8 @@ Android/iOS app, OwnTracks, or any of 200+ supported device protocols. - Device protocols: ports 5000-5150 (TCP + UDP) - Config: \`config/traccar.xml\` - App data: \`data/\` and \`logs/\` +- Database: PostgreSQL (\`traccar-db\` container, data in \`db/\`, credentials in \`.env\`) +- Autoheal: \`traccar-autoheal\` restarts the \`traccar\` container if its healthcheck fails ## Manage \`\`\`bash From 5a329cdd48bd1d1d57119b12dbafe16244ac3d13 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 18:43:02 +0000 Subject: [PATCH 2/5] traccar: resolve Caddy local/remote mode like configure_caddy_for_service does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The caddy_net wiring for the new db/traccar containers was gated only on whether ~/docker/caddy exists locally, so it didn't account for a site where Caddy runs on a different box (CADDY_MODE=remote or the legacy CADDY_REMOTE_HOST var, set with no local Caddy directory). Resolve the mode the same way configure_caddy_for_service does — explicit CADDY_MODE first, then the local directory, then CADDY_REMOTE_HOST — so caddy_net is only joined when Caddy is actually local. A remote Caddy reaches Traccar via this host's published 8082 port regardless, so no other change is needed for that path. --- services/traccar.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/services/traccar.sh b/services/traccar.sh index 5db6323..e74cf57 100644 --- a/services/traccar.sh +++ b/services/traccar.sh @@ -216,15 +216,22 @@ install_traccar() { local TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh) + # so this matches whatever Caddy setup the site actually has: an explicit + # CADDY_MODE from the site config wins, then a local ~/docker/caddy, then + # the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a remote + # Caddy box can't resolve container names on this host's bridge network + # anyway, it reaches Traccar via this host's published 8082 port instead. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: From 6554256e8ee125e019fd0742ea492cf7d1121350 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 18:46:23 +0000 Subject: [PATCH 3/5] traccar: drive database config from .env instead of a static XML file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix still baked database.user/database.password directly into config/traccar.xml, duplicating the secret that .env already held and leaving a second, unmanaged copy of it on disk. Traccar supports reading its config from environment variables (CONFIG_USE_ENVIRONMENT_VARIABLES=true, confirmed against the official traccar/traccar docker/compose/traccar-mysql.yaml reference). Use that: DATABASE_DRIVER/URL/USER/PASSWORD are now set in the compose file via ${POSTGRES_*} interpolation from .env, so .env is the only place the credentials live — drop config/traccar.xml and its volume mount entirely, matching the official reference example. Also switched to the official reference healthcheck (wget against /api/health, 1h start_period) — a real endpoint on real hardware rather than a guessed /dev/tcp probe against an unverified image's toolset — and added the interval/start-period env vars to the autoheal container to match, while keeping the container scoped to just Traccar via the autoheal=true label instead of the reference's host-wide "all". Verified with `docker compose config` (both with and without a local Caddy directory present) that the ${POSTGRES_DB}/${POSTGRES_USER}/ ${POSTGRES_PASSWORD} references resolve correctly from .env with no warnings. --- services/traccar.sh | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/services/traccar.sh b/services/traccar.sh index e74cf57..e2e8ff9 100644 --- a/services/traccar.sh +++ b/services/traccar.sh @@ -192,8 +192,9 @@ install_traccar() { if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Traccar would:" - echo " - Create $TRACCAR_DIR with docker-compose.yml + config/traccar.xml" + echo " - Create $TRACCAR_DIR with docker-compose.yml + .env" echo " - Deploy a PostgreSQL database container (Traccar no longer ships H2)" + echo " - Point Traccar at it via env vars (CONFIG_USE_ENVIRONMENT_VARIABLES) — no secrets in a config file" echo " - Deploy an autoheal container that restarts Traccar if its healthcheck fails" echo " - Expose port 8082 (web) and 5000-5150 (device protocols)" echo " - Default login: admin@admin.com / admin (change immediately!)" @@ -263,21 +264,27 @@ ${_CADDY_NET_BLOCK} healthcheck: container_name: traccar hostname: traccar restart: unless-stopped + env_file: .env depends_on: db: condition: service_healthy labels: - "autoheal=true" + environment: + CONFIG_USE_ENVIRONMENT_VARIABLES: "true" + DATABASE_DRIVER: org.postgresql.Driver + DATABASE_URL: jdbc:postgresql://traccar-db:5432/\${POSTGRES_DB}?sslmode=disable + DATABASE_USER: \${POSTGRES_USER} + DATABASE_PASSWORD: \${POSTGRES_PASSWORD} healthcheck: - test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/127.0.0.1/8082' || exit 1"] - interval: 30s + test: ["CMD", "wget", "-q", "--spider", "http://localhost:8082/api/health"] + interval: 2m timeout: 5s + start_period: 1h retries: 3 - start_period: 60s volumes: - ./logs:/opt/traccar/logs:rw - ./data:/opt/traccar/data:rw - - ./config/traccar.xml:/opt/traccar/conf/traccar.xml:ro ports: - "8082:8082" - "5000-5150:5000-5150" @@ -288,7 +295,9 @@ ${_CADDY_NET_BLOCK} container_name: traccar-autoheal restart: unless-stopped environment: - - AUTOHEAL_CONTAINER_LABEL=autoheal + AUTOHEAL_CONTAINER_LABEL: autoheal + AUTOHEAL_INTERVAL: 60 + AUTOHEAL_START_PERIOD: 3600 volumes: - /var/run/docker.sock:/var/run/docker.sock ${_CADDY_NET_SECTION} @@ -299,28 +308,16 @@ TZ=$TZ_VAL CADDY_NET=$SITE_CADDY_NET # PostgreSQL — backs Traccar's database (Traccar's docker image no longer -# bundles the H2 driver, so a real database is required). +# bundles the H2 driver, so a real database is required). Traccar reads +# these directly (CONFIG_USE_ENVIRONMENT_VARIABLES in docker-compose.yml) +# instead of a config file, so this is the only place the credentials live. POSTGRES_DB=traccar POSTGRES_USER=traccar POSTGRES_PASSWORD=$DB_PASS TRACCAR_ENV chmod 600 .env - mkdir -p logs data config db - - cat > config/traccar.xml << TRACCAR_XML - - - - - - ./conf/default.xml - org.postgresql.Driver - jdbc:postgresql://traccar-db:5432/traccar?sslmode=disable - traccar - $DB_PASS - -TRACCAR_XML + mkdir -p logs data db chown -R "$ACTUAL_USER:$ACTUAL_USER" "$TRACCAR_DIR" log_success "Traccar configured at $TRACCAR_DIR" @@ -336,9 +333,12 @@ Android/iOS app, OwnTracks, or any of 200+ supported device protocols. - Web UI: http://localhost:8082 - Default login: admin@admin.com / admin (change immediately!) - Device protocols: ports 5000-5150 (TCP + UDP) -- Config: \`config/traccar.xml\` - App data: \`data/\` and \`logs/\` -- Database: PostgreSQL (\`traccar-db\` container, data in \`db/\`, credentials in \`.env\`) +- Database: PostgreSQL (\`traccar-db\` container, data in \`db/\`) +- All database settings (name, user, password) live in \`.env\` — Traccar + reads them directly via env vars, nothing is duplicated in a config file. + Change the password there (then recreate both containers) if you need to + rotate it. - Autoheal: \`traccar-autoheal\` restarts the \`traccar\` container if its healthcheck fails ## Manage From 1fc0a6edfe7d12a164b867e68979e9dd1fccbc2d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 18:54:02 +0000 Subject: [PATCH 4/5] Apply the local/remote Caddy mode resolution to every service, not just traccar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit traccar.sh's caddy_net wiring was fixed to mirror configure_caddy_for_service's own mode resolution (CADDY_MODE from site config, then a local ~/docker/caddy, then the legacy CADDY_REMOTE_HOST var) instead of only checking for the local directory. That same bare directory check was copy-pasted into the caddy_net wiring of every other Docker service in the repo, so a site with Caddy on a different box would silently fail to join any of their containers to caddy_net during setup (or, for homeassistant/koha, only get half the wiring right). Applied the same fix mechanically across all 37 services using the standard _CADDY_NET_BLOCK/_CADDY_NET_SECTION pattern (verified identical text via scripted diff before touching any of them), plus by hand for: - homeassistant.sh and koha.sh, which use their own differently-shaped variables (HA_CADDY_NET_LINES / _CADDY_NET_ENTRY) for the same decision - paintplus.sh and ai-stack.sh, which do a live `docker network connect` instead of a compose network block - watchyourlan.sh, whose Caddy note was worded for local-only setups sms-inbound.sh got more than a mode swap: its Caddy wiring was hand-rolled (not routed through configure_caddy_for_service) and had no remote-Caddy path at all — a remote Caddy box would get a misleading "Caddy isn't installed here" message instead of a snippet. Added _sms_write_caddy_snippet(), mirroring the snippet-file pattern configure_caddy_for_service uses everywhere else, and pointed the firewall gate at the same three-way mode instead of a two-way dir check. Verified: bash -n across all of services/*.sh, a scripted check that every touched file has exactly one _CADDY_MODE resolution and no leftover bare `[ -d "$DOCKER_DIR/caddy" ]` feeding a caddy_net decision, and spot-checked docker compose config renders (traccar, mattermost) confirming the ${VAR} interpolation and multi-service usage sites still resolve correctly. --- services/actualbudget.sh | 16 +++++++--- services/ai-stack.sh | 10 +++++- services/archivebox.sh | 16 +++++++--- services/arm.sh | 16 +++++++--- services/audiobookshelf.sh | 16 +++++++--- services/calibre-web.sh | 16 +++++++--- services/changedetection.sh | 16 +++++++--- services/drum-rhythm-game.sh | 16 +++++++--- services/emby.sh | 16 +++++++--- services/filebrowser.sh | 16 +++++++--- services/fmd.sh | 16 +++++++--- services/frigate-audio.sh | 16 +++++++--- services/frigate-notify.sh | 16 +++++++--- services/frigate.sh | 16 +++++++--- services/gatus.sh | 16 +++++++--- services/homeassistant.sh | 13 ++++++-- services/homebox.sh | 16 +++++++--- services/immich.sh | 16 +++++++--- services/iopaint.sh | 16 +++++++--- services/jellyfin.sh | 16 +++++++--- services/joplin.sh | 16 +++++++--- services/js99er.sh | 16 +++++++--- services/koha.sh | 11 ++++++- services/magicmirror.sh | 16 +++++++--- services/mail-archiver.sh | 16 +++++++--- services/mattermost.sh | 16 +++++++--- services/mealie.sh | 16 +++++++--- services/meshcentral.sh | 16 +++++++--- services/n8n.sh | 16 +++++++--- services/nextcloud.sh | 16 +++++++--- services/ntfy.sh | 16 +++++++--- services/onlyoffice.sh | 16 +++++++--- services/paintplus.sh | 13 ++++++-- services/portainer.sh | 16 +++++++--- services/sms-inbound.sh | 62 ++++++++++++++++++++++++++++++++++-- services/stirling-pdf.sh | 16 +++++++--- services/syncthing.sh | 16 +++++++--- services/unifi.sh | 16 +++++++--- services/uptimekuma.sh | 16 +++++++--- services/vaultwarden.sh | 16 +++++++--- services/watchtower.sh | 16 +++++++--- services/watchyourlan.sh | 16 +++++++--- services/wg-easy.sh | 16 +++++++--- 43 files changed, 520 insertions(+), 197 deletions(-) diff --git a/services/actualbudget.sh b/services/actualbudget.sh index d4c1dce..606ed25 100644 --- a/services/actualbudget.sh +++ b/services/actualbudget.sh @@ -202,15 +202,21 @@ install_actualbudget() { local TZ_VAL; TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/ai-stack.sh b/services/ai-stack.sh index e858f85..a5ad090 100644 --- a/services/ai-stack.sh +++ b/services/ai-stack.sh @@ -218,7 +218,15 @@ GPUEOF # ── Caddy (Open WebUI has built-in auth — no Authelia) ──────────────────── # The generated compose doesn't join caddy_net, so attach the container by name. - if [ -d "$DOCKER_DIR/caddy" ] && [ "$INSTALLER_RAN" = true ]; then + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't reach this container by name over a bridge network + # it isn't on anyway. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + if [ "$_CADDY_MODE" = "local" ] && [ "$INSTALLER_RAN" = true ]; then docker network connect "$SITE_CADDY_NET" open-webui 2>/dev/null || true fi configure_caddy_for_service "Open WebUI" "open-webui:8080" "ai" diff --git a/services/archivebox.sh b/services/archivebox.sh index 4973911..a7d3bce 100644 --- a/services/archivebox.sh +++ b/services/archivebox.sh @@ -195,15 +195,21 @@ install_archivebox() { ensure_docker_dir_ownership "$AB_DIR" cd "$AB_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/arm.sh b/services/arm.sh index ad77f5b..02e2ab1 100644 --- a/services/arm.sh +++ b/services/arm.sh @@ -233,15 +233,21 @@ install_arm() { TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" UID_VAL=$(id -u "$ACTUAL_USER"); GID_VAL=$(id -g "$ACTUAL_USER") + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/audiobookshelf.sh b/services/audiobookshelf.sh index b8082b6..4aa96b8 100644 --- a/services/audiobookshelf.sh +++ b/services/audiobookshelf.sh @@ -208,15 +208,21 @@ install_audiobookshelf() { local TZ_VAL; TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/calibre-web.sh b/services/calibre-web.sh index 8ac3643..1f34607 100644 --- a/services/calibre-web.sh +++ b/services/calibre-web.sh @@ -197,15 +197,21 @@ install_calibre-web() { ensure_docker_dir_ownership "$CW_DIR" cd "$CW_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/changedetection.sh b/services/changedetection.sh index 7364eed..4a7776e 100644 --- a/services/changedetection.sh +++ b/services/changedetection.sh @@ -197,15 +197,21 @@ install_changedetection() { ensure_docker_dir_ownership "$CD_DIR" cd "$CD_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/drum-rhythm-game.sh b/services/drum-rhythm-game.sh index 488ae49..c88b55a 100644 --- a/services/drum-rhythm-game.sh +++ b/services/drum-rhythm-game.sh @@ -214,15 +214,21 @@ install_drum-rhythm-game() { chown -R "$ACTUAL_USER:$ACTUAL_USER" "$DRUM_DIR/html" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/emby.sh b/services/emby.sh index 4454630..170df66 100644 --- a/services/emby.sh +++ b/services/emby.sh @@ -222,15 +222,21 @@ install_emby() { TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" UID_VAL=$(id -u "$ACTUAL_USER"); GID_VAL=$(id -g "$ACTUAL_USER") + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/filebrowser.sh b/services/filebrowser.sh index 3a361d9..747a54a 100644 --- a/services/filebrowser.sh +++ b/services/filebrowser.sh @@ -199,15 +199,21 @@ install_filebrowser() { local FB_PATH="" prompt_text "Primary files directory to browse [default: $ACTUAL_HOME]:" "$ACTUAL_HOME" FB_PATH + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/fmd.sh b/services/fmd.sh index 7a11a4b..bc80529 100644 --- a/services/fmd.sh +++ b/services/fmd.sh @@ -214,15 +214,21 @@ install_fmd() { local FMD_PASS FMD_PASS=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16) + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/frigate-audio.sh b/services/frigate-audio.sh index 44414fe..c4fc1b6 100644 --- a/services/frigate-audio.sh +++ b/services/frigate-audio.sh @@ -375,15 +375,21 @@ ENVEOF [ -n "$DRI_LINE" ] && DEVICES_BLOCK="${DEVICES_BLOCK}\n ${DRI_LINE}" fi + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/frigate-notify.sh b/services/frigate-notify.sh index 8242f2d..9aec65e 100644 --- a/services/frigate-notify.sh +++ b/services/frigate-notify.sh @@ -211,15 +211,21 @@ install_frigate-notify() { ensure_docker_dir_ownership "$FN_DIR" cd "$FN_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/frigate.sh b/services/frigate.sh index ce0bfef..f497374 100644 --- a/services/frigate.sh +++ b/services/frigate.sh @@ -558,15 +558,21 @@ install_frigate() { log_warning "No /dev/dri/renderD128 — Frigate will use CPU detection." fi + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/gatus.sh b/services/gatus.sh index 20aa6f7..3a37854 100644 --- a/services/gatus.sh +++ b/services/gatus.sh @@ -210,15 +210,21 @@ install_gatus() { local TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/homeassistant.sh b/services/homeassistant.sh index 38a8892..9501ac4 100644 --- a/services/homeassistant.sh +++ b/services/homeassistant.sh @@ -199,6 +199,15 @@ install_homeassistant() { ensure_docker_dir_ownership "$HOMEASSISTANT_DIR" cd "$HOMEASSISTANT_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches Home Assistant via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + # Networking mode: bridge (published port) vs host networking. echo "" echo " Home Assistant networking mode:" @@ -218,7 +227,7 @@ install_homeassistant() { else HA_NET_LINES=" ports: - \"8123:8123\"" - if [ -d "$DOCKER_DIR/caddy" ]; then + if [ "$_CADDY_MODE" = "local" ]; then HA_CADDY_NET_LINES=" networks: - caddy_net" else @@ -228,7 +237,7 @@ install_homeassistant() { fi local _CADDY_NET_SECTION="" - if [ "$HA_NETMODE" != "2" ] && [ -d "$DOCKER_DIR/caddy" ]; then + if [ "$HA_NETMODE" != "2" ] && [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/homebox.sh b/services/homebox.sh index 4415e28..9795a43 100644 --- a/services/homebox.sh +++ b/services/homebox.sh @@ -197,15 +197,21 @@ install_homebox() { ensure_docker_dir_ownership "$HB_DIR" cd "$HB_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/immich.sh b/services/immich.sh index 833d232..46cbd06 100644 --- a/services/immich.sh +++ b/services/immich.sh @@ -291,15 +291,21 @@ install_immich() { TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" # ── Write docker-compose.yml ──────────────────────────────────────────── + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/iopaint.sh b/services/iopaint.sh index d40798b..1c3691c 100644 --- a/services/iopaint.sh +++ b/services/iopaint.sh @@ -268,15 +268,21 @@ install_iopaint() { # Volume ./models:/root/.cache persists ALL model caches: # /root/.cache/torch/hub/checkpoints/ (LaMa, CV2, ZITS, etc.) # /root/.cache/huggingface/ (SD, PowerPaint, LDM, etc.) + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/jellyfin.sh b/services/jellyfin.sh index 5957abc..ad7086d 100644 --- a/services/jellyfin.sh +++ b/services/jellyfin.sh @@ -225,15 +225,21 @@ install_jellyfin() { log_warning "No /dev/dri/renderD128 — Jellyfin will use CPU transcoding." fi + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/joplin.sh b/services/joplin.sh index 94cc9e4..bae8650 100644 --- a/services/joplin.sh +++ b/services/joplin.sh @@ -201,15 +201,21 @@ install_joplin() { DB_PASS="$(generate_password 32)" local BASE_URL="https://joplin.${SITE_DOMAIN}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/js99er.sh b/services/js99er.sh index 42e2d43..ccfbad9 100644 --- a/services/js99er.sh +++ b/services/js99er.sh @@ -436,15 +436,21 @@ NGINXCONF # ── 4. Standalone docker-compose.yml (per-service folder) ──────────────── # Only join caddy_net if Caddy is installed — otherwise the network doesn't # exist and docker compose up will fail with "network not found". + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/koha.sh b/services/koha.sh index ba48753..6b0b692 100644 --- a/services/koha.sh +++ b/services/koha.sh @@ -346,9 +346,18 @@ install_koha() { ensure_docker_dir_ownership "$KOHA_DIR" cd "$KOHA_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches Koha via the host's published ports. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_ENTRY="" local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_ENTRY=" - caddy_net " _CADDY_NET_SECTION=" caddy_net: diff --git a/services/magicmirror.sh b/services/magicmirror.sh index 1b49d1a..03150c4 100644 --- a/services/magicmirror.sh +++ b/services/magicmirror.sh @@ -231,15 +231,21 @@ install_magicmirror() { ensure_docker_dir_ownership "$MM_DIR" cd "$MM_DIR" || continue + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/mail-archiver.sh b/services/mail-archiver.sh index fdcab66..995c2f9 100644 --- a/services/mail-archiver.sh +++ b/services/mail-archiver.sh @@ -212,15 +212,21 @@ install_mail-archiver() { ADMIN_PASS=$(generate_password 24) TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/mattermost.sh b/services/mattermost.sh index 00b9b37..3a2f098 100644 --- a/services/mattermost.sh +++ b/services/mattermost.sh @@ -247,15 +247,21 @@ install_mattermost() { prompt_text "Mattermost site URL [$SITE_URL]:" "$SITE_URL" CONFIGURED_SITEURL [[ -n "$CONFIGURED_SITEURL" ]] && SITE_URL="$CONFIGURED_SITEURL" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/mealie.sh b/services/mealie.sh index 8e3db6c..0b0c762 100644 --- a/services/mealie.sh +++ b/services/mealie.sh @@ -212,15 +212,21 @@ install_mealie() { MEALIE_BASE_URL="https://recipes.${SITE_DOMAIN}" fi + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/meshcentral.sh b/services/meshcentral.sh index 2eaa7b4..0eb8a1c 100644 --- a/services/meshcentral.sh +++ b/services/meshcentral.sh @@ -215,15 +215,21 @@ install_meshcentral() { ensure_docker_dir_ownership "$MC_DIR" cd "$MC_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/n8n.sh b/services/n8n.sh index 65a39d2..896c4e6 100644 --- a/services/n8n.sh +++ b/services/n8n.sh @@ -197,15 +197,21 @@ install_n8n() { ensure_docker_dir_ownership "$N8N_DIR" cd "$N8N_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/nextcloud.sh b/services/nextcloud.sh index 9c58a19..fefbdcd 100644 --- a/services/nextcloud.sh +++ b/services/nextcloud.sh @@ -205,15 +205,21 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* NCDF + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/ntfy.sh b/services/ntfy.sh index f173bb8..5b92e3d 100644 --- a/services/ntfy.sh +++ b/services/ntfy.sh @@ -203,15 +203,21 @@ install_ntfy() { ensure_docker_dir_ownership "$NTFY_DIR" cd "$NTFY_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/onlyoffice.sh b/services/onlyoffice.sh index d6b7e2c..a50a44e 100644 --- a/services/onlyoffice.sh +++ b/services/onlyoffice.sh @@ -243,15 +243,21 @@ install_onlyoffice() { fi [[ -z "$JWT_SECRET" ]] && JWT_SECRET="$(generate_password 32)" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/paintplus.sh b/services/paintplus.sh index 9ec0e43..5b7d171 100644 --- a/services/paintplus.sh +++ b/services/paintplus.sh @@ -127,12 +127,21 @@ install_paintplus() { mkdir -p data ensure_docker_dir_ownership "$PP_DIR" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't reach this container by name over a bridge network + # it isn't on anyway. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + # ── Caddy network override (cloud mode) ─────────────────────────────────── # The base compose has no networks, so Caddy (on caddy_net) can't reach the # container by name. This override — auto-merged with the default compose — # attaches the app to caddy_net. The GPU compose runs with an explicit -f and # does NOT merge overrides, so GPU mode is wired with `docker network connect`. - if [[ "$PP_MODE" != "2" ]] && [ -d "$DOCKER_DIR/caddy" ]; then + if [[ "$PP_MODE" != "2" ]] && [ "$_CADDY_MODE" = "local" ]; then cat > docker-compose.override.yml << OVR # Added by ubuntu-post-install so Caddy (on caddy_net) can reach this app by name. services: @@ -172,7 +181,7 @@ OVR # Ensure the running container is on caddy_net (covers GPU mode, where the # override file above is not merged by the app's bring-up script). - if [ -d "$DOCKER_DIR/caddy" ] && [[ "$START_PP" =~ ^[Yy]$ ]]; then + if [ "$_CADDY_MODE" = "local" ] && [[ "$START_PP" =~ ^[Yy]$ ]]; then docker network connect "$SITE_CADDY_NET" paintplus 2>/dev/null || true fi diff --git a/services/portainer.sh b/services/portainer.sh index 483122d..2ebd4dc 100644 --- a/services/portainer.sh +++ b/services/portainer.sh @@ -194,15 +194,21 @@ install_portainer() { ensure_docker_dir_ownership "$PORTAINER_DIR" cd "$PORTAINER_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/sms-inbound.sh b/services/sms-inbound.sh index 716d05a..f8447b4 100644 --- a/services/sms-inbound.sh +++ b/services/sms-inbound.sh @@ -581,6 +581,52 @@ CBLOCK fi } +# Remote-Caddy counterpart to _sms_configure_caddy — same site block, but a +# remote Caddy box can't resolve host.docker.internal (that hostname only +# works via the extra_hosts entry a LOCAL Caddy container gets) and isn't on +# this host's Docker bridge at all, so it has to reach the relay over this +# host's own IP and published port instead. Mirrors the snippet-file pattern +# configure_caddy_for_service uses for every other service (lib/common.sh). +_sms_write_caddy_snippet() { + local _domain="$1" _port="$2" + + local _this_ip="${CADDY_REMOTE_HOST:-}" + [ -z "$_this_ip" ] && _this_ip="$(hostname -I 2>/dev/null | awk '{print $1}')" + [ -z "$_this_ip" ] && _this_ip="$(hostname -f 2>/dev/null || echo "127.0.0.1")" + + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/sms-inbound.caddy" + mkdir -p "$_snippet_dir" + + cat > "$_snippet_file" << CBLOCK + +# Inbound SMS webhook (sms-inbound) — deliberately NOT behind Authelia: +# the SMS provider calls this unauthenticated. The secret is the token in +# the request path, checked by the relay itself. +${_domain} { + reverse_proxy ${_this_ip}:${_port} + + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + X-Content-Type-Options "nosniff" + Referrer-Policy "no-referrer" + } + + log { + output file /var/log/caddy/${_domain}.log + format json + } +} +CBLOCK + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + + log_success "Snippet saved: $_snippet_file" + log_info "Copy to your Caddy machine and append to its Caddyfile:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " cat ~/caddy-snippets/sms-inbound.caddy >> /path/to/Caddyfile" + log_info " docker restart caddy # reload API is disabled by default" +} + _sms_write_readme() { local _url="$1" _relay_domain="$2" write_readme "$SMS_APP_DIR" << MD @@ -817,24 +863,36 @@ install_sms-inbound() { [[ -n "${SITE_DOMAIN:-}" && "$SITE_DOMAIN" != "example.com" ]] && _default_domain="sms.${SITE_DOMAIN}" prompt_text "Public domain for the webhook (A record must point here) [${_default_domain:-required}]:" "$_default_domain" RELAY_DOMAIN + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + if [[ -z "$RELAY_DOMAIN" ]]; then log_warning "No domain entered — the relay is running but nothing can reach it yet." log_warning "Re-run this service once DNS is ready, or front it with Caddy by hand." - elif [[ -d "$DOCKER_DIR/caddy" ]]; then + elif [ "$_CADDY_MODE" = "local" ]; then _sms_configure_caddy "$RELAY_DOMAIN" "$RELAY_PORT" + elif [ "$_CADDY_MODE" = "remote" ]; then + _sms_write_caddy_snippet "$RELAY_DOMAIN" "$RELAY_PORT" else log_warning "Caddy isn't installed here — proxy https://${RELAY_DOMAIN} to" log_warning "127.0.0.1:${RELAY_PORT} yourself, with a real certificate." fi if command -v ufw &>/dev/null; then - if [[ -d "$DOCKER_DIR/caddy" ]]; then + if [ "$_CADDY_MODE" = "local" ]; then # Caddy reaches this over the caddy_net bridge, so the port has # no business being open to the internet — but a bare `ufw # delete allow` would block Caddy too (see CLAUDE.md). ufw delete allow "${RELAY_PORT}/tcp" 2>/dev/null || true ufw_allow_from_caddy_net "${RELAY_PORT}" else + # No local Caddy to hide behind — a remote Caddy box needs to + # reach this port over the network, and with no Caddy at all the + # provider needs to reach it directly. Either way it stays open. ufw allow "${RELAY_PORT}/tcp" fi ensure_ufw_enabled diff --git a/services/stirling-pdf.sh b/services/stirling-pdf.sh index 2d1e9ef..94a9d6a 100644 --- a/services/stirling-pdf.sh +++ b/services/stirling-pdf.sh @@ -197,15 +197,21 @@ install_stirling-pdf() { ensure_docker_dir_ownership "$PDF_DIR" cd "$PDF_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/syncthing.sh b/services/syncthing.sh index cb6d657..ff18faa 100644 --- a/services/syncthing.sh +++ b/services/syncthing.sh @@ -172,15 +172,21 @@ install_syncthing() { PUID="$(id -u "$ACTUAL_USER")" PGID="$(id -g "$ACTUAL_USER")" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/unifi.sh b/services/unifi.sh index 1302a6c..c18626f 100644 --- a/services/unifi.sh +++ b/services/unifi.sh @@ -118,15 +118,21 @@ install_unifi() { UID_VAL=$(id -u "$ACTUAL_USER") GID_VAL=$(id -g "$ACTUAL_USER") + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/uptimekuma.sh b/services/uptimekuma.sh index bf4c739..bcd1d46 100644 --- a/services/uptimekuma.sh +++ b/services/uptimekuma.sh @@ -196,15 +196,21 @@ install_uptimekuma() { ensure_docker_dir_ownership "$UPTIME_DIR" cd "$UPTIME_DIR" || return 1 + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/vaultwarden.sh b/services/vaultwarden.sh index fbe5ea3..82867df 100644 --- a/services/vaultwarden.sh +++ b/services/vaultwarden.sh @@ -242,15 +242,21 @@ install_vaultwarden() { prompt_text "SMTP password:" "" SMTP_PASS fi + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/watchtower.sh b/services/watchtower.sh index 4eb51a1..bd7be55 100644 --- a/services/watchtower.sh +++ b/services/watchtower.sh @@ -128,15 +128,21 @@ install_watchtower() { NTFY_URL="http://ntfy/watchtower" fi + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: diff --git a/services/watchyourlan.sh b/services/watchyourlan.sh index 25b2d1c..94b767e 100644 --- a/services/watchyourlan.sh +++ b/services/watchyourlan.sh @@ -161,9 +161,13 @@ WYL_ENV log_success "WatchYourLAN configured at $WYL_DIR" # WatchYourLAN uses network_mode: host, so Caddy container-name routing - # can't reach it via caddy_net. Access is directly on host port $GUI_PORT. - # If behind Caddy on the same host, configure manually with host IP:PORT. - if [ -d "$DOCKER_DIR/caddy" ]; then + # can't reach it via caddy_net regardless of where Caddy runs. Access is + # directly on host port $GUI_PORT. Mirrors configure_caddy_for_service's + # own mode resolution (lib/common.sh) purely to word this note correctly. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + if [ "$_CADDY_MODE" != "none" ]; then echo "" log_info "Note: WatchYourLAN uses host networking (needed for ARP scanning)." log_info "It cannot join caddy_net. To put it behind Caddy, add this block manually:" @@ -172,7 +176,11 @@ WYL_ENV echo " reverse_proxy :$GUI_PORT" echo " }" echo "" - echo " where HOST_IP is this server's IP on the Docker bridge (usually 172.17.0.1)." + if [ "$_CADDY_MODE" = "remote" ]; then + echo " where HOST_IP is this server's real network IP (Caddy is on a different box)." + else + echo " where HOST_IP is this server's IP on the Docker bridge (usually 172.17.0.1)." + fi fi write_readme "$WYL_DIR" << MD diff --git a/services/wg-easy.sh b/services/wg-easy.sh index e309870..3047f18 100644 --- a/services/wg-easy.sh +++ b/services/wg-easy.sh @@ -234,15 +234,21 @@ install_wg-easy() { # Escape $ in hash for docker-compose env (bcrypt hashes contain $$) local WG_HASH_ESCAPED="${WG_PASSWORD_HASH//\$/\$\$}" + # Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh): + # explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy, + # then the legacy CADDY_REMOTE_HOST var. Only "local" joins caddy_net — a + # remote Caddy box can't resolve container names on this host's bridge + # network anyway; it reaches this service via the host's published port. + local _CADDY_MODE="${CADDY_MODE:-none}" + [ "$_CADDY_MODE" = "none" ] && [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ "$_CADDY_MODE" = "none" ] && [ -n "${CADDY_REMOTE_HOST:-}" ] && _CADDY_MODE="remote" + local _CADDY_NET_BLOCK="" - if [ -d "$DOCKER_DIR/caddy" ]; then + local _CADDY_NET_SECTION="" + if [ "$_CADDY_MODE" = "local" ]; then _CADDY_NET_BLOCK=" networks: - caddy_net " - fi - - local _CADDY_NET_SECTION="" - if [ -d "$DOCKER_DIR/caddy" ]; then _CADDY_NET_SECTION=" networks: caddy_net: From 737bc873d168553304703529e64c89acad15d4e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 18:56:02 +0000 Subject: [PATCH 5/5] traccar: fix stale admin@admin.com/admin default-login messaging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current Traccar images ship with no built-in account at all — the login screen's Register flow creates the first user, and that user is automatically made admin. The admin@admin.com/admin default our messages still quoted belongs to older Traccar versions and no longer exists, so anyone following our own output would try that login and fail. Updated the dry-run summary, README, and final on-screen message to describe the real flow, and to flag that self-registration stays open to anyone who reaches the server until it's turned off (Settings → Server → Permissions), since that's a real exposure window on a freshly-installed instance with no way to lock it down at config time. --- services/traccar.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/traccar.sh b/services/traccar.sh index e2e8ff9..4e3ddb7 100644 --- a/services/traccar.sh +++ b/services/traccar.sh @@ -197,7 +197,7 @@ install_traccar() { echo " - Point Traccar at it via env vars (CONFIG_USE_ENVIRONMENT_VARIABLES) — no secrets in a config file" echo " - Deploy an autoheal container that restarts Traccar if its healthcheck fails" echo " - Expose port 8082 (web) and 5000-5150 (device protocols)" - echo " - Default login: admin@admin.com / admin (change immediately!)" + echo " - No default login — register the first account at the web UI, it becomes admin" echo " - Offer a Caddy reverse proxy and to start the container" return 0 fi @@ -331,7 +331,11 @@ GPS tracking server. Track phones, vehicles, and assets via the Traccar Android/iOS app, OwnTracks, or any of 200+ supported device protocols. - Web UI: http://localhost:8082 -- Default login: admin@admin.com / admin (change immediately!) +- No default login — Traccar ships with no built-in account. Open the web UI + and register the first user; it's automatically made admin. Self-registration + stays open to anyone who reaches this server until you turn it off, so do + this right away, then go to Settings → Server → Permissions and uncheck + Registration. - Device protocols: ports 5000-5150 (TCP + UDP) - App data: \`data/\` and \`logs/\` - Database: PostgreSQL (\`traccar-db\` container, data in \`db/\`) @@ -363,7 +367,8 @@ MD echo "" echo " Access at: http://localhost:8082" - echo " Default: admin@admin.com / admin (change immediately!)" + echo " No default login — register the first account now; it becomes admin." + echo " Then disable further registration: Settings → Server → Permissions." echo "" }