Guides
Deployment

Deployment Guide

This guide covers production deployment of DevTeam Orchestrator, including the API server, Temporal workflow engine, worker nodes, and supporting infrastructure.

Architecture Requirements

ComponentPurposeMinimumRecommended
API ServerREST API + WebSocket1 vCPU, 2GB RAM2 vCPU, 4GB RAM
PostgreSQLTasks, plans, HITL state1 vCPU, 2GB RAM2 vCPU, 4GB RAM
Temporal ServerWorkflow orchestration2 vCPU, 4GB RAM4 vCPU, 8GB RAM
Worker Node (CPU)Task execution2 vCPU, 4GB RAM4 vCPU, 8GB RAM
Worker Node (GPU)Local model inference4 vCPU, 16GB RAM + GPU8 vCPU, 24GB RAM + GPU
WeaviateVector search (RAG)2 vCPU, 4GB RAM4 vCPU, 8GB RAM

Docker Compose Deployment

The simplest production deployment uses Docker Compose:

docker-compose.yml
version: "3.8"
 
services:
  api:
    image: matwal/devteam-api:latest
    ports:
      - "8090:8090"
    environment:
      - DATABASE_URL=postgresql://devteam:${DB_PASSWORD}@postgres:5432/devteam
      - TEMPORAL_ADDRESS=temporal:7233
      - JWT_SECRET=${JWT_SECRET}
      - API_PORT=8090
      - NODE_ENV=production
      - LOG_LEVEL=info
    depends_on:
      - postgres
      - temporal
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8090/health"]
      interval: 30s
      timeout: 10s
      retries: 3
 
  postgres:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=devteam
      - POSTGRES_USER=devteam
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    ports:
      - "5432:5432"
    restart: unless-stopped
 
  temporal:
    image: temporalio/auto-setup:1.24
    ports:
      - "7233:7233"
    environment:
      - DB=postgresql
      - DB_PORT=5432
      - POSTGRES_USER=devteam
      - POSTGRES_PWD=${DB_PASSWORD}
      - POSTGRES_SEEDS=postgres
    depends_on:
      - postgres
    restart: unless-stopped
 
  temporal-ui:
    image: temporalio/ui:2.45.3
    ports:
      - "8233:8080"
    environment:
      - TEMPORAL_ADDRESS=temporal:7233
    depends_on:
      - temporal
    restart: unless-stopped
 
  weaviate:
    image: semitechnologies/weaviate:1.28.4
    ports:
      - "8080:8080"
    environment:
      - PERSISTENCE_DATA_PATH=/var/lib/weaviate
      - QUERY_DEFAULTS_LIMIT=25
      - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true
      - DEFAULT_VECTORIZER_MODULE=text2vec-transformers
      - ENABLE_MODULES=text2vec-transformers
      - TRANSFORMERS_INFERENCE_API=http://t2v-transformers:8080
      - GOMEMLIMIT=2GiB
    volumes:
      - weaviate_data:/var/lib/weaviate
    restart: unless-stopped
 
  t2v-transformers:
    image: semitechnologies/transformers-inference:sentence-transformers-all-MiniLM-L6-v2
    environment:
      - ENABLE_CUDA=0
    restart: unless-stopped
 
  worker:
    image: matwal/devteam-worker:v1
    environment:
      - DEVTEAM_API_URL=http://api:8090
      - DEVTEAM_API_KEY=${WORKER_API_KEY}
      - TEMPORAL_ADDRESS=temporal:7233
      - DEVTEAM_QUEUE=default
      - DEVTEAM_CONCURRENCY=4
    depends_on:
      - api
      - temporal
    restart: unless-stopped
    deploy:
      replicas: 2
 
volumes:
  pgdata:
  weaviate_data:

Environment File

.env
DB_PASSWORD=your-secure-database-password
JWT_SECRET=your-jwt-secret-at-least-32-chars
WORKER_API_KEY=dtk_live_worker_key
đźš«

Always use strong, unique passwords for production deployments. Never use default credentials. Store secrets in a vault (AWS Secrets Manager, HashiCorp Vault) rather than .env files.

Start the Stack

docker compose up -d
docker compose logs -f api

Kubernetes Deployment

For larger-scale deployments, use Kubernetes with Helm:

