Skip to content

Add optional Docker / Docker Compose support#372

Open
opastorello wants to merge 1 commit into
ArnasDon:mainfrom
opastorello:feat/docker-support
Open

Add optional Docker / Docker Compose support#372
opastorello wants to merge 1 commit into
ArnasDon:mainfrom
opastorello:feat/docker-support

Conversation

@opastorello

Copy link
Copy Markdown
Contributor

Summary

Add an optional, self-contained container setup (Dockerfile + Docker Compose) for forkers who self-host on their own infra. Purely additive — next start and the Hostinger path are unchanged.

What changed

  • Dockerfile — multi-stage (deps → build → runtime) using Next.js output: "standalone"; runs as a non-root user; only .next/standalone, .next/static, and public land in the final image.
  • docker-compose.yml — single app service. Supabase stays external (no DB container). NEXT_PUBLIC_* are forwarded as build args; server secrets come from .env.local via env_file at runtime. Includes a healthcheck and restart: unless-stopped.
  • .dockerignore — excludes .git, node_modules, .next, docs, supabase, all .env*, etc.
  • docs/docker.md — usage guide, including the build-time (NEXT_PUBLIC_*, inlined into the client bundle) vs runtime (secrets) distinction and the --env-file .env.local note.
  • next.config.tsoutput: "standalone". No-op for next start / Hostinger; only the Docker build consumes it.
  • README.md — one line pointing to docs/docker.md after the quick start.

Database migrations remain out of scope for the container (applied via the Supabase CLI, matching the existing external-Supabase model).

Test plan

  • npm run typecheck clean.
  • npm run build succeeds (runs inside the Docker build).
  • docker compose --env-file .env.local up --build -d builds and starts; container reports healthy; / → 307 /dashboard, /login → 200.
  • Verified end-to-end on a real deploy (Coolify + external self-hosted Supabase): app boots, auth and DB reachable.

Note: the 5 local test failures in currency/date-utils are timezone/ICU-locale artifacts of a non-UTC dev machine and are unrelated to this change (the only source edit is output: "standalone"); they pass in CI (Linux/UTC).

Related

Closes #371.

Give forkers who self-host on their own infra a first-class container
path instead of hand-rolling a Dockerfile and guessing the Next.js
standalone wiring.

- Multi-stage Dockerfile (deps -> build -> runtime) using Next.js
  output: "standalone"; runs as a non-root user.
- Single-service docker-compose.yml — Supabase stays external, so no
  database container is included.
- .dockerignore and docs/docker.md documenting the setup, including the
  build-time (NEXT_PUBLIC_*, inlined into the client bundle) vs runtime
  (server secrets via env_file) split.
- Set output: "standalone" in next.config.ts. This is a no-op for
  next start and the Hostinger path; only the Docker build consumes it.

Purely additive: no dependency or stack changes, existing deploy paths
are unaffected. Database migrations remain out of scope for the
container (applied via the Supabase CLI, matching the external-Supabase
model).
@opastorello
opastorello requested a review from ArnasDon as a code owner July 12, 2026 03:30
@ArnasDon

Copy link
Copy Markdown
Owner

Great, well-documented addition — the multi-stage standalone Dockerfile is textbook: running as non-root and keeping server secrets out of the image (build args limited to NEXT_PUBLIC_*) is exactly right. I built it locally and confirmed output: "standalone" emits server.js plus the static/public paths your final stage copies, so it runs.

A couple of things before merge:

  1. PORT override breaks the container. docs/docker.md suggests setting PORT in .env.local, but env_file: .env.local injects that into the container, so Next starts listening on that port inside the container — while the "${PORT:-3000}:3000" mapping and the healthcheck still target 3000. A separate var, e.g. "${HOST_PORT:-3000}:3000" (leaving PORT for the container only), fixes it cleanly.

  2. Node version parity. The Dockerfile uses node:22-alpine while CI and engines are on Node 20 — worth matching to node:20-alpine for build/prod parity.

  3. Dead doc link. docs/docker.md links to docs/automations-and-cron.md, which doesn't exist yet (the /api/automations/cron endpoint does).

None of these affect the default happy path. Thanks for making self-hosting first-class!

@ArnasDon ArnasDon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added in the main thread.

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.

Add optional Docker / Docker Compose support

2 participants