Docker Configuration Guide
Helix Platform Containerization
Overview
This document explains the Docker containerization strategy for Helix microservices. Each service runs as an independent container built from the monorepo source.
Key Principles
- Monorepo for Development - All code in one repository
- Containers for Production - Each service deploys independently
- Multi-Stage Builds - Optimized image sizes
- Conditional Prisma - Generate only needed clients
Base Dockerfile Template
The Dockerfile.template provides the standard build pattern for all Helix services:
Stage 1: Builder
- Installs all dependencies (including dev)
- Generates Prisma clients (central + service-specific if exists)
- Builds service using Nx
- Bundles shared libraries
Stage 2: Production
- Only production dependencies
- Only built service code
- Prisma clients included
- Runs as non-root user
- Health check configured
Building Services
Using Build Arguments
# Build a specific service
docker build \
--build-arg SERVICE_NAME=auth-service \
-t helix/auth-service:latest \
-f Dockerfile.template .
# Build with version tag
docker build \
--build-arg SERVICE_NAME=kira-service \
-t helix/kira-service:1.0.0 \
-f Dockerfile.template .
Building All Services
# Build all services (when implemented)
for service in api-gateway auth-service tenant-service admin-service file-service kira-service vera-service cleverscreen-service; do
docker build \
--build-arg SERVICE_NAME=$service \
-t helix/$service:latest \
-f Dockerfile.template .
done
Building with Nx Affected
# Build only services changed since last deploy
for service in $(npx nx affected:apps --base=main --plain); do
docker build \
--build-arg SERVICE_NAME=$service \
-t helix/$service:$GIT_SHA \
-f Dockerfile.template .
done
Prisma Client Generation
Two Types of Clients
1. Central Database Client (ALL services):
- Location:
prisma/central/schema.prisma - Generated: Always
- Used for: Tenant lookups, auth, audit logs
- Output:
node_modules/@prisma/client-central
2. Service-Specific Client (Optional):
- Location:
apps/{service}/prisma/schema.prisma - Generated: Only if schema exists
- Used for: Product-specific data
- Output:
node_modules/@prisma/client
Which Services Need Which
| Service | Central Client | Service Client |
|---|---|---|
| api-gateway | ✅ | ❌ |
| auth-service | ✅ | ❌ |
| tenant-service | ✅ | ❌ |
| admin-service | ✅ | ❌ |
| file-service | ✅ | ❌ |
| kira-service | ✅ | ✅ |
| vera-service | ✅ | ✅ |
| cleverscreen-service | ✅ | ✅ |
Service-Specific Dockerfiles
Services can have their own Dockerfile for specific requirements:
Example: apps/kira-service/Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY nx.json tsconfig.base.json ./
COPY apps/ apps/
COPY libs/ libs/
COPY prisma/ prisma/
RUN npm ci
# Generate BOTH Prisma clients for KIRA
RUN npx prisma generate --schema=prisma/central/schema.prisma
RUN npx prisma generate --schema=apps/kira-service/prisma/schema.prisma
RUN npx nx build kira-service --configuration=production
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist/apps/kira-service ./
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
ENV NODE_ENV=production
ENV PORT=3010
EXPOSE 3010
HEALTHCHECK --interval=30s --timeout=3s \
CMD node -e "require('http').get('http://localhost:3010/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
USER node
CMD ["node", "main.js"]
Docker Compose for Local Development
Architecture Overview
The docker-compose setup provides a complete local development environment with:
Infrastructure Services (Always Running):
- PostgreSQL - Multi-database setup (3-tier architecture)
- Redis - Queue and cache
- MinIO - S3-compatible local storage
Application Services (Commented Out Until Implemented):
- API Gateway - Port 3000 (ONLY exposed service)
- Backend Services - Internal network only (ports 3001-3004, 3010-3012)
3-Tier Database Architecture
The PostgreSQL container creates multiple databases automatically:
Tier 1 & 2 (Central - Used by ALL services):
helix_central- Platform metadata (NO PII)helix_tenant_dev_shared- Shared tenant data (WITH PII)
Tier 3 (Service-Specific - ONLY product services):
helix_kira_dev- KIRA service databasehelix_vera_dev- VERA service databasehelix_cleverscreen_dev- CleverScreen service database
Core services (API Gateway, Auth, Tenant, Admin, File) use ONLY Tier 1 & 2.
Port Exposure Strategy ⚠️
CRITICAL: Only the API Gateway is exposed to the host machine.
# CORRECT - API Gateway (exposed)
api-gateway:
ports:
- "3000:3000" # Accessible from host
# CORRECT - Backend services (internal only)
auth-service:
expose:
- "3001" # Docker network only
# ports: # Commented out for production
# - "3001:3001" # Uncomment for debugging
Why?
- Frontend/clients connect ONLY to API Gateway (localhost:3000)
- API Gateway proxies requests to backend services via Docker network
- Backend services cannot be accessed directly from host (security)
- Mimics production architecture where services are behind load balancer
Starting the Stack
# Start infrastructure only (PostgreSQL, Redis, MinIO)
docker-compose up -d
# View logs
docker-compose logs -f
# View logs for specific service
docker-compose logs -f postgres
# Check service status
docker-compose ps
Stopping the Stack
# Stop all services (preserves data)
docker-compose down
# Stop and remove volumes (CLEAN SLATE - deletes all data)
docker-compose down -v
# Stop specific service
docker-compose stop postgres
Database Initialization
The scripts/init-db.sh script automatically creates all databases on first startup:
# Databases created automatically:
✓ helix_central
✓ helix_tenant_dev_shared
✓ helix_kira_dev
✓ helix_vera_dev
✓ helix_cleverscreen_dev
Verify databases:
docker-compose exec postgres psql -U helix -c "\l"
Resetting Local Environment
To start completely fresh:
# 1. Stop and remove everything
docker-compose down -v
# 2. Restart (databases recreated)
docker-compose up -d
# 3. Verify
docker-compose ps
docker-compose logs postgres | grep "database system is ready"
Debugging Backend Services
When services are implemented, you can access them directly for debugging:
# In docker-compose.yml, uncomment the ports: section
auth-service:
expose:
- "3001"
ports: # ← Uncomment this
- "3001:3001"
Then restart:
docker-compose up -d auth-service
curl http://localhost:3001/health
Remember to comment out again before committing!
Accessing Infrastructure Tools
PostgreSQL:
# Connect via psql
docker-compose exec postgres psql -U helix -d helix_central
# Or use your favorite GUI tool:
# Host: localhost
# Port: 5432
# User: helix
# Password: helix_dev_password
Redis:
# Connect via redis-cli
docker-compose exec redis redis-cli
# Or use Redis Commander/RedisInsight:
# Host: localhost
# Port: 6379
MinIO Console:
# Web UI:
http://localhost:9001
# Credentials:
# Username: helix
# Password: helix_dev_password
# API Endpoint:
http://localhost:9000
Services Included
Infrastructure (Exposed Ports):
- PostgreSQL - Port 5432
- Redis - Port 6379
- MinIO API - Port 9000
- MinIO Console - Port 9001
Application Services (When Uncommented):
- API Gateway - Port 3000 (ONLY exposed app port)
- Auth Service - Internal only (3001)
- Tenant Service - Internal only (3002)
- Admin Service - Internal only (3003)
- File Service - Internal only (3004)
- KIRA Service - Internal only (3010)
- VERA Service - Internal only (3011)
- CleverScreen Service - Internal only (3012)
Network Architecture
All services communicate via the helix-network Docker bridge network:
Frontend (localhost:3001)
↓
API Gateway (localhost:3000)
↓
Backend Services (Docker network)
├─ auth-service:3001
├─ tenant-service:3002
├─ admin-service:3003
├─ file-service:3004
├─ kira-service:3010
├─ vera-service:3011
└─ cleverscreen-service:3012
Uncommenting Services
As services are implemented (HLX-6 through HLX-13), uncomment them in docker-compose.yml:
# Find the service block
# api-gateway: ← Remove the # from all lines in the block
# Then restart
docker-compose up -d api-gateway
# Verify
curl http://localhost:3000/health
Troubleshooting
Database won't start:
# Check logs
docker-compose logs postgres
# Recreate
docker-compose down -v
docker-compose up -d postgres
Service can't connect to database:
# Verify database is healthy
docker-compose ps postgres
# Check database exists
docker-compose exec postgres psql -U helix -c "\l"
# Test connection from service
docker-compose exec auth-service nc -zv postgres 5432
Services can't communicate:
# Verify network
docker network inspect helixcore_helix-network
# Test DNS resolution
docker-compose exec api-gateway ping auth-service
MinIO bucket doesn't exist:
# Create bucket manually
docker-compose exec minio mc mb /data/helix-dev
Image Optimization
Expected Sizes
| Service Type | Size Range |
|---|---|
| Core services (no product DB) | 80-100 MB |
| Product services (with DB) | 100-120 MB |
| Services with AI libraries | 200-300 MB |
| Services with heavy deps | 300-400 MB |
Optimization Techniques
- Multi-stage builds - Separate build and runtime
- Alpine base image - ~5MB vs 50MB
- Production deps only - Exclude devDependencies
- Tree-shaking - Remove unused code
- Layer caching - Faster rebuilds
- npm cache clean - Remove install artifacts
Layer Caching Strategy
Optimize build speed with proper layer ordering:
# 1. Package files (rarely change)
COPY package*.json ./
RUN npm ci
# 2. Schemas (change less than code)
COPY prisma/ prisma/
RUN npx prisma generate
# 3. Source code (changes frequently)
COPY apps/ apps/
COPY libs/ libs/
# 4. Build (always fresh)
RUN npx nx build $SERVICE_NAME
Benefits:
- Package layer cached until dependencies change
- Prisma generation cached until schemas change
- Source changes don't invalidate earlier layers
Security Best Practices
In Dockerfile
# Use specific version tags
FROM node:18.19-alpine # Not :latest
# Run as non-root user
USER node
# Health checks
HEALTHCHECK --interval=30s CMD ...
# No secrets in build
# Use runtime environment variables instead
In docker-compose.yml
# Use secrets for sensitive data
secrets:
db_password:
file: ./secrets/db_password.txt
services:
postgres:
environment:
POSTGRES_PASSWORD: /run/secrets/db_password
Environment Variables
Each service requires environment configuration:
# Service
SERVICE_NAME=auth-service
SERVICE_MODE=api
PORT=3001
NODE_ENV=development
# Database
CENTRAL_DATABASE_URL=postgresql://helix:password@postgres-central:5432/helix_central
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
# WorkOS
WORKOS_API_KEY=your_api_key
WORKOS_CLIENT_ID=your_client_id
Health Checks
All services include health check endpoints:
Dockerfile Health Check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD node -e "require('http').get('http://localhost:${PORT}/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
Service Implementation
@Get('health')
async health() {
return {
status: 'healthy',
service: 'auth-service',
timestamp: new Date().toISOString()
};
}
Troubleshooting
Build Fails
# Check Nx build works locally
npx nx build auth-service
# Check Prisma generation
npx prisma generate --schema=prisma/central/schema.prisma
# Clear Nx cache
npx nx reset
Container Won't Start
# Check logs
docker logs helix-auth-service
# Check health
docker inspect --format='{{.State.Health.Status}}' helix-auth-service
# Enter container for debugging
docker exec -it helix-auth-service sh
Image Too Large
# Check image layers
docker history helix/auth-service:latest
# Check what's in the image
docker run --rm helix/auth-service:latest ls -lah
# Ensure .dockerignore is properly configured
CI/CD Integration
GitHub Actions Example
name: Build and Push Docker Images
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
service: ${{ fromJson(needs.affected.outputs.services) }}
steps:
- uses: actions/checkout@v4
- name: Build Docker Image
run: |
docker build \
--build-arg SERVICE_NAME=${{ matrix.service }} \
-t $ECR_REGISTRY/${{ matrix.service }}:${{ github.sha }} \
-f Dockerfile.template .
Best Practices
DO ✅
- Use multi-stage builds
- Pin Node.js version
- Run as non-root user
- Include health checks
- Optimize layer caching
- Use .dockerignore
- Generate only needed Prisma clients
DON'T ❌
- Use
:latesttag in production - Include secrets in images
- Run as root user
- Skip health checks
- Ignore image size
- Copy entire node_modules
- Generate unused Prisma clients
Reference
- Build Strategy: See Build Architecture
- Architecture: This document
- Database Setup: See Database & Prisma
Last Updated: 2025-10-21
Maintained By: CleverChain Platform Team