Skip to content

feat(self-host): one-command Docker Compose stack (Postgres + API + dashboard) - #831

Merged
unjica merged 3 commits into
developfrom
feat/selfhost-compose
Oct 19, 2026
Merged

unjica merged 3 commits into
developfrom
feat/selfhost-compose

Conversation

@unjica

@unjica unjica commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

Closes #827.

What

A one-command self-host stack in selfhost/:

cd selfhost && cp .env.example .env   # set 3 secrets (openssl rand -hex 32)
docker compose up -d
  • selfhost/docker-compose.yml runs postgres:16-alpine, the api and the dashboard.
    • API image: the existing production apps/api/Dockerfile, unchanged, run with user: node. It already runs prisma migrate deploy before it listens.
    • Dashboard image: the new selfhost/dashboard.Dockerfile.
    • Healthchecks: depends_on waits for each healthcheck, so the start order is Postgres, then the API, then the dashboard.
    • Evaluator: the optional alert-rules-evaluator sits behind --profile jobs. It reuses the API image, so there's no new service type.
  • selfhost/dashboard.Dockerfile is a multi-stage Next.js standalone build that runs as node. It comes to 357 MB, against 1.06 GB for the production image.
  • next.config.ts sets output: "standalone" only when NEXT_OUTPUT=standalone, which only this Dockerfile sets. The Railway dashboard build and next start are unchanged.
  • selfhost/.env.example: no default secrets. Compose uses ${VAR:?} for POSTGRES_PASSWORD, CHECKIN_ENCRYPTION_KEY and DASHBOARD_API_SECRET, so up refuses to start until they're set. The .env file is gitignored and dockerignored.
  • Security: Postgres has no published port. The API and dashboard publish on 127.0.0.1 by default (BIND_ADDRESS to change it). Signup is invite-only after the first account unless you opt in.
  • Scope: no new services (no Redis, no proxy), no new npm dependencies. No GHCR publishing: images build locally, and that's noted in the docs.
  • Docs and CHANGELOG: DEPLOYMENT.md, /docs/self-hosting and the self-hosted overview page get the real steps and measured times. They also fix the outdated "API has no Dockerfile" claim; apps/api/Dockerfile has existed since v1.18.5.

Measured on the box (2026-10-19, 8 vCPU / 15.6 GiB, Docker 26.1.5, Compose 2.26.1)

Cold (no images or cache) Cached (images built)
clone 3 s 3 s
docker compose up -d returns 221 s 9 s
all services healthy after up 232 s 12 s
  • First use: register, org, project and key took 0.14 s via the API. A @telemetry-tracker/node 1.4.1 event was on the dashboard Events page 7.5 s after the SDK started (the SDK has a 5 s batch interval).
  • Idle: 30 × docker stats, 5 min after the last activity. API 77 MiB, dashboard 119 MiB, Postgres 65 MiB: 261 MiB total, CPU mean 0.4% of one core.
  • Healthcheck interval: an earlier sample with 10 s probes came to 391 MiB with about 5.8% CPU from the probes. Probes now run every 2 s during startup (start_interval) and every 60 s once healthy.
  • Checked: both containers run as node, the migrations applied, and the jobs profile and docker compose exec api node dist/jobs/run-retention.js both work.

Checks: docker compose config is valid, eslint on the changed files passes, and dashboard vitest is 355/355.

@unjica
unjica merged commit e3909be into develop Oct 19, 2026
7 checks passed
@unjica
unjica deleted the feat/selfhost-compose branch October 19, 2026 11:07
@unjica unjica mentioned this pull request Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Self-host] One-command Docker Compose stack (Postgres + API + dashboard) with prebuilt images

1 participant