Skip to content

Self-Hosted Installation

Deploy DuraGraph in your own infrastructure for full control, data sovereignty, and cost optimization.

DuraGraph uses a modern, event-driven architecture:

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────▶│ API Server │────▶│ PostgreSQL │
│ (SDK/CLI) │ │ (Echo) │ │ (Events) │
└─────────────┘ └──────┬──────┘ └─────────────┘
┌──────▼──────┐
│ NATS │
│ JetStream │
└─────────────┘

Components:

  • API Server - Go-based REST API with SSE streaming
  • PostgreSQL - Event store, projections, and checkpoints
  • NATS JetStream - Real-time event streaming and messaging

The fastest way to get DuraGraph running:

Terminal window
# Clone the repository
git clone https://github.com/duragraph/duragraph.git
cd duragraph
# Start all services
docker compose up -d
# Verify services are running
docker compose ps
# Check health
curl http://localhost:8081/health

This starts:

  • API Server (:8081) - REST API and SSE streaming
  • PostgreSQL (:5433) - Database for events and state
  • NATS (:4223) - Message broker with JetStream
  • Dashboard (:5173) - Web interface

Create a .env file or set environment variables:

Terminal window
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USER=appuser
DB_PASSWORD=apppass
DB_NAME=appdb
DB_SSLMODE=disable
# NATS
NATS_URL=nats://localhost:4222
# Server
PORT=8080
HOST=0.0.0.0
# Optional: Authentication
AUTH_ENABLED=false
JWT_SECRET=your-256-bit-secret
# Optional: LLM Providers
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
VariableDescriptionDefault
DB_HOSTPostgreSQL hostlocalhost
DB_PORTPostgreSQL port5432
DB_USERDatabase userappuser
DB_PASSWORDDatabase passwordapppass
DB_NAMEDatabase nameappdb
DB_SSLMODESSL mode (disable, require)disable
NATS_URLNATS connection URLnats://localhost:4222
PORTAPI server port8080
HOSTAPI server host0.0.0.0
AUTH_ENABLEDEnable JWT authenticationfalse
JWT_SECRETJWT signing secret-
docker-compose.prod.yml
services:
db:
image: postgres:15
container_name: duragraph-postgres
environment:
POSTGRES_USER: ${DB_USER:-duragraph}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME:-duragraph}
ports:
- '5432:5432'
volumes:
- pgdata:/var/lib/postgresql/data
- ./deploy/sql:/docker-entrypoint-initdb.d
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${DB_USER:-duragraph}']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
nats:
image: nats:2.10-alpine
container_name: duragraph-nats
command: '-js -sd /data -m 8222'
ports:
- '4222:4222' # Client connections
- '8222:8222' # HTTP management/monitoring
volumes:
- natsdata:/data
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:8222/healthz']
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
server:
image: duragraph/server:latest
container_name: duragraph-server
ports:
- '8080:8080'
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_USER=${DB_USER:-duragraph}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME:-duragraph}
- DB_SSLMODE=disable
- NATS_URL=nats://nats:4222
- PORT=8080
- HOST=0.0.0.0
- AUTH_ENABLED=${AUTH_ENABLED:-true}
- JWT_SECRET=${JWT_SECRET}
depends_on:
db:
condition: service_healthy
nats:
condition: service_healthy
restart: unless-stopped
volumes:
pgdata:
natsdata:

Start production deployment:

Terminal window
# Set required secrets (replace with your actual values)
export DB_PASSWORD="<YOUR_DB_PASSWORD>"
export JWT_SECRET="<YOUR_JWT_SECRET>"
# Deploy
docker compose -f docker-compose.prod.yml up -d
  • Kubernetes 1.24+
  • kubectl configured
  • Helm 3.x (optional)
namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: duragraph
---
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: duragraph-config
namespace: duragraph
data:
DB_HOST: 'postgres'
DB_PORT: '5432'
DB_NAME: 'duragraph'
DB_SSLMODE: 'require'
NATS_URL: 'nats://nats:4222'
PORT: '8080'
HOST: '0.0.0.0'
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: duragraph-secrets
namespace: duragraph
type: Opaque
stringData:
DB_USER: duragraph
DB_PASSWORD: '<YOUR_DB_PASSWORD>'
JWT_SECRET: '<YOUR_JWT_SECRET>'
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: duragraph-server
namespace: duragraph
spec:
replicas: 3
selector:
matchLabels:
app: duragraph-server
template:
metadata:
labels:
app: duragraph-server
spec:
containers:
- name: server
image: duragraph/server:latest
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: duragraph-config
- secretRef:
name: duragraph-secrets
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: duragraph-server
namespace: duragraph
spec:
selector:
app: duragraph-server
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
---
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: duragraph-ingress
namespace: duragraph
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: '3600'
nginx.ingress.kubernetes.io/proxy-send-timeout: '3600'
spec:
ingressClassName: nginx
tls:
- hosts:
- duragraph.yourcompany.com
secretName: duragraph-tls
rules:
- host: duragraph.yourcompany.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: duragraph-server
port:
number: 8080

Apply manifests:

Terminal window
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml

DuraGraph automatically runs migrations on startup. For manual setup:

