Skip to main content

Overview

Cadence server configuration consists of two layers:
  1. Static Configuration: YAML files that define core server settings (loaded at startup)
  2. Dynamic Configuration: Runtime settings that can be updated without restart
This guide covers both configuration types and common configuration patterns.

Static Configuration

Static configuration is defined in YAML files located in the config/ directory. The configuration is organized into several sections.

Configuration File Structure

log:              # Logging configuration
persistence:      # Database configuration
ringpop:          # Cluster membership
services:         # Service-specific settings
clusterGroupMetadata:  # Multi-cluster setup
archival:         # History archival
domainDefaults:   # Default domain settings
dynamicconfig:    # Dynamic config file path
blobstore:        # Blob storage settings
authorization:    # Auth configuration

Logging Configuration

Configure server logging output:
config/production.yaml
log:
  stdout: true         # Output to stdout
  level: "info"        # Log level: debug, info, warn, error
  levelKey: "level"    # JSON field name for log level
For production deployments, use info or warn level. The debug level generates significant log volume and should only be used for troubleshooting.

Persistence Configuration

Cadence requires two types of persistence stores:
  • defaultStore: Core workflow execution state
  • visibilityStore: Workflow search and listing

Cassandra Configuration

config/cassandra.yaml
persistence:
  defaultStore: cass-default
  visibilityStore: cass-visibility
  numHistoryShards: 1024
  datastores:
    cass-default:
      nosql:
        pluginName: "cassandra"
        hosts: "10.0.1.10,10.0.1.11,10.0.1.12"
        keyspace: "cadence"
        user: "cadence_user"
        password: "${CASSANDRA_PASSWORD}"
        datacenter: "us-east-1a"
        consistency: LOCAL_QUORUM
        serialConsistency: LOCAL_SERIAL
        connectTimeout: 2s
        timeout: 5s
        maxConns: 2
    cass-visibility:
      nosql:
        pluginName: "cassandra"
        hosts: "10.0.1.10,10.0.1.11,10.0.1.12"
        keyspace: "cadence_visibility"
        user: "cadence_user"
        password: "${CASSANDRA_PASSWORD}"

MySQL Configuration

config/mysql.yaml
persistence:
  defaultStore: mysql-default
  visibilityStore: mysql-visibility
  numHistoryShards: 1024
  datastores:
    mysql-default:
      sql:
        pluginName: "mysql"
        databaseName: "cadence"
        connectAddr: "mysql.example.com:3306"
        connectProtocol: "tcp"
        user: "cadence"
        password: "${MYSQL_PASSWORD}"
        maxConns: 20
        maxIdleConns: 20
        maxConnLifetime: "1h"
        connectAttributes:
          tx_isolation: "READ-COMMITTED"  # Required for MySQL 5.6
    mysql-visibility:
      sql:
        pluginName: "mysql"
        databaseName: "cadence_visibility"
        connectAddr: "mysql.example.com:3306"
        connectProtocol: "tcp"
        user: "cadence"
        password: "${MYSQL_PASSWORD}"
        maxConns: 2
        maxIdleConns: 2

PostgreSQL Configuration

config/postgres.yaml
persistence:
  defaultStore: postgres-default
  visibilityStore: postgres-visibility
  numHistoryShards: 1024
  datastores:
    postgres-default:
      sql:
        pluginName: "postgres"
        databaseName: "cadence"
        connectAddr: "postgres.example.com:5432"
        connectProtocol: "tcp"
        user: "postgres"
        password: "${POSTGRES_PASSWORD}"
        maxConns: 20
        maxIdleConns: 20
        maxConnLifetime: "1h"
    postgres-visibility:
      sql:
        pluginName: "postgres"
        databaseName: "cadence_visibility"
        connectAddr: "postgres.example.com:5432"
        connectProtocol: "tcp"
        user: "postgres"
        password: "${POSTGRES_PASSWORD}"
        maxConns: 2
        maxIdleConns: 2
numHistoryShards cannot be changed after initial cluster setup. Plan for your maximum expected cluster size. Typical production values:
  • Small deployments: 512-1024 shards
  • Medium deployments: 2048-4096 shards
  • Large deployments: 8192+ shards

