Files
gridpilot.gg/README.docker.md
2025-12-29 19:44:11 +01:00

10 KiB
Raw Blame History

Docker Setup for GridPilot

This document describes the Docker setup for local development and production deployment of GridPilot.

Quick Start

Development

Start all services with hot-reloading:

npm run docker:dev:build

This will:

  • Start PostgreSQL database on port 5432
  • Start API on port 3001 (container port 3000, debugger 9229)
  • Start Website on port 3000
  • Enable hot-reloading for both apps

Access:

Production

Start all services in production mode:

npm run docker:prod:build

This will:

  • Build optimized Docker images
  • Start PostgreSQL, Redis, API, Website, and Nginx
  • Enable health checks, auto-restart, and resource limits
  • Configure caching and performance optimizations

Access:

Available Commands

Development

  • npm run docker:dev - Start dev environment (alias of docker:dev:up)
  • npm run docker:dev:up - Start dev environment
  • npm run docker:dev:postgres - Start dev environment with GRIDPILOT_API_PERSISTENCE=postgres
  • npm run docker:dev:inmemory - Start dev environment with GRIDPILOT_API_PERSISTENCE=inmemory
  • npm run docker:dev:build - Rebuild and start
  • npm run docker:dev:restart - Restart services
  • npm run docker:dev:ps - Show service status
  • npm run docker:dev:down - Stop services
  • npm run docker:dev:logs - View logs
  • npm run docker:dev:clean - Stop and remove volumes

Production

  • npm run docker:prod - Start prod environment
  • npm run docker:prod:build - Rebuild and start
  • npm run docker:prod:down - Stop services
  • npm run docker:prod:logs - View logs
  • npm run docker:prod:clean - Stop and remove volumes

Testing (Docker)

The goal of Docker-backed tests is to catch wiring issues between Website ↔ API (wrong hostnames/ports/env vars, missing CORS for credentialed requests, etc.) in a deterministic environment.

  • npm run test:docker:website - Start a dedicated test stack (ports 3100/3101) and run Playwright smoke tests against it.
    • Uses docker-compose.test.yml.
    • Runs the Website in Docker and talks to an API mock container.
    • Validates that pages render and that core API fetches succeed (hostname + CORS + routing).

Supporting scripts:

  • npm run docker:test:deps - Install monorepo deps inside a reusable Docker volume (one-shot).
  • npm run docker:test:up - Bring up the test stack.
  • npm run docker:test:wait - Wait for http://localhost:3100 and http://localhost:3101/health to be ready.
  • npm run docker:test:down - Tear the test stack down (including volumes).

Environment Variables

“Mock vs Real” (Website & API)

There is no AUTOMATION_MODE equivalent for the Website/API runtime.

  • Website “mock vs real” is controlled purely by which API base URL you point it at via getWebsiteApiBaseUrl():

    • Browser calls use NEXT_PUBLIC_API_BASE_URL
    • Server/Next.js calls use API_BASE_URL ?? NEXT_PUBLIC_API_BASE_URL
  • API “mock vs real” is controlled by API runtime env:

    • Persistence: GRIDPILOT_API_PERSISTENCE=postgres|inmemory in AppModule
    • Optional bootstrapping: GRIDPILOT_API_BOOTSTRAP=0|1 in AppModule

Practical presets:

  • Website + real API (Docker dev): npm run docker:dev:build (Website 3000, API 3001, Postgres required).
    • Website browser → API: NEXT_PUBLIC_API_BASE_URL=http://localhost:3001
    • Website container → API container: API_BASE_URL=http://api:3000
  • Website + mock API (Docker smoke): npm run test:docker:website (Website 3100, API mock 3101).
    • API mock is defined inline in docker-compose.test.yml
    • Website browser → API mock: NEXT_PUBLIC_API_BASE_URL=http://localhost:3101
    • Website container → API mock container: API_BASE_URL=http://api:3000

Website ↔ API Connection

The website talks to the API via fetch() in BaseApiClient, and it always includes cookies (credentials: 'include'). That means:

  • The browser must be pointed at a host-accessible API URL via NEXT_PUBLIC_API_BASE_URL
  • The server (Next.js / Node) must be pointed at a container-network API URL via API_BASE_URL (when running in Docker)

The single source of truth for “what base URL should I use?” is getWebsiteApiBaseUrl():

  • Browser: reads NEXT_PUBLIC_API_BASE_URL
  • Server: reads API_BASE_URL ?? NEXT_PUBLIC_API_BASE_URL
  • In Docker/CI/test: throws if missing (no silent localhost fallback)

Dev Docker defaults (docker-compose.dev.yml)

  • Website: http://localhost:3000
  • API: http://localhost:3001 (maps to container api:3000)
  • NEXT_PUBLIC_API_BASE_URL=http://localhost:3001 (browser → host port)
  • API_BASE_URL=http://api:3000 (website container → api container)

