# Docker Compose Templates

Nickel-based Docker Compose templates for deploying platform services across all deployment modes.

## Overview

This directory contains Nickel templates that generate Docker Compose files for different deployment scenarios.
Each template imports configuration from `values/*.ncl` and expands to valid Docker Compose YAML.

**Key Pattern**: Templates use **Nickel composition** to build service definitions dynamically based on configuration, allowing parameterized infrastructure-as-code.

## Templates

### 1. platform-stack.solo.yml.ncl

**Purpose**: Single-developer local development stack

**Services**:
- `orchestrator` - Workflow engine
- `control-center` - Policy and RBAC management
- `mcp-server` - MCP protocol server

**Configuration**:
- Network: Bridge network named `provisioning`
- Volumes: 5 named volumes for persistence
  - `orchestrator-data` - Orchestrator workflows
  - `control-center-data` - Control Center policies
  - `mcp-server-data` - MCP Server cache
  - `logs` - Shared log volume
  - `cache` - Shared cache volume
- Ports:
  - 9090 - Orchestrator API
  - 8080 - Control Center UI
  - 8888 - MCP Server
- Health Checks: 30-second intervals for all services
- Logging: JSON format, 10MB max file size, 3 backups
- Restart Policy: `unless-stopped` (survives host reboot)

**Usage**:

```bash
# Generate from Nickel template
nickel export --format json platform-stack.solo.yml.ncl | yq -P > docker-compose.solo.yml

# Start services
docker-compose -f docker-compose.solo.yml up -d

# View logs
docker-compose -f docker-compose.solo.yml logs -f

# Stop services
docker-compose -f docker-compose.solo.yml down
```

**Environment Variables** (recommended in `.env` file):

```bash
ORCHESTRATOR_LOG_LEVEL=debug
CONTROL_CENTER_LOG_LEVEL=info
MCP_SERVER_LOG_LEVEL=info
```

---

### 2. platform-stack.multiuser.yml.ncl

**Purpose**: Team collaboration with persistent database storage

**Services** (6 total):
- `postgres` - Primary database (PostgreSQL 15)
- `orchestrator` - Workflow engine
- `control-center` - Policy and RBAC management
- `mcp-server` - MCP protocol server
- `surrealdb` - Workflow storage (SurrealDB server)
- `gitea` - Git repository hosting (optional, for version control)

**Configuration**:
- Network: Custom bridge network named `provisioning-network`
- Volumes:
  - `postgres-data` - PostgreSQL database files
  - `orchestrator-data` - Orchestrator workflows
  - `control-center-data` - Control Center policies
  - `surrealdb-data` - SurrealDB files
  - `gitea-data` - Gitea repositories and configuration
  - `logs` - Shared logs
- Ports:
  - 9090 - Orchestrator API
  - 8080 - Control Center UI
  - 8888 - MCP Server
  - 5432 - PostgreSQL (internal only)
  - 8000 - SurrealDB (internal only)
  - 3000 - Gitea web UI (optional)
  - 22 - Gitea SSH (optional)
- Service Dependencies: Explicit `depends_on` with health checks
  - Control Center waits for PostgreSQL
  - SurrealDB starts before Orchestrator
- Health Checks: Service-specific health checks
- Restart Policy: `always` (automatic recovery on failure)
- Logging: JSON format with rotation

**Usage**:

```bash
# Generate from Nickel template
nickel export --format json platform-stack.multiuser.yml.ncl | yq -P > docker-compose.multiuser.yml

# Create environment file
cat > .env.multiuser << 'EOF'
DB_PASSWORD=secure-postgres-password
SURREALDB_PASSWORD=secure-surrealdb-password
JWT_SECRET=secure-jwt-secret-256-bits
EOF

# Start services
docker-compose -f docker-compose.multiuser.yml --env-file .env.multiuser up -d

# Wait for all services to be healthy
docker-compose -f docker-compose.multiuser.yml ps

# Create database and initialize schema (one-time)
docker-compose exec postgres psql -U postgres -c "CREATE DATABASE provisioning;"
```

**Database Initialization**:

```bash
# Connect to PostgreSQL for schema creation
docker-compose exec postgres psql -U provisioning -d provisioning

# Connect to SurrealDB for schema setup
docker-compose exec surrealdb surreal sql --auth root:password

# Connect to Gitea web UI
# http://localhost:3000 (admin:admin by default)
```

