Coolify production deploy, configuration, and healthchecks #9

Open
opened 2026-08-31 05:33:42 +00:00 by PlasticDigits · 1 comment
PlasticDigits commented 2026-08-31 05:33:42 +00:00 (Migrated from gitlab.com)

Summary

Production Coolify deploy + configuration for the research API and weekly worker, including healthchecks (/health, /ready) so Coolify and reverse proxies can restart/unhealthy-route correctly.

Gap: gaps/GAP_1788152435.md §2 (no Dockerfile, compose is DB-only). Pattern reference: voting/deploy/coolify.env.example.

Posts API issue owns route shapes; this issue owns how they run in Coolify.


Current codebase

  • docker-compose.yml: Postgres/pgvector only (host 5433).
  • No Dockerfile, no deploy/, no Coolify service spec, no systemd.
  • CI weekly-dry-run is GitLab schedule on fixtures — not production collect.
  • Binary is a clap CLI (week, ingest, search, fetch, migrate). No long-running HTTP server yet (posts API issue).
  • .env.example lists secrets but nothing loads .env (dotenv missing).

Why this is needed

Operators cannot place this on Coolify next to other CL8Y services. Without healthchecks, a hung worker or dead DB still looks “up.” The posts API must be independently restartable from the cron-like week job.


Constraints / guardrails

  1. Secrets only in Coolify env / Docker secrets. Never bake tokens into images. .env gitignored.
  2. Two processes (or one image, two Coolify services): api (HTTP) and worker (scheduled week / ingest). Shared Postgres.
  3. API binds 0.0.0.0; document API_BIND. Do not expose Postgres publicly.
  4. /health = process alive (no secret leak in body). /ready = DB ping + optional migrations applied. Coolify should use /ready for the API.
  5. Worker schedule: Coolify cron / scheduled task, not “hit a public URL to run week” unless authenticated. Prefer in-cluster cron invoking cl8y-research week ....
  6. Image runs as non-root. Read-only root FS where practical.
  7. CORS/public origin env for https://cl8y.com.
  8. Do not auto-merge or deploy CL8Y-web from this stack.
  9. Feature flags: live sources / live Replicate / live Telegram off until operator enables them in Coolify.
  10. Logs: tracing, no tokens. JSON optional.

Relevant files

Path Role
docker-compose.yml DB only today
.env.example Secret names
.gitlab-ci.yml Schedule dry-run
src/main.rs CLI entry
README.md Local run
voting/deploy/coolify.env.example Sibling pattern (other repo)

Add: Dockerfile, deploy/coolify.env.example, deploy/README.md (Coolify click-path: image, ports, healthcheck URL, cron, Postgres addon), compose override for api+worker+db.


  1. Multi-stage Dockerfile: cargo build --release --features postgres (and server feature).
  2. CMD default API; worker service overrides to cl8y-research week ... or worker subcommand that loops/cron.
  3. Health routes implemented even if posts API is a stub ({"ok":true} / ready checks SELECT 1).
  4. Document Coolify: Git source vs prebuilt image, network to Postgres, env var list matching .env.example + PUBLIC_ORIGIN, API_BIND, TAVILY_API_KEY, telegram private chat id, replicate models.
  5. Local: docker compose --profile prod up (or deploy/docker-compose.coolify.yml) for smoke.

Acceptance criteria

  • GET /health 200 without DB.
  • GET /ready 200 with DB; 503 if DB down (no stack traces / DSNs in body).
  • Dockerfile builds; container listens; healthcheck in compose/Coolify uses /ready.
  • deploy/coolify.env.example lists all production vars with comments; no real secrets.
  • README section: Coolify API service + worker schedule + Postgres (pgvector image).
  • Worker can run one week --dry-run in container against fixtures without live tokens.
  • Non-root user in image.

Test plan — functional paths

  1. Health — unit/http: health 200; ready 503 with bad DATABASE_URL; ready 200 after migrate.
  2. Image — CI optional docker build (or documented local).
  3. Compose smoke — db + api; curl /health /ready /v1/posts (if API issue merged) or 404 until then.
  4. Cron argv — worker --dry-run exits 0 in image.

Test plan — attack, hack, and abuse

  1. Info leak/ready 503 body has no postgres:// password.
  2. Health DDoS — health is cheap (no Replicate/Telegram).
  3. Public week trigger — if any HTTP POST /run-week exists, it must be authn; default is no such route.
  4. Privileged container — Dockerfile does not USER root at runtime.
  5. Env dump — error pages do not print REPLICATE_API_TOKEN.

Verification criteria

  1. Reviewer follows deploy/README.md against a throwaway Coolify (or compose) and sees /ready green.
  2. Killing Postgres flips /ready to 503; /health stays 200.
  3. No secrets in git.
