How throughput is calculated
Tracecat benchmarks a reference workload of independentcore.table.insert_row actions on three 4 vCPU executors.
The measured coefficients for that workload are:
Two formulas size the executor tier for a target of
λ actions per second:
TRACECAT__EXECUTOR_MAX_CONCURRENT_ACTIVITIES controls in-flight actions per executor replica, not actions per second.
A 4 vCPU replica completes about 0.8 × 4 / 1.21 ≈ 2.7 actions per second for the reference workload whether it admits 8 or 32 activities, so keep the default of 32 and add replicas to add throughput.
The extra admission absorbs bursts; it does not add CPU.
Your actions differ from the reference workload.
HTTP actions that wait on remote APIs use less CPU and longer wall time, so they need more admission and fewer cores.
Python and data-transform actions use more CPU per action.
Measure both coefficients on your own workload before committing hardware, and re-run the formulas with your numbers.
Sizing table
Every tier uses the same executor replica shape: 4 vCPU, 4 GiB memory, the default 32 admitted activities, and a Postgres client pool of 8 + 8 connections. Host totals add roughly 8 vCPU and 16 GiB for the API, worker, UI, Temporal, both PostgreSQL instances, Redis, and MinIO.
Only the 8 actions/s row is measured, on three 4 vCPU executors that admitted 8 activities each.
The benchmark also showed that admitting more per replica did not raise throughput, so the rows keep the default 32 and change only the replica count: three executors per 8 actions per second, doubling with each tier.
The in-flight column is the burst the tier can hold, not its sustained rate.
The rows assume the reference action mix and that every replica gets its own physical cores.
Validate any tier above 16 actions/s with the benchmark suite on the target hardware before you commit to it.
The default settings row is deliberately conservative.
A single executor with 32 admitted activities could reach 32 / 2.6 ≈ 12 actions per second, but on an 8-core host shared with every other service it has roughly 4 cores available, which caps it at 0.8 × 4 / 1.21 ≈ 2.6 actions per second for the reference workload.
Adding cores or replicas, not a higher
TRACECAT__EXECUTOR_MAX_CONCURRENT_ACTIVITIES, raises sustained throughput on that host.
Physical capacity is a hard limit
Composecpus limits and replica counts do not create capacity.
Declaring six 4 vCPU executors on a 12-core host dropped measured throughput from 8.4 to 2.9 actions per second because the host was oversubscribed.
Before adding a replica, confirm the host has 4 idle cores for it: executor replicas × 4 + 8 ≤ host cores.
A single Docker Compose host tops out around 32 actions per second on commodity 64-core machines.
Above that, convert to Docker Swarm across several nodes or deploy on Kubernetes, and move PostgreSQL and Temporal to managed services.
Configure a tier
1
Keep the default executor concurrency
Leave
TRACECAT__EXECUTOR_MAX_CONCURRENT_ACTIVITIES and TRACECAT__EXECUTOR_THREADPOOL_MAX_WORKERS at their default of 32.
Every tier scales by replica count, not by this value.Actions that use for_each multiply database concurrency by TRACECAT__EXECUTOR_FOR_EACH_MAX_CONCURRENCY (default 4), so one replica can queue 128 database-bound tasks against its 8 + 8 pool.
If executor logs show pool timeouts, lower TRACECAT__EXECUTOR_FOR_EACH_MAX_CONCURRENCY to 1 or lower TRACECAT__EXECUTOR_MAX_CONCURRENT_ACTIVITIES to 8 rather than raising the pool, because the validated 8 + 8 pool is what the max_connections column budgets for.2
Pin executor resources and Postgres pools
Create Use the
docker-compose.override.yml next to docker-compose.yml.
Compose merges it automatically.max_connections value from the table.
Each executor replica can open at most TRACECAT__DB_POOL_SIZE + TRACECAT__DB_MAX_OVERFLOW connections per pool, and the API, worker, and agent services keep their default 10 + 60 pools.
Budget max_connections ≥ 16 × executor replicas + 150 so a burst cannot exhaust PostgreSQL connection slots.
Set shared_buffers to about 25% of the PostgreSQL memory limit.3
Start with the executor replica count
executor service has no fixed container_name, so Compose can run several replicas behind the shared Temporal task queue with no other changes.4
Verify against the target
Watch these signals while running a representative burst:
docker stats: executor containers should sit below 80% of their CPU limit. Sustained 100% means you need more replicas.SELECT count(*) FROM pg_stat_activity;onpostgres_db: peak connections should stay belowmax_connectionswith headroom for migrations and administration.- Temporal UI (uncomment the
temporal_uiport mapping indocker-compose.yml, then openhttp://localhost:8081): a growingshared-action-queuebacklog with idle executor CPU means admission is too low; a growing backlog with saturated CPU means you are out of cores.
Add a connection pooler above 12 executors
Client pools fan out with executor replicas while the useful number of PostgreSQL backends does not. In the reference benchmark, 16 physical executor backends behind PgDog preserved 99% of direct throughput while cutting peak PostgreSQL connections by 32%; 12 backends kept 90%. Twice as many executor backends did not raise throughput. From 12 executor replicas upward, place a transaction-mode pooler such as PgDog or PgBouncer between the executors and PostgreSQL, and size its executor backend pool at roughly 1.3 backends per executor vCPU (the benchmark’s 16 backends for 12 vCPU). A pooler multiplexes connections; it does not add database CPU. If PostgreSQL CPU or I/O saturates while connections stay within budget, give PostgreSQL more vCPU or move it to a managed instance.Scale the supporting services
The executor tier is measured; the other services are sized from defaults and operational experience.
Add worker replicas when workflow-task schedule-to-start latency in the Temporal UI grows while executor CPU stays idle.
Lower
TEMPORAL__THREADPOOL_MAX_WORKERS and TEMPORAL__MAX_CONCURRENT_WORKFLOW_TASKS from 100 on workers with fewer than 4 cores.
Related pages
- See Docker Compose for the base deployment, general host sizing, and the Docker Swarm conversion.
- See Kubernetes for KEDA autoscaling of worker and executor deployments beyond a single host.
- See Architecture for how the API, worker, executor, Temporal, and PostgreSQL services fit together.