Docker Compose
The root docker-compose.yml builds and runs the full stack: the web dashboard, the
server (core engine), and a TimescaleDB database. The app Dockerfiles live at
apps/web/Dockerfile and apps/server/Dockerfile.
Commands
Section titled “Commands”bun run docker:build # build imagesbun run docker:up # build & start (detached)bun run docker:logs # tail logsbun run docker:down # stopServices and ports
Section titled “Services and ports”| Service | Image / build | Host port |
|---|---|---|
web |
apps/web/Dockerfile |
3001 |
server |
apps/server/Dockerfile |
3000 |
migrate |
docker/migrate.Dockerfile (run-once) |
— |
postgres |
timescale/timescaledb:2.28.2-pg17 (pinned) |
5432 |
The dashboard is served on http://localhost:3001 and the API on http://localhost:3000.
Configuration
Section titled “Configuration”- Each app reads its own
.env(apps/web/.env,apps/server/.env), which are optional in Compose (required: false). docker-compose.ymloverrides a few values for container networking:servergetsCORS_ORIGIN=http://localhost:3001and aDATABASE_URLpointing at thepostgresservice.- Set
POSTGRES_PASSWORDin your environment to override the default (password).
PUBLIC_SERVER_URLis read at runtime (container start), not baked into the image. It tells the browser where to reach the API and defaults tohttp://localhost:3000; override it in thewebservice environment when the API lives elsewhere. Left unset, the web client falls back to same-origin resolution (used by the Home Assistant addon’s reverse proxy).
See the Environment Variables reference for every value.
- The server image is distroless — no shell, node, or curl inside. Its healthcheck runs
the server binary itself (
/app/server --healthcheck), which probes/healthzand round-trips the database;webwaits forservice_healthy. - The
postgresservice has apg_isreadyhealthcheck and the server waits for it (service_healthy) before starting. - The Postgres image tag is pinned: the data volume is only compatible with the pg major that created it, and the timescaledb extension binary must be ≥ the version stamped in the database. Tag bumps ship deliberately with releases.
- Database data persists in the
SunReye_postgres_datavolume.
Schema migrations
Section titled “Schema migrations”Automatic. The migrate service runs the journaled migration runner
(packages/db/src/migrate.ts) against the Compose Postgres, then exits; the server waits
for it to complete (service_completed_successfully) before starting. The runner:
- Refuses downgrades — an older release won’t start against a database migrated by a newer one (restore a backup instead).
- Baselines pre-journal databases — deployments created in the old
db:pushera get the baseline migration recorded without re-executing it. - Applies pending drizzle migrations transactionally, then the TimescaleDB pipeline (journaled structural files + re-applied policies).
A failed migration exits non-zero, the server never starts, and the error is the last thing
in docker compose logs migrate. Nothing can prompt interactively — the old db:push hang
is gone.