Skip to main content

Overview

Cadence provides official Docker images for easy deployment. This guide covers running Cadence with Docker for both development and production environments.

Docker Images

Cadence maintains several Docker images on Docker Hub:
  • ubercadence/server: Standard Cadence server
  • ubercadence/server:master-auto-setup: Development image with automatic schema setup
  • ubercadence/cli: Cadence CLI tools
  • ubercadence/web: Web UI for workflow visualization
  • ubercadence/canary: Canary testing tool
  • ubercadence/bench: Load testing tool
The master-auto-setup image automatically sets up database schemas on startup. Use this for development only. For production, use the standard server image and manage schemas separately.

Quick Start with docker-compose

The fastest way to run Cadence locally:
1

Clone the repository

git clone https://github.com/cadence-workflow/cadence.git
cd cadence/docker
2

Start Cadence with docker-compose

docker compose up
This starts:
  • Cadence server (all services)
  • Cassandra database
  • Cadence Web UI
  • Prometheus for metrics
  • Grafana for visualization
3

Access the services

docker-compose Configurations

Cadence provides multiple compose files for different configurations:
services:
  cassandra:
    image: cassandra:4.1.1
    ports:
      - "9042:9042"
    environment:
      - "MAX_HEAP_SIZE=256M"
      - "HEAP_NEWSIZE=128M"
  
  cadence:
    image: ubercadence/server:master-auto-setup
    ports:
      - "7933:7933"  # TChannel
      - "7833:7833"  # gRPC
      - "8000:8000"  # Prometheus metrics
    environment:
      - "CASSANDRA_SEEDS=cassandra"
      - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000"
      - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml"
    depends_on:
      - cassandra
  
  cadence-web:
    image: ubercadence/web:latest
    ports:
      - "8088:8088"
    environment:
      - "CADENCE_GRPC_PEERS=cadence:7833"
    depends_on:
      - cadence

Available Compose Files

FileDescription
docker-compose.ymlCassandra + basic visibility
docker-compose-mysql.ymlMySQL persistence
docker-compose-postgres.ymlPostgreSQL persistence
docker-compose-es.ymlCassandra + ElasticSearch 6.x
docker-compose-es-v7.ymlCassandra + ElasticSearch 7.x
docker-compose-opensearch.ymlCassandra + OpenSearch
docker-compose-multiclusters.ymlMulti-cluster setup
docker-compose-statsd.ymlStatsD metrics instead of Prometheus

MySQL Example

Complete docker-compose configuration for MySQL:
docker-compose-mysql.yml
services:
  mysql:
    platform: linux/amd64
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      - "MYSQL_ROOT_PASSWORD=root"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  cadence:
    image: ubercadence/server:master-auto-setup
    ports:
      - "8000:8000"
      - "8001:8001"
      - "8002:8002"
      - "8003:8003"
      - "7933:7933"
      - "7934:7934"
      - "7935:7935"
      - "7939:7939"
      - "7833:7833"
    environment:
      - "DB=mysql"
      - "MYSQL_USER=root"
      - "MYSQL_PWD=root"
      - "MYSQL_SEEDS=mysql"
      - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000"
      - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001"
      - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002"
      - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003"
      - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml"
    depends_on:
      mysql:
        condition: service_healthy

  cadence-web:
    image: ubercadence/web:latest
    environment:
      - "CADENCE_GRPC_PEERS=cadence:7833"
    ports:
      - "8088:8088"
    depends_on:
      - cadence
Start with:
docker compose -f docker-compose-mysql.yml up

PostgreSQL Example

Complete docker-compose configuration for PostgreSQL:
docker-compose-postgres.yml
services:
  postgres:
    image: postgres:17.4
    environment:
      POSTGRES_USER: cadence
      POSTGRES_PASSWORD: cadence
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U cadence"]
      interval: 10s
      timeout: 5s
      retries: 5

  cadence:
    image: ubercadence/server:master-auto-setup
    ports:
      - "8000:8000"
      - "8001:8001" 
      - "8002:8002"
      - "8003:8003"
      - "7933:7933"
      - "7834:7834"
      - "7935:7935"
      - "7939:7939"
      - "7833:7833"
    environment:
      - "DB=postgres"
      - "DB_PORT=5432"
      - "POSTGRES_USER=cadence"
      - "POSTGRES_PWD=cadence"
      - "POSTGRES_SEEDS=postgres"
      - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000"
      - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001"
      - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002"
      - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003"
    depends_on:
      postgres:
        condition: service_healthy

  cadence-web:
    image: ubercadence/web:latest
    environment:
      - "CADENCE_GRPC_PEERS=cadence:7833"
    ports:
      - "8088:8088"
    depends_on:
      - cadence

