Skip to Content

Docker

The project includes a production-ready multi-stage Dockerfile and a docker-compose.yml.

Build the Image

docker build -t buildpad-daas:latest .

Run with Docker Compose

Copy .env.local.example to .env and fill in your Supabase credentials, then:

docker-compose up -d

The app is available at http://localhost:3000.

Environment Variables

Pass environment variables via a .env file or directly:

docker run -d \ -p 3000:3000 \ -e NEXT_PUBLIC_SUPABASE_URL=https://xyz.supabase.co \ -e NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \ -e SUPABASE_SERVICE_ROLE_KEY=your-service-role-key \ buildpad-daas:latest

In Docker/Kubernetes environments where Supabase runs as a sidecar or internal service, use SUPABASE_INTERNAL_URL so the server connects directly without going through the public load balancer:

docker run -d \ -p 3000:3000 \ -e NEXT_PUBLIC_SUPABASE_URL=https://xyz.supabase.co \ -e SUPABASE_INTERNAL_URL=http://supabase-kong:8000 \ -e NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \ -e SUPABASE_SERVICE_ROLE_KEY=your-service-role-key \ buildpad-daas:latest

The browser still uses NEXT_PUBLIC_SUPABASE_URL. All server-side code — including cookie-based session requests — uses SUPABASE_INTERNAL_URL. Cookie names are automatically kept in sync with NEXT_PUBLIC_SUPABASE_URL so browser sessions work correctly.

Startup Entrypoint

The docker-entrypoint.sh script runs database migrations automatically on container start before starting the cluster launcher (cluster.js). This ensures the schema is always up to date without manual intervention.

Migrations are idempotent — running them on a database that’s already up to date is safe.

Multi-Process Cluster

The image starts node cluster.js (not node server.js). The launcher forks one worker per available core; workers share port 3000 and the primary distributes connections between them. Set WEB_CONCURRENCY to cap the worker count.

Each worker is a full Next.js server (~400 MB resident under load), so size container memory at roughly workers × 400–500 MB. If the container’s memory limit is below cores × 400 MB, set WEB_CONCURRENCY explicitly — otherwise the default forks more workers than the limit can hold and the container OOM-loops on startup (e.g., a 1 GB limit → WEB_CONCURRENCY=2).

Crashed workers restart automatically, and the cron scheduler runs on a single leader worker with failover if that worker dies.

Known limitation: the in-memory log buffer behind the Logs page is per worker, so /logs shows only one worker’s output.

Health Check

The Docker Compose configuration includes a health check that polls /api/health every 30 seconds.

healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', ...)"] interval: 30s timeout: 3s retries: 3

Building the Docs Site

The documentation site is built into the image automatically (Dockerfile stage docs-builder) and served by the app at /docs. The separate nginx docs service in docker-compose.yml (profile standalone-docs, port 3001) is only needed to serve the docs as a standalone site — see AWS Deployment.

Last updated on