Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit30e70f3

fix(deploy): tolerate co-tenant caddy port conflict + gate on real app health

fix(deploy): tolerate co-tenant caddy port conflict + gate on real app health

The prior reattach fix had a latent hole: auto-update.sh runs under `set -e`,
and `docker compose up` exits non-zero when the co-tenant caddy service fails
to bind :80/:443 (Coolify's proxy owns them). That abort happened BEFORE the
coolify-network reattach, so real timer-driven deploys would still 502.

Now `up` is `|| true` (the caddy conflict is expected and harmless here), and
success is judged by polling the gluecron container's own /healthz — a real
app failure exits non-zero so systemd records a failed deploy instead of a
false green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ccanty labs committed on July 13, 2026Parent: 5bac517
1 file changed+25430e70f364d7bd3bdc49d69213e23fc0eb9d5a441
1 changed file+25−4
Modifiedscripts/auto-update.sh+25−4View fileUnifiedSplit
2121echo "$(date -Is) deploying $local_sha -> $remote_sha"
2222git reset --hard "origin/$BRANCH" # untracked .env / backups are preserved
2323
24$COMPOSE up -d --build
24# NOTE: `|| true` is deliberate. On this Coolify co-tenant box the compose
25# also defines a `caddy` service that tries to bind host :80/:443, which
26# Coolify's proxy already owns — so `up` reports a non-zero exit for caddy
27# even though the app (gluecron) started fine. Under `set -e` that would abort
28# the deploy BEFORE the coolify reattach + health gate below. We tolerate the
29# partial failure here and instead gate on the app's OWN health further down.
30$COMPOSE up -d --build || echo "$(date -Is) compose up returned non-zero (likely the co-tenant caddy port conflict) — continuing to app health gate"
2531sleep 5
2632
2733# Co-tenant ingress reattach (Coolify boxes only).
3137# reaches the app over the external "coolify" network via the file route
3238# /traefik/dynamic/gluecron.yaml in the coolify-proxy container. Without this
3339# reattach, every deploy 502s the site until someone reconnects by hand.
34# Idempotent: a no-op if already attached; harmless no-op on a dedicated VPS
35# that has no "coolify" network (errors are swallowed).
40# Idempotent: a no-op if already attached; skipped entirely on a dedicated VPS
41# that has no "coolify" network.
3642if docker network inspect coolify >/dev/null 2>&1; then
3743 docker network connect coolify gluecron-gluecron-1 2>/dev/null \
3844 && echo "$(date -Is) reattached gluecron to coolify network" \
4248$COMPOSE exec -T gluecron bun run db:migrate || true
4349docker image prune -f >/dev/null 2>&1 || true
4450
45echo "$(date -Is) deploy complete: $remote_sha"
51# App health gate — the real success signal (not caddy). Poll the container's
52# own /healthz; if the app itself never comes up, exit non-zero so the systemd
53# unit records a failed deploy instead of a silent green.
54healthy=0
55for _ in $(seq 1 20); do
56 if docker exec gluecron-gluecron-1 wget -qO- --timeout=4 http://localhost:3000/healthz >/dev/null 2>&1; then
57 healthy=1; break
58 fi
59 sleep 3
60done
61if [ "$healthy" != "1" ]; then
62 echo "$(date -Is) DEPLOY FAILED: app /healthz never came up for $remote_sha" >&2
63 exit 1
64fi
65
66echo "$(date -Is) deploy complete: $remote_sha (app healthy)"
4667