Multiple Database Sharding

For very large scale deployments, Cadence supports sharding across multiple SQL databases:
config/sharded-mysql.yaml
persistence:
  defaultStore: mysql-sharded
  datastores:
    mysql-sharded:
      sql:
        pluginName: "mysql"
        connectProtocol: "tcp"
        maxConnLifetime: "1h"
        useMultipleDatabases: true
        nShards: 4
        multipleDatabasesConfig:
          - user: "cadence"
            password: "${MYSQL_PASSWORD}"
            connectAddr: "mysql-shard0.example.com:3306"
            databaseName: "cadence0"
          - user: "cadence"
            password: "${MYSQL_PASSWORD}"
            connectAddr: "mysql-shard1.example.com:3306"
            databaseName: "cadence1"
          - user: "cadence"
            password: "${MYSQL_PASSWORD}"
            connectAddr: "mysql-shard2.example.com:3306"
            databaseName: "cadence2"
          - user: "cadence"
            password: "${MYSQL_PASSWORD}"
            connectAddr: "mysql-shard3.example.com:3306"
            databaseName: "cadence3"

Service Configuration

Configure individual Cadence services:
config/production.yaml
services:
  frontend:
    rpc:
      port: 7933              # TChannel port
      grpcPort: 7833          # gRPC port
      bindOnLocalHost: false  # Bind to all interfaces
      grpcMaxMsgSize: 33554432
    metrics:
      prometheus:
        timerType: "histogram"
        listenAddress: "0.0.0.0:8000"
    pprof:
      port: 7936

  matching:
    rpc:
      port: 7935
      grpcPort: 7835
      bindOnLocalHost: false
    metrics:
      prometheus:
        timerType: "histogram"
        listenAddress: "0.0.0.0:8001"

  history:
    rpc:
      port: 7934
      grpcPort: 7834
      bindOnLocalHost: false
    metrics:
      prometheus:
        timerType: "histogram"
        listenAddress: "0.0.0.0:8002"

  worker:
    rpc:
      port: 7939
      bindOnLocalHost: false
    metrics:
      prometheus:
        timerType: "histogram"
        listenAddress: "0.0.0.0:8003"

Metrics Configuration

services:
  frontend:
    metrics:
      prometheus:
        timerType: "histogram"  # or "summary"
        listenAddress: "0.0.0.0:8000"

Ringpop Configuration

Ringpop provides service discovery and cluster membership:
config/production.yaml
ringpop:
  name: cadence
  bootstrapMode: hosts     # or "dns"
  bootstrapHosts:
    - "cadence-frontend-0.cadence.svc.cluster.local:7933"
    - "cadence-frontend-1.cadence.svc.cluster.local:7933"
    - "cadence-history-0.cadence.svc.cluster.local:7934"
    - "cadence-matching-0.cadence.svc.cluster.local:7935"
  maxJoinDuration: 30s
For Kubernetes deployments, use headless services with DNS-based discovery.

Archival Configuration

Enable workflow history and visibility archival:
config/archival.yaml
archival:
  history:
    status: "enabled"
    enableRead: true
    provider:
      filestore:
        fileMode: "0666"
        dirMode: "0766"
      gstorage:
        credentialsPath: "/etc/cadence/gcp-credentials.json"
  visibility:
    status: "enabled"
    enableRead: true
    provider:
      filestore:
        fileMode: "0666"
        dirMode: "0766"

domainDefaults:
  archival:
    history:
      status: "enabled"
      URI: "gs://cadence-archive-bucket/history"
    visibility:
      status: "enabled"
      URI: "gs://cadence-archive-bucket/visibility"

Advanced Visibility (ElasticSearch)

Configure ElasticSearch for enhanced workflow search:
config/elasticsearch.yaml
persistence:
  defaultStore: cass-default
  visibilityStore: cass-visibility
  advancedVisibilityStore: es-visibility
  datastores:
    es-visibility:
      elasticsearch:
        version: "v7"  # or "v6", "opensearch"
        url:
          scheme: "https"
          host: "elasticsearch.example.com:9200"
        username: "elastic"
        password: "${ES_PASSWORD}"
        indices:
          visibility: "cadence-visibility-prod"

