9.6 KiB
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:
- Website: http://localhost:3000
- API: http://localhost:3001
- Database: localhost:5432
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:
- Nginx (Website + API): http://localhost:80
Available Commands
Development
npm run docker:dev- Start dev environmentnpm run docker:dev:build- Rebuild and startnpm run docker:dev:down- Stop servicesnpm run docker:dev:logs- View logsnpm run docker:dev:clean- Stop and remove volumes
Production
npm run docker:prod- Start prod environmentnpm run docker:prod:build- Rebuild and startnpm run docker:prod:down- Stop servicesnpm run docker:prod:logs- View logsnpm 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 (ports3100/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).
- Uses
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 forhttp://localhost:3100andhttp://localhost:3101/healthto 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
- Browser calls use
-
API “mock vs real” is controlled by API runtime env:
Practical presets:
- Website + real API (Docker dev):
npm run docker:dev:build(Website3000, API3001, Postgres required).- Website browser → API:
NEXT_PUBLIC_API_BASE_URL=http://localhost:3001 - Website container → API container:
API_BASE_URL=http://api:3000
- Website browser → API:
- Website + mock API (Docker smoke):
npm run test:docker:website(Website3100, API mock3101).- 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
- API mock is defined inline in
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 containerapi: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 containerapi: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 stack’s 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:devis running, usenpm run docker:dev:downbeforenpm run test:docker:websiteto 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 topostgresifDATABASE_URLis set, otherwiseinmemory)- Optional:
GRIDPILOT_API_BOOTSTRAP=0to skipBootstrapModule
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
- First time setup: Use
docker:dev:buildto ensure images are built - Clean slate: Use
docker:dev:cleanto remove all data and start fresh - Production testing: Test prod setup locally before deploying
- Database access: Use any PostgreSQL client with credentials from .env file
- Debugging: Attach debugger to port 9229 for API debugging
Production Deployment
Before deploying to production:
- Update
.env.productionwith real credentials - Configure SSL certificates in
nginx/ssl/ - Update Nginx configuration for HTTPS
- Set proper domain names in environment variables
- 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