Production Docker Deployment

For production, use the standard server image and manage schema separately:
docker-compose-production.yml
services:
  cadence-frontend:
    image: ubercadence/server:1.2.7  # Use specific version tag
    command:
      - "/start-cadence.sh"
    environment:
      - "CASSANDRA_SEEDS=cassandra-prod.example.com"
      - "KEYSPACE=cadence_prod"
      - "VISIBILITY_KEYSPACE=cadence_visibility_prod"
      - "RINGPOP_SEEDS=cadence-frontend-0:7933,cadence-frontend-1:7933"
      - "SERVICES=frontend"
      - "LOG_LEVEL=info"
      - "NUM_HISTORY_SHARDS=4096"
      - "STATSD_ENDPOINT=statsd.example.com:8125"
    ports:
      - "7933:7933"
      - "7833:7833"
    restart: unless-stopped
    volumes:
      - ./config:/etc/cadence/config

  cadence-history:
    image: ubercadence/server:1.2.7
    command:
      - "/start-cadence.sh"
    environment:
      - "CASSANDRA_SEEDS=cassandra-prod.example.com"
      - "RINGPOP_SEEDS=cadence-history-0:7934,cadence-history-1:7934"
      - "SERVICES=history"
      - "LOG_LEVEL=info"
      - "NUM_HISTORY_SHARDS=4096"
    ports:
      - "7934:7934"
      - "7834:7834"
    restart: unless-stopped

  cadence-matching:
    image: ubercadence/server:1.2.7
    command:
      - "/start-cadence.sh"
    environment:
      - "CASSANDRA_SEEDS=cassandra-prod.example.com"
      - "RINGPOP_SEEDS=cadence-matching-0:7935,cadence-matching-1:7935"
      - "SERVICES=matching"
      - "LOG_LEVEL=info"
    ports:
      - "7935:7935"
    restart: unless-stopped

  cadence-worker:
    image: ubercadence/server:1.2.7
    command:
      - "/start-cadence.sh"
    environment:
      - "CASSANDRA_SEEDS=cassandra-prod.example.com"
      - "SERVICES=worker"
      - "LOG_LEVEL=info"
    ports:
      - "7939:7939"
    restart: unless-stopped

Environment Variables Reference

Key environment variables for Docker deployment:
VariableDescriptionDefault
DBDatabase type: cassandra, mysql, postgrescassandra
CASSANDRA_SEEDSCassandra seed nodes (CSV)-
MYSQL_SEEDSMySQL host-
POSTGRES_SEEDSPostgreSQL host-
KEYSPACE / DBNAMEDatabase/keyspace namecadence
VISIBILITY_KEYSPACEVisibility store namecadence_visibility
NUM_HISTORY_SHARDSNumber of history shards4
RINGPOP_SEEDSCluster membership seeds (CSV)-
SERVICESServices to run (CSV)history,matching,frontend,worker
LOG_LEVELLogging levelinfo
STATSD_ENDPOINTStatsD server-
PROMETHEUS_ENDPOINT_0Prometheus metrics address-
ENABLE_ESEnable ElasticSearchfalse
ES_SEEDSElasticSearch seeds-
DYNAMIC_CONFIG_FILE_PATHDynamic config file path/etc/cadence/config/dynamicconfig/development.yaml

Volumes and Persistence

Mount configuration files and data directories:
services:
  cadence:
    volumes:
      # Custom configuration
      - ./config:/etc/cadence/config:ro
      
      # Dynamic config (read-write for updates)
      - ./dynamicconfig:/etc/cadence/config/dynamicconfig
      
      # TLS certificates
      - ./certs:/etc/cadence/certs:ro
      
      # File-based archival
      - ./archival:/tmp/cadence_archival

Building Custom Images

Build Standard Server Image

git clone https://github.com/cadence-workflow/cadence.git
cd cadence

# Build server image
docker build . -t mycorp/cadence-server:v1.0.0 \
  --build-arg TARGET=server