Multi-cluster Configuration

For cross-datacenter replication:
config/multi-cluster.yaml
clusterGroupMetadata:
  failoverVersionIncrement: 10
  primaryClusterName: "cluster0"
  currentClusterName: "cluster0"
  clusterGroup:
    cluster0:
      enabled: true
      initialFailoverVersion: 0
      rpcAddress: "cadence-frontend.us-east-1.example.com:7833"
      rpcTransport: "grpc"
    cluster1:
      enabled: true
      initialFailoverVersion: 1
      rpcAddress: "cadence-frontend.eu-west-1.example.com:7833"
      rpcTransport: "grpc"

Dynamic Configuration

Dynamic configuration allows runtime updates without service restart. Settings are stored in YAML files that are polled periodically.

Dynamic Config File Setup

config/production.yaml
dynamicconfig:
  client: filebased
  filebased:
    filepath: "config/dynamicconfig/production.yaml"
    pollInterval: "60s"  # Check for updates every 60s

Dynamic Config Format

Dynamic config uses a constraint-based system:
config/dynamicconfig/production.yaml
# Enable client version checking
frontend.enableClientVersionCheck:
  - value: true
    constraints: {}

# Set minimum retention days
system.minRetentionDays:
  - value: 7
    constraints: {}

# Per-domain configuration
history.EnableConsistentQueryByDomain:
  - value: true
    constraints:
      domainName: "critical-workflows"
  - value: false
    constraints:
      domainName: "experimental-workflows"

# Task list specific settings
matching.taskListLoadBalancerStrategy:
  - value: "round-robin"
    constraints:
      domainName: "high-volume-domain"
      taskListName: "fast-tasks"

Common Dynamic Config Settings

frontend.rps:
  - value: 2400  # Requests per second
    constraints: {}

frontend.domainRPS:
  - value: 1200
    constraints:
      domainName: "high-priority-domain"

Constraint Types

Dynamic config supports three constraint types:
  1. domainName: Apply to specific domain (string)
  2. taskListName: Apply to specific task list (string)
  3. taskType: Apply to task type (int: 0=Decision, 1=Activity)
# Example with all constraint types
setting.example:
  - value: "default-value"
    constraints: {}
  - value: "domain-specific"
    constraints:
      domainName: "my-domain"
  - value: "tasklist-specific"
    constraints:
      domainName: "my-domain"
      taskListName: "my-tasklist"
  - value: "activity-specific"
    constraints:
      domainName: "my-domain"
      taskListName: "my-tasklist"
      taskType: 1  # Activities only

Environment Variables

Cadence supports environment variable substitution in configuration files:
persistence:
  datastores:
    default:
      sql:
        user: "${DB_USER}"
        password: "${DB_PASSWORD}"
        connectAddr: "${DB_HOST}:${DB_PORT}"
Set variables before starting the server:
export DB_USER=cadence
export DB_PASSWORD=secret
export DB_HOST=postgres.example.com
export DB_PORT=5432

cadence-server --root config --env production start

Configuration Best Practices

1

Use environment-specific configs

Maintain separate configuration files for each environment:
config/
  development.yaml
  staging.yaml
  production.yaml
  dynamicconfig/
    development.yaml
    staging.yaml
    production.yaml
2

Version control configurations

Store all configuration files in version control (except secrets).
3

Use secret management

Never commit passwords or keys. Use environment variables or secret management systems:
password: "${DB_PASSWORD}"  # From env or secret manager
4

Start with conservative settings

Begin with lower connection pools and rate limits, then tune based on metrics.
5

Test dynamic config changes

Test dynamic configuration changes in non-production first.

Configuration Validation

Validate your configuration before deploying:
# Start server in dry-run mode (validates config)
cadence-server --root config --env production validate

# Check for syntax errors
yamlint config/production.yaml

Next Steps

Database Setup

Configure and initialize your persistence layer

Docker Deployment

Deploy with Docker and docker-compose

Build docs developers (and LLMs) love