Terminal window
# Run all migrations
psql $DATABASE_URL -f deploy/sql/001_init.sql
psql $DATABASE_URL -f deploy/sql/002_event_store.sql
psql $DATABASE_URL -f deploy/sql/003_outbox.sql
psql $DATABASE_URL -f deploy/sql/004_projections.sql

To use a managed PostgreSQL instance (RDS, Cloud SQL, etc.):

Terminal window
# Set connection string
export DATABASE_URL="postgresql://user:pass@your-db-host:5432/duragraph?sslmode=require"
# Or individual variables
export DB_HOST=your-db-host
export DB_PORT=5432
export DB_USER=duragraph
export DB_PASSWORD=your-password
export DB_NAME=duragraph
export DB_SSLMODE=require

DuraGraph requires NATS JetStream for event streaming:

Terminal window
# Standalone NATS with JetStream
docker run -d --name nats \
-p 4222:4222 \
-p 8222:8222 \
-v natsdata:/data \
nats:2.10-alpine -js -sd /data -m 8222

For high availability, deploy a NATS cluster:

nats-cluster.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nats
namespace: duragraph
spec:
serviceName: nats
replicas: 3
selector:
matchLabels:
app: nats
template:
metadata:
labels:
app: nats
spec:
containers:
- name: nats
image: nats:2.10-alpine
args:
- '-js'
- '-sd'
- '/data'
- '-m'
- '8222'
- '--cluster_name'
- 'duragraph-nats'
- '--cluster'
- 'nats://0.0.0.0:6222'
- '--routes'
- 'nats://nats-0.nats:6222,nats://nats-1.nats:6222,nats://nats-2.nats:6222'
ports:
- containerPort: 4222
name: client
- containerPort: 6222
name: cluster
- containerPort: 8222
name: monitor
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ['ReadWriteOnce']
resources:
requests:
storage: 10Gi

Access NATS monitoring:

Terminal window
# Health check
curl http://localhost:8222/healthz
# Server info
curl http://localhost:8222/varz
# JetStream info
curl http://localhost:8222/jsz
# Connections
curl http://localhost:8222/connz

DuraGraph exposes Prometheus metrics at /metrics:

Terminal window
curl http://localhost:8081/metrics

Add to prometheus.yml:

scrape_configs:
- job_name: 'duragraph'
static_configs:
- targets: ['duragraph-server:8080']
metrics_path: /metrics
scrape_interval: 15s
MetricDescription
duragraph_runs_totalTotal runs created
duragraph_runs_activeCurrently running runs
duragraph_http_requests_totalHTTP requests by endpoint
duragraph_http_request_duration_secondsRequest latency histogram
Terminal window
# API health
curl http://localhost:8081/health
# Simple liveness check
curl http://localhost:8081/ok
# Server info
curl http://localhost:8081/info

Configure structured logging:

Terminal window
# JSON logs for production
export LOG_FORMAT=json
export LOG_LEVEL=info
# View logs
docker compose logs -f server

Scale API servers behind a load balancer:

Terminal window
# Docker Compose
docker compose up --scale server=3
# Kubernetes
kubectl scale deployment duragraph-server --replicas=5 -n duragraph
ComponentCPUMemoryStorage
API Server0.5-2 cores512MB-2GB-
PostgreSQL2-4 cores4-8GB50-500GB
NATS0.5-1 core256MB-1GB10-50GB
/etc/nginx/sites-available/duragraph
server {
listen 443 ssl http2;
server_name duragraph.yourcompany.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://localhost:8081;
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;
# SSE support
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_read_timeout 3600s;
}
}

Enable JWT authentication:

Terminal window
export AUTH_ENABLED=true
export JWT_SECRET="your-256-bit-secret-key"

Generate tokens:

Terminal window
# Using jwt-cli or similar
jwt encode --secret "$JWT_SECRET" '{"sub": "user-id", "exp": 1735689600}'
Terminal window
# Backup
docker exec duragraph-postgres pg_dump -U appuser appdb > backup.sql
# Restore
docker exec -i duragraph-postgres psql -U appuser appdb < backup.sql
apiVersion: batch/v1
kind: CronJob
metadata:
name: postgres-backup
namespace: duragraph
spec:
schedule: '0 2 * * *' # Daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: postgres:15
command:
- /bin/sh
- -c
- pg_dump -h postgres -U $DB_USER $DB_NAME | gzip > /backups/backup-$(date +%Y%m%d).sql.gz
envFrom:
- secretRef:
name: duragraph-secrets
volumeMounts:
- name: backups
mountPath: /backups
volumes:
- name: backups
persistentVolumeClaim:
claimName: backup-pvc
restartPolicy: OnFailure

Services won’t start:

Terminal window
# Check logs
docker compose logs server
docker compose logs db
docker compose logs nats
# Verify database connection
docker exec duragraph-server ping db

Database connection errors:

Terminal window
# Test connection
psql -h localhost -p 5433 -U appuser -d appdb
# Check if migrations ran
psql -h localhost -p 5433 -U appuser -d appdb -c "\dt"

NATS connection issues:

Terminal window
# Check NATS status
curl http://localhost:8223/healthz
# View NATS logs
docker compose logs nats

High latency:

Terminal window
# Check resource usage
docker stats
# Check database performance
docker exec duragraph-postgres pg_stat_activity
docker-compose.override.yml
services:
server:
environment:
- GOMAXPROCS=4
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G

Need help? Open an issue or check discussions.