Agentspan is a durable execution runtime for AI agents. This guide covers every deployment path from a single-command local start to a production Kubernetes cluster with auto-scaling, TLS, and managed PostgreSQL.
- Quick Start
- Architecture
- Deployment Options
- Local Development
- Single Server — Docker
- Production — Docker Compose
- Production — Kubernetes
- Production — Helm
- Managed Database (RDS / Cloud SQL / Azure DB)
- TLS / HTTPS
- Observability
- Backup & Restore
- Production Checklist
- Configuration Reference
# Install the CLI
curl -fsSL https://raw.githubusercontent.com/agentspan-ai/agentspan/main/cli/install.sh | sh
# Start the server (SQLite, no setup needed)
export OPENAI_API_KEY=sk-...
agentspan server startOpen the UI at http://localhost:6767
| Component | Description |
|---|---|
| API Server | REST API at /api/** and React UI at /**, both served from port 6767 |
| Execution Engine | Durable state machine — survives restarts, picks up in-progress agents |
| Sweeper | Background process that monitors running agent executions |
| Tool Worker | Executes built-in tools (HTTP, file, code, search) |
| Credential Store | AES-256-GCM encrypted storage for API keys and secrets |
A single agentspan/server Docker image contains all components — no separate frontend container.
| Layer | Development | Production |
|---|---|---|
| Database | SQLite (file) | PostgreSQL 16 |
| Storage | Local file | PVC (K8s) or Docker volume |
| Scaling | Single process | 3–10 replicas + HPA |
| Networking | localhost:6767 | Ingress → LoadBalancer |
Internet ──► DNS ──► LoadBalancer ──► Ingress (nginx) ──► agentspan-server:6767
│
agentspan-postgres (StatefulSet + PVC)
Choose the stack that fits your environment:
| Environment | Method | Database | Best for |
|---|---|---|---|
| Local dev | agentspan server start |
SQLite | Development, testing |
| Single VM (demo) | docker run |
SQLite | Demos, staging |
| Single VM (prod) | Docker Compose | PostgreSQL | Small teams, single-region |
| Multi-instance | Kubernetes | PostgreSQL | HA, auto-scaling, production SaaS |
| GitOps | Helm | PostgreSQL | Templated multi-env deployments |
| Backend | When to use |
|---|---|
| SQLite | Local development only. Single process, no setup, data in agent-runtime.db. |
| PostgreSQL | All production deployments. ACID-compliant, supports multiple replicas. |
Zero config. Uses SQLite. Data persists in ./agent-runtime.db.
export OPENAI_API_KEY=sk-...
agentspan server startFiles created:
~/.agentspan/— server JAR and config./agent-runtime.db— SQLite database (working directory)
Reset: rm agent-runtime.db
docker run -d \
--name agentspan \
-p 6767:6767 \
-e OPENAI_API_KEY=sk-... \
-v agentspan_data:/data \
agentspan/server:latestdocker run -d \
--name agentspan \
-p 6767:6767 \
-e SPRING_PROFILES_ACTIVE=postgres \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://your-db-host:5432/agentspan \
-e SPRING_DATASOURCE_USERNAME=agentspan \
-e SPRING_DATASOURCE_PASSWORD=your-password \
-e OPENAI_API_KEY=sk-... \
agentspan/server:latestVerify:
curl http://localhost:6767/actuator/health
# {"status":"UP"}Best for: single VM production. Includes PostgreSQL with a persistent volume.
cd deployment/docker-compose
cp .env.example .envEdit .env — set at minimum:
# Generate and set the encryption master key
AGENTSPAN_MASTER_KEY=$(openssl rand -base64 32)
# Change the database password
POSTGRES_PASSWORD=your-strong-password
# Set at least one LLM provider key
OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
# GEMINI_API_KEY=...docker compose up -d
# Stream logs
docker compose logs -f agentspan
# Verify health
curl http://localhost:6767/actuator/healthdocker compose pull
docker compose up -ddocker compose down # stop, keep data
docker compose down -v # stop + delete database (irreversible)Data persists in the postgres_data Docker volume. Back it up before upgrading.
Best for: high availability, auto-scaling, managed cloud.
| Tool | Purpose |
|---|---|
kubectl |
Kubernetes CLI |
| ingress-nginx | Ingress controller |
| cert-manager | Automatic TLS (optional but recommended) |
| metrics-server | Required for HPA auto-scaling |
# ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.1/deploy/static/provider/cloud/deploy.yaml
# cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# metrics-server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yamlEdit deployment/k8s/agentspan/secret.yaml. Never commit this file with real values.
apiVersion: v1
kind: Secret
metadata:
name: agentspan-secrets
namespace: agentspan
type: Opaque
stringData:
AGENTSPAN_MASTER_KEY: "..." # openssl rand -base64 32
POSTGRES_PASSWORD: "your-strong-password" # required
POSTGRES_USER: "agentspan"
OPENAI_API_KEY: "sk-..." # at least one LLM key
# ANTHROPIC_API_KEY: "sk-ant-..."
# GEMINI_API_KEY: "..."Tip: Use Sealed Secrets, External Secrets Operator, or your cloud secrets manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) instead of committing plaintext.
Non-sensitive settings go in deployment/k8s/agentspan/configmap.yaml (safe to commit):
data:
SPRING_PROFILES_ACTIVE: "postgres"
POSTGRES_HOST: "agentspan-postgres"
POSTGRES_DB: "agentspan"
JAVA_OPTS: "-Xms512m -Xmx1536m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
LOGGING_LEVEL_ROOT: "WARN"
LOGGING_LEVEL_DEV_AGENTSPAN: "INFO"Edit deployment/k8s/agentspan/ingress.yaml, replace agentspan.example.com with your domain:
rules:
- host: agentspan.yourdomain.com# Deploy everything in order
kubectl apply -f deployment/k8s/agentspan/namespace.yaml
kubectl apply -f deployment/k8s/agentspan/configmap.yaml
kubectl apply -f deployment/k8s/agentspan/secret.yaml
kubectl apply -f deployment/k8s/agentspan/postgres.yaml
kubectl rollout status statefulset/agentspan-postgres -n agentspan
kubectl apply -f deployment/k8s/agentspan/server.yaml
kubectl rollout status deployment/agentspan-server -n agentspan
kubectl apply -f deployment/k8s/agentspan/ingress.yaml
kubectl apply -f deployment/k8s/agentspan/hpa.yamlOr use the deploy script:
./deployment/k8s/deploy.sh
./deployment/k8s/deploy.sh --image registry.example.com/agentspan/server:v1.2.0
./deployment/k8s/deploy.sh --skip-build --context my-cluster-context# Get the load balancer IP
kubectl get ingress -n agentspan
# Create an A record:
# agentspan.yourdomain.com → <EXTERNAL-IP># Pod status
kubectl get pods -n agentspan
# Server logs
kubectl logs -f deployment/agentspan-server -n agentspan
# Restart (pick up new image)
kubectl rollout restart deployment/agentspan-server -n agentspan
# Local port-forward (bypass ingress)
kubectl port-forward svc/agentspan-server 6767:6767 -n agentspan
# Manual scale
kubectl scale deployment/agentspan-server --replicas=5 -n agentspan
# Tear down
kubectl delete namespace agentspanThe default HPA scales between 3 and 10 replicas based on CPU (70%) and memory (80%). All replicas share the same PostgreSQL database — no additional coordination needed.
To change limits, edit deployment/k8s/agentspan/hpa.yaml or set Helm values (see section 8).
Best for: GitOps, templated multi-environment deployments.
helm install agentspan ./deployment/helm/agentspan \
--namespace agentspan \
--create-namespace \
--set secrets.masterKey=$(openssl rand -base64 32) \
--set secrets.postgresPassword=your-strong-password \
--set secrets.openaiApiKey=sk-...# Use an external (managed) database instead of the bundled one
helm install agentspan ./deployment/helm/agentspan \
--namespace agentspan \
--create-namespace \
--set postgres.enabled=false \
--set externalDatabase.enabled=true \
--set externalDatabase.host=your-rds-endpoint.rds.amazonaws.com \
--set externalDatabase.database=agentspan \
--set secrets.postgresPassword=your-password \
--set secrets.openaiApiKey=sk-...
# Enable ingress + TLS
helm upgrade agentspan ./deployment/helm/agentspan \
--set ingress.enabled=true \
--set ingress.hosts[0].host=agentspan.yourdomain.com \
--set ingress.tls[0].hosts[0]=agentspan.yourdomain.com \
--set ingress.tls[0].secretName=agentspan-tls
# Change image tag for a version upgrade
helm upgrade agentspan ./deployment/helm/agentspan \
--set image.tag=0.0.15helm uninstall agentspan --namespace agentspanSee deployment/helm/agentspan/values.yaml for all configurable values.
For production, replace the bundled PostgreSQL StatefulSet with a managed service for automated backups, failover, and point-in-time recovery.
| Cloud | Service |
|---|---|
| AWS | RDS for PostgreSQL |
| GCP | Cloud SQL for PostgreSQL |
| Azure | Azure Database for PostgreSQL |
| Self-hosted | CloudNativePG |
POSTGRES_HOST=your-rds-endpoint.rds.amazonaws.com
POSTGRES_USER=agentspan
POSTGRES_PASSWORD=your-password
POSTGRES_DB=agentspandeployment/k8s/agentspan/configmap.yaml:
POSTGRES_HOST: "your-rds-endpoint.rds.amazonaws.com"
POSTGRES_DB: "agentspan"deployment/k8s/agentspan/secret.yaml:
POSTGRES_USER: "agentspan"
POSTGRES_PASSWORD: "your-password"Also remove the postgres.yaml manifest — the bundled DB is no longer needed.
helm upgrade agentspan ./deployment/helm/agentspan \
--set postgres.enabled=false \
--set externalDatabase.enabled=true \
--set externalDatabase.host=your-rds-endpoint.rds.amazonaws.com \
--set secrets.postgresUser=agentspan \
--set secrets.postgresPassword=your-passwordWhen using the bundled PostgreSQL StatefulSet on cloud K8s, set the right storage class for your provider:
| Cloud | storageClassName |
|---|---|
| AWS EKS | gp3 |
| GCP GKE | (default — no change needed) |
| Azure AKS | managed-premium |
Edit deployment/k8s/agentspan/postgres.yaml or set postgres.persistence.storageClass in Helm values.
Step 1 — Create a ClusterIssuer:
# deployment/k8s/agentspan/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@yourdomain.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginxkubectl apply -f deployment/k8s/agentspan/cluster-issuer.yamlStep 2 — Uncomment TLS in deployment/k8s/agentspan/ingress.yaml:
metadata:
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- agentspan.yourdomain.com
secretName: agentspan-tlsserver {
listen 443 ssl;
server_name agentspan.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/agentspan.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/agentspan.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:6767;
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 / streaming support
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
}
}curl http://localhost:6767/actuator/health # overall
curl http://localhost:6767/actuator/health/liveness # liveness probe
curl http://localhost:6767/actuator/health/readiness # readiness probeMetrics are exposed at /actuator/prometheus:
curl http://localhost:6767/actuator/prometheusPrometheus scrape config:
scrape_configs:
- job_name: agentspan
static_configs:
- targets: ['agentspan:6767']
metrics_path: /actuator/prometheusKey metrics:
| Metric | Description |
|---|---|
agentspan_agent_executions_total |
Total agent runs |
agentspan_agent_duration_seconds |
Execution duration histogram |
agentspan_tool_calls_total |
Tool invocations |
agentspan_guardrail_failures_total |
Guardrail failures |
jvm_memory_used_bytes |
JVM heap usage |
# Verbose (troubleshooting)
LOGGING_LEVEL_ROOT=INFO
LOGGING_LEVEL_DEV_AGENTSPAN=DEBUG
# Default (production)
LOGGING_LEVEL_ROOT=WARN
LOGGING_LEVEL_DEV_AGENTSPAN=INFODistributed tracing is disabled by default. Enable via env vars:
MANAGEMENT_OTLP_METRICS_EXPORT_ENABLED=true
MANAGEMENT_OTLP_METRICS_EXPORT_URL=http://your-otel-collector:4318/v1/metrics# Backup
docker exec agentspan-postgres-1 pg_dump -U agentspan agentspan > backup_$(date +%Y%m%d).sql
# Restore
docker exec -i agentspan-postgres-1 psql -U agentspan agentspan < backup_20260101.sql# Backup
kubectl exec -n agentspan statefulset/agentspan-postgres -- \
pg_dump -U agentspan agentspan > backup_$(date +%Y%m%d).sql
# Restore
kubectl exec -i -n agentspan statefulset/agentspan-postgres -- \
psql -U agentspan agentspan < backup_20260101.sql# Backup
cp agent-runtime.db agent-runtime.db.$(date +%Y%m%d).bak
# Restore
cp agent-runtime.db.20260101.bak agent-runtime.dbRun a backup before every upgrade.
-
AGENTSPAN_MASTER_KEYset to a generated key (openssl rand -base64 32) -
POSTGRES_PASSWORDchanged fromchangemeto a strong password - TLS / HTTPS enabled
- K8s secrets managed via Sealed Secrets or a cloud secrets manager
-
secret.yamladded to.gitignore— no plaintext secrets in git - LLM API keys rotated if ever committed or shared
- PostgreSQL in use (not SQLite)
- Managed database service used (RDS, Cloud SQL) — not a stateful pod
- Automated backups scheduled
- Restore tested before go-live
- 2+ replicas running (
kubectl get pods -n agentspan) - HPA configured (
kubectl get hpa -n agentspan) - PodDisruptionBudget in place (
minAvailable: 2) - Liveness and readiness probes responding
- Prometheus scraping
/actuator/prometheus - Log aggregation set up (CloudWatch, Datadog, Loki, etc.)
- Alerting on error rate and latency
- Upgrade runbook written: pull →
docker compose up -dorkubectl rollout restart - Backup → restore → verify tested end-to-end
All env vars. Bold = sensitive — set in K8s Secret or .env, never in ConfigMap or git.
| Variable | Default | Description |
|---|---|---|
AGENTSPAN_MASTER_KEY |
(auto-generated) | AES-256-GCM key for credential encryption (base64-encoded 32 bytes). Generate with openssl rand -base64 32. Required in production — auto-generated keys are ephemeral in containers. |
| Variable | Default | Description |
|---|---|---|
SERVER_PORT |
6767 |
HTTP listen port |
SPRING_PROFILES_ACTIVE |
default (SQLite) |
Set to postgres for PostgreSQL |
| Variable | Default | Description |
|---|---|---|
SPRING_DATASOURCE_URL |
jdbc:sqlite:agent-runtime.db |
SQLite file path |
| Variable | Default | Description |
|---|---|---|
SPRING_DATASOURCE_URL |
jdbc:postgresql://localhost:5432/agentspan |
Full JDBC URL |
SPRING_DATASOURCE_USERNAME |
postgres |
DB user |
SPRING_DATASOURCE_PASSWORD |
postgres |
DB password |
SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE |
8 |
Connection pool size |
| Variable | Default | Description |
|---|---|---|
JAVA_TOOL_OPTIONS |
-Xms512m -Xmx1536m -XX:+UseG1GC |
JVM heap + GC settings |
Guidelines:
- Dev:
-Xms256m -Xmx512m - Prod small:
-Xms512m -Xmx1536m - Prod large:
-Xms1g -Xmx3g - Never set
-Xmxabove 75% of the container memory limit
Set at least one API key. The server auto-detects and enables providers when their key is present.
| Variable | Provider |
|---|---|
OPENAI_API_KEY |
OpenAI |
ANTHROPIC_API_KEY |
Anthropic |
GEMINI_API_KEY |
Google Gemini |
AZURE_OPENAI_API_KEY |
Azure OpenAI |
AZURE_OPENAI_ENDPOINT |
Azure OpenAI |
AZURE_OPENAI_DEPLOYMENT |
Azure OpenAI (model deployment name) |
AWS_ACCESS_KEY_ID |
AWS Bedrock |
AWS_SECRET_ACCESS_KEY |
AWS Bedrock |
AWS_REGION |
AWS Bedrock (default: us-east-1) |
GOOGLE_CLOUD_PROJECT |
Vertex AI |
GOOGLE_CLOUD_LOCATION |
Vertex AI (default: us-central1) |
MISTRAL_API_KEY |
Mistral |
COHERE_API_KEY |
Cohere |
XAI_API_KEY |
Grok / xAI |
PERPLEXITY_API_KEY |
Perplexity |
OLLAMA_BASE_URL |
Ollama (e.g. http://localhost:11434) |
| Variable | Default | Description |
|---|---|---|
LOGGING_LEVEL_ROOT |
WARN |
Root log level |
LOGGING_LEVEL_DEV_AGENTSPAN |
INFO |
App log level |
MANAGEMENT_OTLP_METRICS_EXPORT_ENABLED |
false |
Enable OpenTelemetry export |
MANAGEMENT_OTLP_METRICS_EXPORT_URL |
(unset) | OTLP collector endpoint |
Bold variables are sensitive — store them in K8s Secrets (not ConfigMaps) and in
.env(not committed to git).