## Summary Production **Coolify** deploy + configuration for the research **API** and **weekly worker**, including **healthchecks** (`/health`, `/ready`) so Coolify and reverse proxies can restart/unhealthy-route correctly. Gap: [`gaps/GAP_1788152435.md`](https://gitlab.com/PlasticDigits/cl8y-research/-/blob/main/gaps/GAP_1788152435.md) §2 (no Dockerfile, compose is DB-only). Pattern reference: `voting/deploy/coolify.env.example`. Posts API issue owns route shapes; this issue owns **how they run in Coolify**. --- ## Current codebase - `docker-compose.yml`: Postgres/pgvector only (host 5433). - No Dockerfile, no `deploy/`, no Coolify service spec, no systemd. - CI `weekly-dry-run` is GitLab **schedule** on fixtures — not production collect. - Binary is a clap CLI (`week`, `ingest`, `search`, `fetch`, `migrate`). No long-running HTTP server yet (posts API issue). - `.env.example` lists secrets but nothing loads `.env` (`dotenv` missing). --- ## Why this is needed Operators cannot place this on Coolify next to other CL8Y services. Without healthchecks, a hung worker or dead DB still looks “up.” The posts API must be independently restartable from the cron-like week job. --- ## Constraints / guardrails 1. Secrets only in Coolify env / Docker secrets. Never bake tokens into images. `.env` gitignored. 2. **Two processes** (or one image, two Coolify services): `api` (HTTP) and `worker` (scheduled `week` / ingest). Shared Postgres. 3. API binds `0.0.0.0`; document `API_BIND`. Do not expose Postgres publicly. 4. `/health` = process alive (no secret leak in body). `/ready` = DB ping + optional migrations applied. Coolify should use `/ready` for the API. 5. Worker schedule: Coolify cron / scheduled task, not “hit a public URL to run week” unless authenticated. Prefer in-cluster cron invoking `cl8y-research week ...`. 6. Image runs as non-root. Read-only root FS where practical. 7. CORS/public origin env for `https://cl8y.com`. 8. Do not auto-merge or deploy CL8Y-web from this stack. 9. Feature flags: live sources / live Replicate / live Telegram **off** until operator enables them in Coolify. 10. Logs: tracing, no tokens. JSON optional. --- ## Relevant files | Path | Role | | --- | --- | | `docker-compose.yml` | DB only today | | `.env.example` | Secret names | | `.gitlab-ci.yml` | Schedule dry-run | | `src/main.rs` | CLI entry | | `README.md` | Local run | | `voting/deploy/coolify.env.example` | Sibling pattern (other repo) | **Add:** `Dockerfile`, `deploy/coolify.env.example`, `deploy/README.md` (Coolify click-path: image, ports, healthcheck URL, cron, Postgres addon), compose override for api+worker+db. --- ## Recommended direction 1. Multi-stage Dockerfile: `cargo build --release --features postgres` (and server feature). 2. `CMD` default API; worker service overrides to `cl8y-research week ...` or `worker` subcommand that loops/cron. 3. Health routes implemented even if posts API is a stub (`{"ok":true}` / ready checks `SELECT 1`). 4. Document Coolify: Git source vs prebuilt image, network to Postgres, env var list matching `.env.example` + `PUBLIC_ORIGIN`, `API_BIND`, `TAVILY_API_KEY`, telegram private chat id, replicate models. 5. Local: `docker compose --profile prod up` (or `deploy/docker-compose.coolify.yml`) for smoke. --- ## Acceptance criteria - [ ] `GET /health` 200 without DB. - [ ] `GET /ready` 200 with DB; 503 if DB down (no stack traces / DSNs in body). - [ ] Dockerfile builds; container listens; healthcheck in compose/Coolify uses `/ready`. - [ ] `deploy/coolify.env.example` lists all production vars with comments; no real secrets. - [ ] README section: Coolify API service + worker schedule + Postgres (pgvector image). - [ ] Worker can run one `week --dry-run` in container against fixtures without live tokens. - [ ] Non-root user in image. --- ## Test plan — functional paths 1. **Health** — unit/http: health 200; ready 503 with bad `DATABASE_URL`; ready 200 after migrate. 2. **Image** — CI optional `docker build` (or documented local). 3. **Compose smoke** — db + api; `curl /health` `/ready` `/v1/posts` (if API issue merged) or 404 until then. 4. **Cron argv** — worker `--dry-run` exits 0 in image. --- ## Test plan — attack, hack, and abuse 1. **Info leak** — `/ready` 503 body has no `postgres://` password. 2. **Health DDoS** — health is cheap (no Replicate/Telegram). 3. **Public week trigger** — if any HTTP `POST /run-week` exists, it must be authn; default is **no such route**. 4. **Privileged container** — Dockerfile does not `USER root` at runtime. 5. **Env dump** — error pages do not print `REPLICATE_API_TOKEN`. --- ## Verification criteria 1. Reviewer follows `deploy/README.md` against a throwaway Coolify (or compose) and sees `/ready` green. 2. Killing Postgres flips `/ready` to 503; `/health` stays 200. 3. No secrets in git.
PlasticDigits commented 2026-08-31 05:34:59 +00:00 (Migrated from gitlab.com)

marked as related to #2

marked as related to #2
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-research#9
No description provided.