**Environment Variables** (in `.env.multiuser`):

```bash
# Database Credentials (CRITICAL - change before production)
DB_PASSWORD=your-strong-password
SURREALDB_PASSWORD=your-strong-password

# Security
JWT_SECRET=your-256-bit-random-string

# Logging
ORCHESTRATOR_LOG_LEVEL=info
CONTROL_CENTER_LOG_LEVEL=info
MCP_SERVER_LOG_LEVEL=info

# Optional: Gitea Configuration
GITEA_DOMAIN=localhost:3000
GITEA_ROOT_URL=http://localhost:3000/
```

---

### 3. platform-stack.cicd.yml.ncl

**Purpose**: Ephemeral CI/CD pipeline stack with minimal persistence

**Services** (2 total):
- `orchestrator` - API-only mode (no UI, streamlined for programmatic use)
- `api-gateway` - Optional: Request routing and authentication

**Configuration**:
- Network: Bridge network
- Volumes:
  - `orchestrator-tmpfs` - Temporary storage (tmpfs - in-memory, no persistence)
- Ports:
  - 9090 - Orchestrator API (read-only orchestrator state)
  - 8000 - API Gateway (optional)
- Health Checks: Fast checks (10-second intervals)
- Restart Policy: `no` (containers do not auto-restart)
- Logging: Minimal (only warnings and errors)
- Cleanup: All artifacts deleted when containers stop

**Characteristics**:
- **Ephemeral**: No persistent storage (uses tmpfs)
- **Fast Startup**: Minimal services, quick boot time
- **API-First**: No UI, command-line/API integration only
- **Stateless**: Clean slate each run
- **Low Resource**: Minimal memory/CPU footprint

**Usage**:

```bash
# Generate from Nickel template
nickel export --format json platform-stack.cicd.yml.ncl | yq -P > docker-compose.cicd.yml

# Start ephemeral stack
docker-compose -f docker-compose.cicd.yml up

# Run CI/CD commands (in parallel terminal)
curl -X POST http://localhost:9090/api/workflows 
  -H "Content-Type: application/json" 
  -d @workflow.json

# Stop and cleanup (all data lost)
docker-compose -f docker-compose.cicd.yml down
# Or with volume cleanup
docker-compose -f docker-compose.cicd.yml down -v
```

**CI/CD Integration Example**:

```bash
# GitHub Actions workflow
- name: Start Provisioning Stack
  run: docker-compose -f docker-compose.cicd.yml up -d

- name: Run Tests
  run: |
    ./tests/integration.sh
    curl -X GET http://localhost:9090/health

- name: Cleanup
  if: always()
  run: docker-compose -f docker-compose.cicd.yml down -v
```

**Environment Variables** (minimal):

```bash
# Logging (optional)
ORCHESTRATOR_LOG_LEVEL=warn
```

---

### 4. platform-stack.enterprise.yml.ncl

**Purpose**: Production-grade high-availability deployment

**Services** (10+ total):
- `postgres` - PostgreSQL 15 (primary database)
- `orchestrator` (3 replicas) - Load-balanced workflow engine
- `control-center` (2 replicas) - Load-balanced policy management
- `mcp-server` (1-2 replicas) - MCP server for AI integration
- `surrealdb-1`, `surrealdb-2`, `surrealdb-3` - SurrealDB cluster (3 nodes)
- `nginx` - Load balancer and reverse proxy
- `prometheus` - Metrics collection
- `grafana` - Visualization and dashboards
- `loki` - Log aggregation

**Configuration**:
- Network: Custom bridge network named `provisioning-enterprise`
- Volumes:
  - `postgres-data` - PostgreSQL HA storage
  - `surrealdb-node-1`, `surrealdb-node-2`, `surrealdb-node-3` - Cluster storage
  - `prometheus-data` - Metrics storage
  - `grafana-data` - Grafana configuration
  - `loki-data` - Log storage
  - `logs` - Shared log aggregation
- Ports:
  - 80 - HTTP (Nginx reverse proxy)
  - 443 - HTTPS (TLS - requires certificates)
  - 9090 - Orchestrator API (internal)
  - 8080 - Control Center UI (internal)
  - 8888 - MCP Server (internal)
  - 5432 - PostgreSQL (internal only)
  - 8000 - SurrealDB cluster (internal)
  - 9091 - Prometheus metrics (internal)
  - 3000 - Grafana dashboards (external)
- Service Dependencies:
  - Control Center waits for PostgreSQL
  - Orchestrator waits for SurrealDB cluster
  - MCP Server waits for Orchestrator and Control Center
  - Prometheus waits for all services
- Health Checks: 30-second intervals with 10-second timeout
- Restart Policy: `always` (high availability)
- Load Balancing: Nginx upstream blocks for orchestrator, control-center
- Logging: JSON format with 500MB files, kept 30 versions

**Architecture**:

```bash
┌──────────────────────┐
│   External Client    │
│  (HTTPS, Port 443)   │
└──────────┬───────────┘
           │
    ┌──────▼──────────┐
    │ Nginx Load      │
    │ Balancer        │
    │ (TLS, CORS,     │
    │  Rate Limiting) │
    └───────┬──────┬──────┬─────┐
            │      │      │     │
   ┌────────▼──┐  ┌──────▼──┐ ┌──▼────────┐
   │Orchestrator│  │Control  │ │MCP Server │
   │ (3 copies) │  │ Center  │ │ (1-2 copy)│
   │            │  │(2 copies)│ │          │
   └────────┬──┘  └─────┬───┘  └──┬───────┘
            │           │         │
    ┌───────▼────────┬──▼────┐    │
    │  SurrealDB     │  PostSQL  │
    │  Cluster       │  HA      │
    │  (3 nodes)     │  (Primary/│
    │                │   Replica)│
    └────────────────┴──────────┘

Observability Stack:
┌────────────┬───────────┬───────────┐
│ Prometheus │  Grafana  │   Loki    │
│  (Metrics) │(Dashboard)│   (Logs)  │
└────────────┴───────────┴───────────┘
```

**Usage**:

```bash
# Generate from Nickel template
nickel export --format json platform-stack.enterprise.yml.ncl | yq -P > docker-compose.enterprise.yml

# Create environment file with secrets
cat > .env.enterprise << 'EOF'
# Database
DB_PASSWORD=generate-strong-password
SURREALDB_PASSWORD=generate-strong-password

# Security
JWT_SECRET=generate-256-bit-random-string
ADMIN_PASSWORD=generate-strong-admin-password

# TLS Certificates
TLS_CERT_PATH=/path/to/cert.pem
TLS_KEY_PATH=/path/to/key.pem

# Logging and Monitoring
PROMETHEUS_RETENTION=30d
GRAFANA_ADMIN_PASSWORD=generate-strong-password
LOKI_RETENTION_DAYS=30
EOF

# Start entire stack
docker-compose -f docker-compose.enterprise.yml --env-file .env.enterprise up -d

# Verify all services are healthy
docker-compose -f docker-compose.enterprise.yml ps

# Check load balancer status
curl -H "Host: orchestrator.example.com" http://localhost/health

# Access monitoring
# Grafana: http://localhost:3000 (admin/password)
# Prometheus: http://localhost:9091 (internal)
# Loki: http://localhost:3100 (internal)
```

**Production Checklist**:
- [ ] Generate strong database passwords (32+ characters)
- [ ] Generate strong JWT secret (256-bit random string)
- [ ] Provision valid TLS certificates (not self-signed)
- [ ] Configure Nginx upstream health checks
- [ ] Set up log retention policies (30+ days)
- [ ] Enable Prometheus scraping with 15-second intervals
- [ ] Configure Grafana dashboards and alerts
- [ ] Test SurrealDB cluster failover
- [ ] Document backup procedures
- [ ] Enable PostgreSQL replication and backups
- [ ] Configure external log aggregation (ELK stack, Splunk, etc.)

**Environment Variables** (in `.env.enterprise`):

```bash
# Database Credentials (CRITICAL)
DB_PASSWORD=your-strong-password-32-chars-min
SURREALDB_PASSWORD=your-strong-password-32-chars-min

# Security
JWT_SECRET=your-256-bit-random-base64-encoded-string
ADMIN_PASSWORD=your-strong-admin-password

# TLS/HTTPS
TLS_CERT_PATH=/etc/provisioning/certs/server.crt
TLS_KEY_PATH=/etc/provisioning/certs/server.key

# Logging and Monitoring
PROMETHEUS_RETENTION=30d
PROMETHEUS_SCRAPE_INTERVAL=15s
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=your-strong-grafana-password
LOKI_RETENTION_DAYS=30

# Optional: External Integrations
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxxxxxx
PAGERDUTY_INTEGRATION_KEY=your-pagerduty-key
```

---

## Workflow: From Nickel to Docker Compose

### 1. Configuration Source (values/*.ncl)

```nickel
# values/orchestrator.enterprise.ncl
{
  orchestrator = {
    server = {
      host = "0.0.0.0",
      port = 9090,
      workers = 8,
    },
    storage = {
      backend = 'surrealdb_cluster,
      surrealdb_url = "surrealdb://surrealdb-1:8000",
    },
    queue = {
      max_concurrent_tasks = 100,
      retry_attempts = 5,
      task_timeout = 7200000,
    },
    monitoring = {
      enabled = true,
      metrics_interval = 10,
    },
  },
}
```

### 2. Template Generation (Nickel → JSON)

```nickel
# Exports Nickel config as JSON
nickel export --format json platform-stack.enterprise.yml.ncl
```

### 3. YAML Conversion (JSON → YAML)

```yaml
# Converts JSON to YAML format
nickel export --format json platform-stack.enterprise.yml.ncl | yq -P > docker-compose.enterprise.yml
```

### 4. Deployment (YAML → Running Containers)

```yaml
# Starts all services defined in YAML
docker-compose -f docker-compose.enterprise.yml up -d
```

---

## Common Customizations

### Change Service Replicas

Edit the template to adjust replica counts:

```bash
# In platform-stack.enterprise.yml.ncl
let orchestrator_replicas = 5  in  # Instead of 3
let control_center_replicas = 3 in  # Instead of 2
services.orchestrator_replicas
```

### Add Custom Service

Add to the template services record:

```bash
# In platform-stack.enterprise.yml.ncl
services = base_services & {
  custom_service = {
    image = "custom:latest",
    ports = ["9999:9999"],
    volumes = ["custom-data:/data"],
    restart = "always",
    healthcheck = {
      test = ["CMD", "curl", "-f", "http://localhost:9999/health"],
      interval = "30s",
      timeout = "10s",
      retries = 3,
    },
  },
}
```

### Modify Resource Limits

In each service definition:

```bash
orchestrator = {
  deploy = {
    resources = {
      limits = {
        cpus = "2.0",
        memory = "2G",
      },
      reservations = {
        cpus = "1.0",
        memory = "1G",
      },
    },
  },
}
```

---

## Validation and Testing

### Syntax Validation

```bash
# Validate YAML before deploying
docker-compose -f docker-compose.enterprise.yml config --quiet

# Check service definitions
docker-compose -f docker-compose.enterprise.yml ps
```

### Health Checks

```bash
# Monitor health of all services
watch docker-compose ps

# Check specific service health
docker-compose exec orchestrator curl -s http://localhost:9090/health
```

### Log Inspection

```bash
# View logs from all services
docker-compose logs -f

# View logs from specific service
docker-compose logs -f orchestrator

# Follow specific container
docker logs -f $(docker ps | grep orchestrator | awk '{print $1}')
```

---

## Troubleshooting

### Port Already in Use

**Error**: `bind: address already in use`

**Fix**: Change port in template or stop conflicting container:

```bash
# Find process using port
lsof -i :9090

# Kill process
kill -9 <PID>

# Or change port in docker-compose file
ports:
  - "9999:9090"  # Use 9999 instead
```

### Service Fails to Start

**Check logs**:

```bash
docker-compose logs orchestrator
```

**Common causes**:
- Port conflict - Check if another service uses port
- Missing volume - Create volume before starting
- Network connectivity - Verify docker network exists
- Database not ready - Wait for db service to become healthy
- Configuration error - Validate YAML syntax

### Persistent Volume Issues

**Clean volumes** (WARNING: Deletes data):

```bash
docker-compose down -v
docker volume prune -f
```

---

## See Also

- **Kubernetes Templates**: `../kubernetes/` - For production K8s deployments
- **Configuration System**: `../../` - Full configuration documentation
- **Examples**: `../../examples/` - Example deployment scenarios
- **Scripts**: `../../scripts/` - Automation scripts

---

**Version**: 1.0
**Last Updated**: 2025-01-05
**Status**: Production Ready