Install the Helm Chart

helm repo add devteam https://charts.devteam.marsala.dev
helm repo update

Configure Values

values.yaml
api:
  replicas: 2
  resources:
    requests:
      cpu: "1"
      memory: "2Gi"
    limits:
      cpu: "2"
      memory: "4Gi"
  env:
    JWT_SECRET: "${JWT_SECRET}"
    LOG_LEVEL: "info"
 
postgres:
  enabled: true
  persistence:
    size: 50Gi
  auth:
    password: "${DB_PASSWORD}"
 
temporal:
  enabled: true
  server:
    replicas: 2
 
workers:
  default:
    replicas: 3
    queue: "default"
    concurrency: 4
    resources:
      requests:
        cpu: "2"
        memory: "4Gi"
  gpu:
    replicas: 1
    queue: "gpu-queue"
    concurrency: 2
    resources:
      requests:
        cpu: "4"
        memory: "16Gi"
        nvidia.com/gpu: "1"
 
weaviate:
  enabled: true
  persistence:
    size: 100Gi
 
ingress:
  enabled: true
  host: devteam.marsala.dev
  tls:
    enabled: true
    secretName: devteam-tls

Deploy

helm install devteam devteam/devteam-orchestrator \
  -f values.yaml \
  -n orchestrator \
  --create-namespace

Nginx Reverse Proxy

For exposing the API and dashboard through a single domain:

/etc/nginx/sites-enabled/devteam.conf
server {
    listen 443 ssl http2;
    server_name devteam.marsala.dev;
 
    ssl_certificate /etc/letsencrypt/live/devteam.marsala.dev/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/devteam.marsala.dev/privkey.pem;
 
    # API
    location /api/ {
        proxy_pass http://localhost:8090/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
 
    # WebSocket
    location /ws {
        proxy_pass http://localhost:8090/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
    }
 
    # Dashboard
    location / {
        proxy_pass http://localhost:8233/;
        proxy_set_header Host $host;
        proxy_cookie_flags _csrf nosecure samesite=lax;
    }
}

TLS and Security

Enable HTTPS

# Using Let's Encrypt
certbot --nginx -d devteam.marsala.dev -d doc.devteam.marsala.dev

Firewall Rules

# Allow only necessary ports
ufw default deny incoming
ufw allow ssh
ufw allow 443/tcp        # HTTPS
ufw allow from 100.64.0.0/10  # Tailscale mesh
ufw enable

Authentication

Configure JWT authentication for the API:

api-config.ts
export const authConfig = {
  jwtSecret: process.env.JWT_SECRET,
  tokenExpiry: '24h',
  refreshTokenExpiry: '30d',
  roles: {
    admin: ['*'],
    operator: ['tasks:*', 'plans:*', 'templates:read', 'hitl:*'],
    viewer: ['tasks:read', 'plans:read', 'templates:read'],
    worker: ['tasks:execute', 'tasks:update'],
  },
};

Monitoring

Health Endpoints

EndpointPurpose
GET /healthAPI server health
GET /health/temporalTemporal connection status
GET /health/databasePostgreSQL connection status
GET /health/workersWorker node status summary
GET /health/weaviateWeaviate connection status

Prometheus Metrics

The API server exposes Prometheus metrics at /metrics:

devteam_tasks_total{status="completed",model="sonnet"} 1547
devteam_tasks_total{status="failed",model="opus"} 23
devteam_tasks_duration_seconds{model="sonnet",quantile="0.99"} 12.3
devteam_plans_total{status="completed"} 234
devteam_hitl_pending_count 3
devteam_workers_active 5
devteam_queue_depth{queue="gpu-queue"} 12
devteam_queue_depth{queue="default"} 3

Backup

# PostgreSQL backup
pg_dump -U devteam devteam | gzip > backup-$(date +%Y%m%d).sql.gz
 
# Weaviate backup
curl -X POST http://localhost:8080/v1/backups/filesystem \
  -H 'Content-Type: application/json' \
  -d '{"id": "backup-20260220", "include": ["DocChunk", "USIRSTaxRule"]}'

Set up automated daily backups with a cron job. Store backups in S3 or equivalent object storage with a 30-day retention policy.

Next Steps