Skip to content

Deployment

Docker Compose (Development)

yaml
# docker-compose.yml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: planner
      POSTGRES_USER: planner
      POSTGRES_PASSWORD: planner
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  backend:
    build:
      context: .
      dockerfile: docker/Dockerfile.backend
    ports:
      - "4000:4000"
    depends_on:
      - db
    environment:
      DATABASE_URL: postgresql://planner:planner@db:5432/planner?schema=public

  frontend:
    build:
      context: .
      dockerfile: docker/Dockerfile.frontend
    ports:
      - "3000:3000"
    depends_on:
      - backend

  website:
    build:
      context: .
      dockerfile: docker/Dockerfile.website
    ports:
      - "8080:8080"

  docs:
    build:
      context: .
      dockerfile: docker/Dockerfile.docs
    ports:
      - "8081:8080"

volumes:
  pgdata:

Production Deployment

bash
docker compose -f docker-compose.prod.yml up -d

The production compose file uses optimized Dockerfiles (Dockerfile.backend.prod) with multi-stage builds for smaller images.

Dockerfiles

All Dockerfiles are based on node:22-alpine with consistent stage naming:

StageDescription
baseBase Node image
depsInstall dependencies
devDevelopment mode
builderBuild the app
runnerProduction runtime

Backend Dockerfile (docker/Dockerfile.backend)

dockerfile
FROM node:22-alpine AS base
# ... standard pnpm setup with corepack
WORKDIR /app

FROM base AS deps
COPY pnpm-lock.yaml package.json pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile

FROM base AS dev
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["sh", "-c", "cd apps/backend && pnpm dev"]

Environment Variables

VariableDefaultDescription
DATABASE_URLpostgresql://planner:planner@localhost:5432/plannerPostgreSQL connection
JWT_SECRET(required)Secret for access tokens
JWT_REFRESH_SECRET(required)Secret for refresh tokens
UPLOAD_DIR./uploadsFile upload directory

Port Mapping

ServiceInternal PortExternal Port
Backend40004000
Frontend30003000
Website80808080
Docs80808081
PostgreSQL54325432

Health Check

All services respond with HTTP 200 when running:

bash
curl http://localhost:4000/api/health
curl http://localhost:3000
curl http://localhost:8080
curl http://localhost:8081