feat(161): phase-2 bridge — coolify network at create time; dead postgres removed #5526
4 changed files+45−20
Modifieddocker-compose.standalone.yml+8−17View fileUnifiedSplit
@@ -17,23 +17,14 @@ x-logging: &default-logging
1717 max-file: "3"
1818
1919services:
20 postgres:
21 # pgvector image is required: migration 0057 does CREATE EXTENSION vector.
22 image: pgvector/pgvector:pg16
23 environment:
24 POSTGRES_USER: gluecron
25 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
26 POSTGRES_DB: gluecron
27 volumes:
28 - pgdata:/var/lib/postgresql/data
29 restart: unless-stopped
30 logging: *default-logging
31 healthcheck:
32 test: ["CMD-SHELL", "pg_isready -U gluecron -d gluecron"]
33 interval: 10s
34 timeout: 5s
35 retries: 6
36
20 # The local `postgres` service was REMOVED 2026-08-24. Production's
21 # database is Neon (DATABASE_URL); the container ran for months as unused
22 # dead weight that CLAUDE.md explicitly warned must never be backed up or
23 # queried as production — RAM spent guarding against exactly one mistake.
24 # auto-update.sh deploys with --remove-orphans, so the stray container is
25 # cleaned up on the first deploy after this lands. The `pgdata` volume
26 # below is KEPT untouched as a historical safety net; delete it manually
27 # only after confirming nothing of value ever landed in it.
3728 gluecron:
3829 build: .
3930 # Pass the ENTIRE .env into the container. Previously only a hardcoded
Modifiedscripts/auto-update.sh+17−2View fileUnifiedSplit
@@ -24,6 +24,18 @@ HEALTH_TRIES="${HEALTH_TRIES:-20}"
2424HEALTH_SLEEP="${HEALTH_SLEEP:-3}"
2525BRANCH="main"
2626COMPOSE="docker compose -f docker-compose.standalone.yml"
27# Co-tenant bridge (dies with Phase 2 of the 161 clean rebuild): when the
28# coolify network exists on this host, layer the override that declares it
29# on the gluecron service, so recreation ATTACHES AT CREATE TIME instead of
30# relying on the post-up reconnect below — which left a 502 window, and
31# made any hand-run `compose up` a trap (INCIDENT 2026-08-24: a manual up
32# recreated the container unattached and 502'd the site). The reconnect
33# stays as belt-and-braces. Hosts without the network (dedicated boxes)
34# skip the override entirely, so the external-network declaration can
35# never fail their deploys.
36if docker network inspect coolify >/dev/null 2>&1; then
37 COMPOSE="$COMPOSE -f scripts/docker-compose.coolify-override.yml"
38fi
2739IMAGE="gluecron-gluecron"
2840FAILED_MARKER="$REPO_DIR/.last-failed-deploy-sha"
2941
@@ -167,7 +179,10 @@ if [ "$migrate_ok" = "0" ]; then
167179 exit 1
168180fi
169181
170$COMPOSE up -d || echo "$(date -Is) compose up returned non-zero (likely the co-tenant caddy port conflict) — continuing to app health gate"
182# --remove-orphans: services deleted from the compose file (the dead
183# gluecron-postgres, removed 2026-08-24) get their containers cleaned up on
184# the next deploy instead of running forever as unmanaged strays.
185$COMPOSE up -d --remove-orphans || echo "$(date -Is) compose up returned non-zero (likely the co-tenant caddy port conflict) — continuing to app health gate"
171186
172187# Co-tenant ingress reattach (Coolify boxes only).
173188# `docker compose up --build` recreates the gluecron container, which DROPS
@@ -265,7 +280,7 @@ fi
265280echo "$(date -Is) rolling back to $prev_sha" >&2
266281git reset --hard "$prev_sha"
267282docker tag "$IMAGE:last-good" "$IMAGE:latest"
268$COMPOSE up -d --no-build || echo "$(date -Is) compose up (rollback) returned non-zero (likely the co-tenant caddy port conflict) — continuing to health gate"
283$COMPOSE up -d --no-build --remove-orphans || echo "$(date -Is) compose up (rollback) returned non-zero (likely the co-tenant caddy port conflict) — continuing to health gate"
269284
270285if docker network inspect coolify >/dev/null 2>&1; then
271286 docker network connect coolify gluecron-gluecron-1 2>/dev/null || true
Addedscripts/docker-compose.coolify-override.yml+19−0View fileUnifiedSplit
@@ -0,0 +1,19 @@
1# Co-tenant override — layered by scripts/auto-update.sh ONLY when the
2# `coolify` docker network exists on the host (the 161 configuration where
3# Coolify's Traefik owns 80/443 and reaches gluecron over this network).
4# Declaring it here makes `compose up` attach the container at CREATE time;
5# without this, every recreation depended on a post-up `docker network
6# connect`, and a hand-run compose up 502'd the site (2026-08-24).
7#
8# Dies with Phase 2 of the 161 clean rebuild (Coolify eviction) — once our
9# caddy owns the ports, auto-update.sh's network-exists gate simply stops
10# including this file.
11services:
12 gluecron:
13 networks:
14 - default
15 - coolify
16
17networks:
18 coolify:
19 external: true
Modifiedsrc/__tests__/auto-update-deploy.test.ts+1−1View fileUnifiedSplit
@@ -396,7 +396,7 @@ describe("failures that must NOT fail the deploy", () => {
396396 // before the health gate that actually decides.
397397 const script = await Bun.file(SCRIPT).text();
398398 expect(script).toMatch(
399 /\$COMPOSE up -d \|\| echo[\s\S]{0,160}continuing to app health gate/
399 /\$COMPOSE up -d --remove-orphans \|\| echo[\s\S]{0,160}continuing to app health gate/
400400 );
401401 // And it still succeeds end to end with docker returning 0 everywhere.
402402 const r = await run({});
403403
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts