Deployment Guide
This guide covers production deployment of DevTeam Orchestrator, including the API server, Temporal workflow engine, worker nodes, and supporting infrastructure.
Architecture Requirements
| Component | Purpose | Minimum | Recommended |
|---|---|---|---|
| API Server | REST API + WebSocket | 1 vCPU, 2GB RAM | 2 vCPU, 4GB RAM |
| PostgreSQL | Tasks, plans, HITL state | 1 vCPU, 2GB RAM | 2 vCPU, 4GB RAM |
| Temporal Server | Workflow orchestration | 2 vCPU, 4GB RAM | 4 vCPU, 8GB RAM |
| Worker Node (CPU) | Task execution | 2 vCPU, 4GB RAM | 4 vCPU, 8GB RAM |
| Worker Node (GPU) | Local model inference | 4 vCPU, 16GB RAM + GPU | 8 vCPU, 24GB RAM + GPU |
| Weaviate | Vector search (RAG) | 2 vCPU, 4GB RAM | 4 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 apiKubernetes Deployment
For larger-scale deployments, use Kubernetes with Helm:
Install the Helm Chart
helm repo add devteam https://charts.devteam.marsala.dev
helm repo updateConfigure 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-tlsDeploy
helm install devteam devteam/devteam-orchestrator \
-f values.yaml \
-n orchestrator \
--create-namespaceNginx 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.devFirewall 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 enableAuthentication
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
| Endpoint | Purpose |
|---|---|
GET /health | API server health |
GET /health/temporal | Temporal connection status |
GET /health/database | PostgreSQL connection status |
GET /health/workers | Worker node status summary |
GET /health/weaviate | Weaviate 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"} 3Backup
# 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
- API Reference -- Full REST API documentation
- Multi-Agent Guide -- Scaling worker nodes