From 8bd2a54c1a055c99005bfb9cda6a878b61155d61 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:34:32 -0700 Subject: [PATCH] fix(selfhost): add start_period to postgres and pgbouncer healthchecks Every failed pg_isready probe counted toward retries from the first check with no grace period, unlike every other service with a genuine cold-start delay in this file. A slow pgvector initdb (first pull, slow disk) could plausibly exceed 5 retries at 10s, marking postgres unhealthy and failing every service_healthy depends_on gate downstream. Closes #3898 --- docker-compose.yml | 7 +++++++ test/unit/selfhost-compose-db-health.test.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index b7752eded6..d82753f566 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -229,9 +229,15 @@ services: resources: limits: memory: "${POSTGRES_MEM_LIMIT:-2g}" + # start_period (#3898): pg_isready's failed-probe count applies from the very first check unless a + # start_period grace is set. A genuinely cold initdb (first pull, slow disk, low-memory VPS, creating + # the pgvector extension) can plausibly take longer than the 5x10s=50s this healthcheck otherwise + # allows before Docker marks the service unhealthy and fails every downstream service_healthy gate. healthcheck: test: ["CMD-SHELL", "pg_isready -U gittensory"] interval: 10s + timeout: 5s + start_period: 20s retries: 5 # ── PgBouncer (--profile pgbouncer) ─────────────────────────────────────── @@ -264,6 +270,7 @@ services: test: ["CMD", "pg_isready", "-h", "127.0.0.1", "-p", "5432"] interval: 10s timeout: 5s + start_period: 15s retries: 5 # Postgres internals exporter. Starts with the Postgres profiles so Prometheus can scrape it when diff --git a/test/unit/selfhost-compose-db-health.test.ts b/test/unit/selfhost-compose-db-health.test.ts index 8fccaac4ab..86ef1381dd 100644 --- a/test/unit/selfhost-compose-db-health.test.ts +++ b/test/unit/selfhost-compose-db-health.test.ts @@ -49,4 +49,17 @@ describe("docker-compose.yml — postgres/pgbouncer startup ordering (#2500, #25 expect(dependsOn.postgres).toEqual({ condition: "service_healthy" }); }); + + it("gives postgres and pgbouncer a start_period so a cold pg_isready probe isn't marked unhealthy (#3898)", () => { + const compose = readYaml("docker-compose.yml"); + const services = (compose.services as Record>) ?? {}; + + // Every failed probe counts toward `retries` from the very first check unless start_period is set -- + // a genuinely cold pgvector initdb (first pull, slow disk) can plausibly exceed 5 retries at 10s each + // with no grace period, marking postgres unhealthy and failing every service_healthy depends_on gate. + for (const name of ["postgres", "pgbouncer"]) { + const healthcheck = (services[name] ?? {}).healthcheck as { start_period?: string } | undefined; + expect(healthcheck?.start_period, name).toBeTruthy(); + } + }); });