Test Docker defaults (docker-compose.test.yml)

This stack is intended for deterministic smoke tests and uses different host ports to avoid colliding with docker:dev:

  • Website: http://localhost:3100
  • API mock: http://localhost:3101 (maps to container api:3000)
  • NEXT_PUBLIC_API_BASE_URL=http://localhost:3101 (browser → host port)
  • API_BASE_URL=http://api:3000 (website container → api container)

Important: the test stacks API is a mock server defined inline in docker-compose.test.yml. It exists to validate Website ↔ API wiring, not domain correctness.

Troubleshooting

  • If docker:dev is running, use npm run docker:dev:down before npm run test:docker:website to avoid port conflicts.
  • If Docker volumes get stuck, run npm run docker:test:down (it uses --remove-orphans + rm -f).

API “Real vs In-Memory” Mode

The API can now be run either:

  • postgres: loads DatabaseModule (requires Postgres)
  • inmemory: does not load DatabaseModule (no Postgres required)

Control it with:

  • GRIDPILOT_API_PERSISTENCE=postgres|inmemory (defaults to postgres if DATABASE_URL is set, otherwise inmemory)
  • Optional: GRIDPILOT_API_BOOTSTRAP=0 to skip BootstrapModule

Development (.env.development)

Copy and customize as needed. Default values work out of the box.

Production (.env.production)

IMPORTANT: Update these before deploying:

  • Database credentials (POSTGRES_PASSWORD, DATABASE_URL)
  • Website/API URLs (NEXT_PUBLIC_API_BASE_URL, NEXT_PUBLIC_SITE_URL)
  • Vercel KV credentials (KV_REST_API_URL, KV_REST_API_TOKEN) (required for production email signups/rate limit)

Architecture

Development Setup

  • Hot-reloading enabled via volume mounts
  • Source code changes reflect immediately
  • Database persisted in named volume
  • Debug port exposed for API (9229)

Production Setup

  • Multi-stage builds for optimized images
  • Only production dependencies included
  • Nginx reverse proxy for both services
  • Health checks for all services
  • Auto-restart on failure

Docker Services

API (NestJS)

  • Dev: apps/api/Dockerfile.dev
  • Prod: apps/api/Dockerfile.prod
  • Port: 3000
  • Debug: 9229 (dev only)

Website (Next.js)

  • Dev: apps/website/Dockerfile.dev
  • Prod: apps/website/Dockerfile.prod
  • Port: 3001 (dev), 3000 (prod)

Database (PostgreSQL)

  • Image: postgres:15-alpine
  • Port: 5432 (internal)
  • Data: Persisted in Docker volume
  • Optimized with performance tuning parameters

Redis (Production only)

  • Image: redis:7-alpine
  • Port: 6379 (internal)
  • Configured with:
    • LRU eviction policy
    • 512MB max memory
    • AOF persistence
    • Password protection

Nginx (Production only)

  • Reverse proxy for website + API
  • Features:
    • Rate limiting (API: 10r/s, General: 30r/s)
    • Security headers (XSS, CSP, Frame-Options)
    • Gzip compression
    • Static asset caching
    • Connection pooling
    • Request buffering
  • Port: 80, 443

Troubleshooting

Services won't start

# Clean everything and rebuild
npm run docker:dev:clean
npm run docker:dev:build

Hot-reloading not working

Check that volume mounts are correct in docker-compose.dev.yml

Database connection issues

Ensure DATABASE_URL in .env matches the database service configuration

Check logs

# All services
npm run docker:dev:logs

# Specific service
docker-compose -f docker-compose.dev.yml logs -f api
docker-compose -f docker-compose.dev.yml logs -f website
docker-compose -f docker-compose.dev.yml logs -f db

Tips

  1. First time setup: Use docker:dev:build to ensure images are built
  2. Clean slate: Use docker:dev:clean to remove all data and start fresh
  3. Production testing: Test prod setup locally before deploying
  4. Database access: Use any PostgreSQL client with credentials from .env file
  5. Debugging: Attach debugger to port 9229 for API debugging

Production Deployment

Before deploying to production:

  1. Update .env.production with real credentials
  2. Configure SSL certificates in nginx/ssl/
  3. Update Nginx configuration for HTTPS
  4. Set proper domain names in environment variables
  5. Consider using Docker secrets for sensitive data

File Structure

.
├── docker-compose.dev.yml       # Development orchestration
├── docker-compose.prod.yml      # Production orchestration
├── .env.development             # Dev environment variables
├── .env.production              # Prod environment variables
├── apps/
│   ├── api/
│   │   ├── Dockerfile.dev       # API dev image
│   │   ├── Dockerfile.prod      # API prod image
│   │   └── .dockerignore
│   └── website/
│       ├── Dockerfile.dev       # Website dev image
│       ├── Dockerfile.prod      # Website prod image
│       └── .dockerignore
└── nginx/
    └── nginx.conf               # Nginx configuration