diff --git a/docker-compose.yml b/docker-compose.yml index b3ebd56f22..bc5f890e6a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -222,6 +222,14 @@ services: profiles: ["ollama"] volumes: - ollama-models:/root/.ollama + # The image ships the ollama CLI but no curl/wget, so probe the daemon through it directly instead of a + # raw HTTP request: `ollama list` only succeeds once the server is accepting API calls. + healthcheck: + test: ["CMD", "ollama", "list"] + interval: 10s + timeout: 5s + start_period: 15s + retries: 5 # ── Litestream (--profile litestream) ───────────────────────────────────── # Continuous WAL backup of the SQLite DB to S3/B2/R2. Copy litestream.yml.example diff --git a/test/unit/selfhost-compose-ollama-health.test.ts b/test/unit/selfhost-compose-ollama-health.test.ts new file mode 100644 index 0000000000..45622522f9 --- /dev/null +++ b/test/unit/selfhost-compose-ollama-health.test.ts @@ -0,0 +1,28 @@ +import { readFileSync } from "node:fs"; +import { parse } from "yaml"; +import { describe, expect, it } from "vitest"; + +function readYaml(path: string): Record { + const value = parse(readFileSync(path, "utf8")); + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${path} must be a YAML object`); + } + return value as Record; +} + +// Pure structural checks only (no `docker` CLI invocation): the self-hosted runner container this actually +// runs on does not have Docker-in-Docker access, so a test that shells out to `docker compose config` +// would be unreliable/environment-dependent here (same constraint as docker-compose-override-example.test.ts). +describe("docker-compose.yml — ollama healthcheck (#2504)", () => { + it("gives ollama a healthcheck so docker compose ps and a future depends_on can gate on it", () => { + const compose = readYaml("docker-compose.yml"); + const services = (compose.services as Record>) ?? {}; + const ollama = services.ollama ?? {}; + const healthcheck = ollama.healthcheck as { test?: unknown[]; retries?: number }; + + // The image ships the ollama CLI but no curl/wget/nc, so `ollama list` is the only in-image probe that + // only succeeds once the daemon is actually serving API calls. + expect(healthcheck.test).toEqual(["CMD", "ollama", "list"]); + expect(healthcheck.retries).toBeGreaterThan(0); + }); +});