deployment
workflowdev

Deployment

Two deployment paths: Docker Compose for local development, Helm chart for Kubernetes.

Docker Compose

The simplest way to run everything locally.

git clone https://github.com/excalibase/excalibase-workflow.git
cd excalibase-workflow
cp .env.example .env

docker compose up -d --build

Services

ServiceImagePort
postgrespgvector/pgvector:pg165434
natsnats:2-alpine4222
dinddocker:24-dind2375
backendexcalibase-workflow-backend8080
workerexcalibase-workflow-worker
frontendexcalibase-workflow-frontend3000

Scale workers

docker compose up -d --scale worker=3

Reset everything

docker compose down -v
docker compose up -d --build

Kubernetes (Helm)

For production environments. The Helm chart deploys all services with proper health checks, RBAC, and auto-scaling.

Prerequisites

  • Kubernetes cluster (minikube, EKS, GKE, AKS, etc.)
  • Helm 3
  • kubectl

Install

helm install excalibase ./helm/excalibase-workflow \
  --namespace excalibase --create-namespace

Configure

Create a values-override.yaml:

backend:
  replicas: 2
  env:
    OPENAI_API_KEY: "sk-..."
    ANTHROPIC_API_KEY: "sk-ant-..."

worker:
  replicas: 3

postgres:
  password: "strong-password-here"
  storage:
    size: 10Gi

ingress:
  enabled: true
  host: workflow.example.com
helm install excalibase ./helm/excalibase-workflow \
  --namespace excalibase --create-namespace \
  -f values-override.yaml

What gets deployed

ResourceDescription
Deployment: backendGo API server
Deployment: workerGo task worker (scales independently)
Deployment: natsNATS JetStream message queue
StatefulSet: postgresPostgreSQL 16 with pgvector
Deployment: frontendReact UI (nginx)
ServiceAccount + Role + RoleBindingRBAC for K8s Job execution
IngressRoutes /api/* to backend, /* to frontend
SecretDatabase password, API keys

No Docker-in-Docker needed

In Kubernetes, script tasks run as native K8s Jobs — no DinD layer. The runner auto-detects the environment:

  • Local (Docker Compose): Uses Docker-in-Docker to run script containers
  • Kubernetes: Creates K8s Jobs directly via the API

This means 2 layers instead of 3:

Docker Compose:  Host → DinD Container → Task Container (3 layers)
Kubernetes:      K8s Node → Task Pod (2 layers, native)

The ServiceAccount is configured with least-privilege RBAC:

ResourceVerbsPurpose
batch/jobscreate, get, list, watch, deleteSpawn task pods
podsget, list, watchMonitor task status
pods/loggetCollect task output
configmaps, secretscreate, get, deleteTask configuration

Minikube quickstart

minikube start --cpus=4 --memory=4096 --driver=docker
minikube addons enable ingress

# Build images into minikube's Docker daemon
eval $(minikube docker-env)
docker build -t excalibase-workflow-backend:latest ./server/
docker build -t excalibase-workflow-worker:latest -f ./server/Dockerfile.worker ./server/
docker build -t excalibase-workflow-frontend:latest ./web/

# Deploy
helm install excalibase ./helm/excalibase-workflow \
  --namespace excalibase --create-namespace

# Verify
kubectl get pods -n excalibase

# Access
kubectl port-forward -n excalibase svc/excalibase-excalibase-workflow-backend 8080:8080
kubectl port-forward -n excalibase svc/excalibase-excalibase-workflow-frontend 3000:80

Environment Variables

VariableDefaultDescription
DATABASE_URLPostgreSQL connection string
NATS_URLNATS server URL
PORT8080API server port
DOCKER_HOSTunix:///var/run/docker.sockDocker daemon (local dev only)
RUNNER_MODE(auto)docker or kubernetes (auto-detects if empty)
KUBE_NAMESPACEdefaultNamespace for K8s task Jobs
WORKER_CONCURRENCY10Concurrent task workers
OPENAI_API_KEYFor AI tasks
ANTHROPIC_API_KEYFor Anthropic provider
GEMINI_API_KEYFor Gemini provider
CREDENTIAL_ENCRYPTION_KEY32-char AES key for credential encryption

CLI

A command-line tool for managing workflows from the terminal.

# Build
cd server && go build -o excalibase-cli ./cmd/cli

# Login
excalibase-cli login --server http://localhost:8080 --token <jwt>

# Manage workflows
excalibase-cli validate -f workflow.yaml
excalibase-cli apply -f workflow.yaml
excalibase-cli run <flow-id>
excalibase-cli status <execution-id>
excalibase-cli logs <execution-id>