# Build auto-setup image for development
docker build . -t mycorp/cadence-server:v1.0.0-auto-setup \
  --build-arg TARGET=auto-setup

# Build CLI image
docker build . -t mycorp/cadence-cli:v1.0.0 \
  --build-arg TARGET=cli

Multi-stage Dockerfile Structure

The Cadence Dockerfile uses multi-stage builds:
Dockerfile
ARG TARGET=server

# Stage 1: Build binaries
FROM golang:1.23.4-alpine3.21 AS builder
WORKDIR /cadence
COPY . .
RUN make cadence-server cadence-cassandra-tool cadence-sql-tool

# Stage 2: Base server image  
FROM alpine:3.18 AS cadence-server
COPY --from=builder /cadence/cadence-server /usr/local/bin
COPY --from=builder /cadence/schema /etc/cadence/schema
COPY docker/start-cadence.sh /start-cadence.sh
EXPOSE 7933 7934 7935 7939
CMD ["/start-cadence.sh"]

# Stage 3: Auto-setup image (includes schema tools)
FROM cadence-server AS cadence-auto-setup
RUN apk add --no-cache mysql-client
RUN pip3 install cqlsh
COPY docker/start.sh /start.sh
CMD ["/start.sh"]

# Final stage selected by TARGET arg
FROM cadence-${TARGET}

Testing and Development

Running Locally with Custom Code

Test custom server code with Docker dependencies:
1

Start dependencies only

docker compose up cassandra prometheus grafana
2

Build and run local server

make bins
./cadence-server start

Running Tests with Docker

Use docker-compose for integration tests:
# Start test environment
cd docker/github_actions
docker compose -f docker-compose-local.yml up

# Run tests
make test

Monitoring and Observability

Prometheus Metrics

Cadence exposes Prometheus metrics on each service:
services:
  cadence:
    environment:
      - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000"  # Frontend
      - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001"  # Matching
      - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002"  # History
      - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003"  # Worker
Access metrics:
curl http://localhost:8000/metrics

Grafana Dashboards

The docker-compose setup includes pre-configured Grafana dashboards:
services:
  grafana:
    image: grafana/grafana
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/provisioning/dashboards:/var/lib/grafana/dashboards
    ports:
      - '3000:3000'
Included dashboards:
  • Cadence Server Overview
  • Frontend Service Metrics
  • History Service Metrics
  • Matching Service Metrics
  • Persistence Metrics

Troubleshooting

Container Won’t Start

# Check container logs
docker compose logs cadence

# Check if database is ready
docker compose logs cassandra

# Verify network connectivity
docker compose exec cadence ping cassandra

Database Connection Issues

# Test Cassandra connection
docker compose exec cadence cqlsh cassandra -e "describe keyspaces"

# Test MySQL connection
docker compose exec cadence mysql -h mysql -u root -proot -e "show databases"

# Test PostgreSQL connection
docker compose exec cadence psql -h postgres -U cadence -c "\l"

Schema Not Created

If using auto-setup image and schema isn’t created:
# Check startup logs for schema setup
docker compose logs cadence | grep -i schema

# Manually run schema setup
docker compose exec cadence bash
cadence-cassandra-tool --ep cassandra create --keyspace cadence

Performance Issues

  1. Increase container resources:
    services:
      cadence:
        deploy:
          resources:
            limits:
              cpus: '2'
              memory: 4G
    
  2. Tune database container:
    cassandra:
      environment:
        - "MAX_HEAP_SIZE=2G"
        - "HEAP_NEWSIZE=512M"
    

Production Best Practices

1

Use specific version tags

Never use latest or master tags in production:
image: ubercadence/server:1.2.7  # Good
image: ubercadence/server:latest # Bad
2

Separate services

Run each service type in separate containers for better scaling:
cadence-frontend:
  environment:
    - "SERVICES=frontend"
cadence-history:
  environment:
    - "SERVICES=history"
3

Use external databases

Don’t run production databases in containers. Use managed database services.
4

Configure health checks

cadence:
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
    interval: 30s
    timeout: 10s
    retries: 3
5

Set resource limits

deploy:
  resources:
    limits:
      cpus: '2'
      memory: 4G
    reservations:
      cpus: '1'
      memory: 2G

Next Steps

Kubernetes Deployment

Deploy Cadence on Kubernetes

Configuration

Advanced configuration options

Build docs developers (and LLMs) love