Configuration
Review the Trama configuration surface for workers, Redis topology, queue sharding, telemetry, and deployment profiles. Start with local defaults, then move into worker scaling and Redis Cluster settings when preparing production environments.
Start here
For local development, focus on runtime enablement, Redis/Postgres connectivity, and metrics. For production, focus on worker count, Redis topology, queue behavior, telemetry, and shard ownership settings.
Core Keys
| Key | Default | Purpose |
|---|---|---|
| RUNTIME | ||
runtime.enabled | true | Run queue workers on this instance. With false the instance only serves the API (runs, callbacks, queries) and leaves execution to worker pods. |
runtime.workerCount | 32 | Executions a process runs at once. Workers are coroutines that mostly wait on HTTP, so this is a concurrency limit, not a thread count; it also bounds the concurrent calls a process makes to downstream services. Default 4 before 2.2.0. |
runtime.prefetch | workerCount | Executions claimed ahead of a free worker. A process holds at most workerCount + prefetch claims. Since 2.1.0. |
runtime.bufferSize | ignored | Deprecated since 2.1.0: claims are bounded by workerCount + prefetch. |
runtime.emptyPollDelayMillis | 50 | Delay between polls of the due-work index when nothing is due. |
runtime.fullSweepIntervalMillis | 5000 | How often claimers also visit every owned shard, as a safety net for the due-work index. Since 2.1.0. |
runtime.livenessStallMillis | 120000 | /healthz fails after this long without consumer progress. Since 2.1.0. |
runtime.maxStepsPerExecution | 25 | Maximum nodes processed per worker cycle before re-enqueue. |
runtime.store | REDIS | REDIS or POSTGRES. Since 2.1.0 execution state is always in Postgres; REDIS only adds fast callback replay protection. |
runtime.callback.baseUrl | "" | Public base URL injected as {{runtime.callback.url}} in async task requests. Required for async tasks. |
runtime.callback.hmacSecret | "" | HMAC-SHA256 secret for signing and validating callback tokens. Required for async tasks. |
runtime.callback.hmacKid | default | Key ID embedded in the token header for future key rotation. |
| REDIS | ||
redis.topology | STANDALONE | Redis client mode: STANDALONE or CLUSTER. |
redis.url | redis://localhost:6379 | Connection URL used in standalone mode. |
redis.commandTimeoutMillis | 5000 | Upper bound for any Redis command. While disconnected, commands fail immediately instead of queueing. Since 2.1.0. |
redis.pool.* | ignored | Deprecated since 2.1.0: Trama uses one multiplexed connection per process. |
redis.cluster.nodes | [] | Seed nodes used when redis.topology=CLUSTER. |
redis.queue.keyPrefix | saga:executions | Prefix for shard-aware ready and in-flight queue keys. |
redis.consumer.batchSize | 50 | Messages claimed per poll cycle. |
redis.consumer.processingTimeoutMillis | 20000 | In-flight claim lease. Workers renew the claims they are still processing every third of it, so it only bounds how long work claimed by a pod that died waits before being re-delivered. |
redis.consumer.requeueIntervalMillis | ignored | Deprecated since 2.0.0: expired claims are recovered by the regular claim pass. |
redis.sharding.virtualShardCount | 64 | Queue shards that pods divide by rendezvous allocation. Must be the same on every pod; it bounds how many pods can share the work. Lowering it is safe: work in removed shards is moved over automatically. Default 1024 before 2.3.0. |
redis.sharding.membershipKey | saga:runtime:pods | Redis ZSET tracking live worker pods. |
redis.sharding.membershipTtlMillis | 10000 | Worker membership entry TTL. |
redis.sharding.heartbeatIntervalMillis | 3000 | How often each worker refreshes its membership entry. |
redis.sharding.refreshIntervalMillis | 2000 | How often each worker recalculates shard ownership. |
redis.sharding.claimerCount | 4 | Parallel shard claimers per worker process. |
| DATABASE | ||
database.host | db | Postgres host. Postgres is always required: it is the source of truth for definitions, each execution's state and resume point, step history and join barriers. |
database.port | 5432 | Postgres port. |
database.database | saga | Postgres database name. |
database.user | saga | Postgres user. |
database.password | saga | Postgres password. |
database.pool.maxPoolSize | 10 | Maximum JDBC connection pool size. |
database.pool.minIdle | 1 | Minimum idle JDBC connections. |
database.pool.definitionCacheMaxSize | 1000 | Stored definitions cached per pod. |
database.pool.definitionCacheTtlMillis | 5000 | How long a pod may keep serving a cached definition (e.g. one deleted through another pod). 0 disables the cache. |
| HTTP CLIENT | ||
http.connectTimeoutMillis | 10000 | Outbound HTTP connect timeout for workflow task requests. |
http.requestTimeoutMillis | 30000 | Total outbound HTTP request timeout. |
http.socketTimeoutMillis | 30000 | Outbound HTTP socket read timeout. |
| RATE LIMIT | ||
rateLimit.enabled | true | Per-workflow breaker: after repeated worker failures (exceptions while processing), delay that workflow's executions. Executions that end FAILED by their own logic don't count (since 2.1.0). Fails open if Redis is unavailable. |
rateLimit.maxFailures | 5 | Worker failures within the window before the workflow is paused. |
rateLimit.windowMillis | 60000 | Sliding window duration for failure counting. |
rateLimit.blockMillis | 60000 | How long a blocked execution is held before retry is allowed. |
rateLimit.keyPrefix | saga:rate | Redis key prefix for rate-limit counters. |
| RECOVERY | ||
reconciler.enabled | true | Re-send running or sleeping executions that stopped advancing (e.g. their Redis queue item was lost), from their Postgres checkpoint. Since 2.1.0. |
reconciler.staleAfterMillis | 120000 | How long past its due time an execution must be idle to be re-sent. Keep it well above the longest single node (the HTTP request timeout). |
reconciler.intervalMillis | 30000 | How often the reconciler scans. |
reconciler.batchSize | 200 | Executions re-sent per scan. |
joinCompletionScanner.intervalMillis | 30000 | Backstop that resumes a parent whose branches all finished. Default 5 minutes before 2.1.0. |
callbackTimeoutScanner.intervalMillis | 300000 | Backstop that times out callback waits whose deadline passed (bufferSeconds grace, default 120). |
| MAINTENANCE | ||
maintenance.enabled | true | Enable the background partition maintenance job (Postgres only). |
maintenance.partitionLookaheadMonths | 13 | How many months ahead to pre-create Postgres partitions. |
maintenance.partitionStartOffsetMonths | 1 | How many months back to start partition creation. |
maintenance.retentionDays | 15 | Days of execution history to retain before purging (executions, step results and step calls). |
maintenance.intervalMillis | 3600000 | How often the maintenance job runs (1 hour default). |
| METRICS & TELEMETRY | ||
metrics.enabled | true | Expose Prometheus metrics at /metrics. |
telemetry.enabled | false | Enable OpenTelemetry span export. |
telemetry.serviceName | trama | Service name attached to all exported spans. |
telemetry.otlpEndpoint | http://localhost:4317 | OTLP gRPC endpoint for trace export. |
Async Callback
Async task nodes pause a workflow and wait for an external system to POST to a callback URL. The three runtime.callback.* keys must be set for this to work. The runtime injects {{runtime.callback.url}} and {{runtime.callback.token}} template variables into the outbound request body, so the external service knows where to call back and which token to present.
runtime:
callback:
baseUrl: "https://trama.internal" # must be reachable by downstream services
hmacSecret: "change-me-in-production"
hmacKid: "default"Redis Cluster
Trama supports Redis Cluster-aware queue sharding. Ready/in-flight queue keys and rate-limit keys are written with hash tags so multi-key Lua operations stay inside one Redis slot. The due-work index is a single key.
- Use
redis.topology: CLUSTERto enable the Lettuce cluster client. - Set
redis.cluster.nodesto one or more reachable cluster seed nodes. - Keep the same
redis.queue.keyPrefixon every worker pod. - Give each worker process a unique
redis.sharding.podIdso rendezvous ownership is stable. - Tune
redis.sharding.virtualShardCountandredis.sharding.claimerCounttogether when scaling out.
redis:
topology: "CLUSTER"
cluster:
nodes:
- "redis://redis-cluster-0.redis:6379"
- "redis://redis-cluster-1.redis:6379"
- "redis://redis-cluster-2.redis:6379"
queue:
keyPrefix: "saga:executions"
consumer:
batchSize: 100
processingTimeoutMillis: 20000
sharding:
podId: "${HOSTNAME}"
virtualShardCount: 64
membershipKey: "saga:runtime:pods"
membershipTtlMillis: 10000
heartbeatIntervalMillis: 3000
refreshIntervalMillis: 2000
claimerCount: 4Environment Overrides
Supported overrides include:
RUNTIME_ENABLED
RUNTIME_WORKERCOUNT # since 2.1.0
RUNTIME_PREFETCH # since 2.1.0
RUNTIME_EMPTYPOLLDELAYMILLIS
RUNTIME_CALLBACK_BASEURL
RUNTIME_CALLBACK_HMACSECRET
RUNTIME_CALLBACK_HMACKID
METRICS_ENABLED
TELEMETRY_ENABLED
REDIS_URL
REDIS_TOPOLOGY # STANDALONE | CLUSTER (since 2.0.1)
REDIS_CLUSTER_NODES # comma-separated seed nodes (since 2.0.1)
REDIS_SHARDING_VIRTUALSHARDCOUNT
REDIS_COMMANDTIMEOUTMILLIS # since 2.1.0
RECONCILER_ENABLED # since 2.1.0
RECONCILER_STALEAFTERMILLIS # since 2.1.0
RECONCILER_INTERVALMILLIS # since 2.1.0
RATELIMIT_ENABLED # since 2.1.0
DATABASE_HOST
DATABASE_PORT
DATABASE_DATABASE
DATABASE_USER
DATABASE_PASSWORD
DATABASE_POOL_DEFINITIONCACHETTLMILLISEach override can also be passed as a JVM system property with the dotted key (e.g. -Dredis.topology=CLUSTER). Any other key can be overridden with Hoplite's -Dconfig.override.<key>=<value> system properties.
Profiles
Dev profile
runtime:
workerCount: 2
emptyPollDelayMillis: 100
metrics:
enabled: true
telemetry:
enabled: falseProd profile (split API and Workers)
In production you can run API pods separately from worker pods. An API pod (runtime.enabled: false) accepts runs and callbacks and queues them; worker pods execute them. Since 2.1.0; earlier versions answered 503 in this mode.
API deployment profile
runtime:
enabled: false
metrics:
enabled: true
telemetry:
enabled: true
database:
pool:
maxPoolSize: 20Worker deployment profile
runtime:
enabled: true
workerCount: 32
maxStepsPerExecution: 25
redis:
consumer:
batchSize: 100
processingTimeoutMillis: 20000
rateLimit:
enabled: true
metrics:
enabled: true
telemetry:
enabled: trueWorker deployment profile with Redis Cluster
runtime:
enabled: true
workerCount: 32
redis:
topology: "CLUSTER"
cluster:
nodes:
- "redis://redis-cluster-0.redis:6379"
- "redis://redis-cluster-1.redis:6379"
- "redis://redis-cluster-2.redis:6379"
consumer:
batchSize: 100
processingTimeoutMillis: 20000
sharding:
podId: "${HOSTNAME}"
virtualShardCount: 64
membershipKey: "saga:runtime:pods"
membershipTtlMillis: 10000
heartbeatIntervalMillis: 3000
refreshIntervalMillis: 2000
claimerCount: 4
metrics:
enabled: true
telemetry:
enabled: trueScale workers horizontally by running multiple worker deployments/replicas with this same worker profile.