Scaling and capacity planning¶
How the three concurrency settings multiply into CPU pressure, and how to size a host.
Not sure how many workers to run? On each host, in the install directory, run
./logstotal recommend-scaling. It detects the host's CPU and RAM and prints recommended
values with the reasoning; the model below is what it applies. It needs only python3, so
it works on a Docker host.
The mental model¶
LogsTotal has three nested layers of concurrency, and they multiply:
- Jobs per worker process —
HUEY_WORKERS. A worker runs this many threads, and each thread processes one job at a time. Analysis jobs and admin backfills share this pool. This is your throughput setting. - Tools per job —
parallel_execution+TOOL_MAX_WORKERS. Within a single job, tools run one after another by default. If an admin turns on Parallel tool execution (/admin/settings), up toTOOL_MAX_WORKERStools in that job run at once — never more than the workflow has. This is a per-job latency setting, not a throughput one. - Threads per tool —
threads(workflow YAML). Hayabusa (--threads) and Chainsaw (--num-threads) can use several threads each; Zircolite ignores the setting. RSigma batch evaluation uses one thread and rejectsthreadsvalues above 1. LogsTotal always passes an explicit value, so omittingthreadsmeans 1 (never all cores). The shipped workflows setthreads: 2for Hayabusa and Chainsaw. Workflows are stored in the database and shared by every worker, so sizethreadsfor your smallest worker host.
They multiply into CPU pressure:
peak CPU pressure ≈ HUEY_WORKERS × (TOOL_MAX_WORKERS if parallel else 1) × per-tool threads
Keep peak near your core count. Above it, tools fight for CPU and everything gets
slower. The default (parallel_execution off) collapses the middle term to 1, so peak is
just HUEY_WORKERS × threads.
Recommended defaults by host size¶
What ./logstotal recommend-scaling prints for a dedicated worker host running the
shipped workflows (the largest runs four tools), with parallel_execution off — the
throughput-optimal default:
| Cores | RAM | HUEY_WORKERS |
per-tool threads |
TOOL_MAX_WORKERS² |
DB_POOL_SIZE¹ |
peak CPU |
|---|---|---|---|---|---|---|
| 2 | 2 GB | 1 | 1 | 2 | 5 | 1 |
| 4 | 8 GB | 2 | 2 | 2 | 5 | 4 |
| 8 | 16 GB | 4 | 2 | 4 | 5 | 8 |
| 16 | 32 GB | 8 | 2 | 4 | 8 | 16 |
| 32 | 64 GB | 16 | 2 | 4 | 16 | 32 |
¹ DB_POOL_SIZE applies to PostgreSQL only (SQLite ignores it). Size it at least
HUEY_WORKERS so each worker thread can hold a connection; the pool is per process (web
and each worker get their own).
² TOOL_MAX_WORKERS only takes effect when parallel_execution is on, so it does not
enter this table's peak-CPU column. It is sized to the largest workflow's tool count, but
capped so one job's parallel tools (TOOL_MAX_WORKERS × threads) stay within the core
budget.
Notes:
- Everything on one host (web + worker + Redis): drop
HUEY_WORKERSby 1 to leave a core for the web and Redis processes. - Low RAM, many cores: RAM caps concurrency first — the model budgets about 1.5 GB per
concurrent job (a tool parsing a large EVTX is memory-hungry), and
recommend-scalingtakes the lower of the CPU and RAM limits. parallel_executionis worth turning on only on larger hosts where jobs usually arrive one at a time and you want each to finish faster.recommend-scalingalready sizesTOOL_MAX_WORKERSfor that; lowerHUEY_WORKERSso peak stays near the core count. Under steady load, leave it off.- Fleets: these recommendations are per host — run
./logstotal recommend-scalingon each machine and set that host's own.env. Dedicated worker hosts started fromdocker-compose.worker.ymldefault toHUEY_WORKERS=4; the fleet deploy sets it fromDEPLOY_HUEY_WORKERS(also 4).
Storage growth¶
Each job stores its uploaded log plus each tool's raw output. Stored tool stdout/stderr is
capped by MAX_LOG_OUTPUT_BYTES (default 50 KB per task). Raw outputs are deleted after
JOB_OUTPUT_RETENTION_DAYS (default 90) by a daily sweep; uploaded files are kept unless
you set UPLOAD_RETENTION_DAYS. Watch usage on /admin/storage and the disk gauge on
/admin. The schedules and the manual reclaim actions are in
Storage and retention.
Apply a recommendation¶
On the host, in the install directory:
./logstotal recommend-scaling # print recommendation + reasoning for this host
./logstotal recommend-scaling -- --apply # write HUEY_WORKERS / TOOL_MAX_WORKERS / DB_POOL_SIZE into .env
./logstotal recommend-scaling -- --apply-workflows # write per-tool threads: into workflows/*.yml
--apply only touches the .env settings (and skips DB_POOL_SIZE on SQLite, where it is
ignored); it asks before writing. Follow Apply configuration changes
afterwards — Docker containers must be recreated to see a new .env.
--apply-workflows writes the recommended threads: into every Hayabusa and Chainsaw task
in workflows/*.yml (Zircolite ignores threads and is left alone), editing only those
lines. Workflows live in the database, so load the edited files where the database is
initialised: on a Docker host — the control plane, in a fleet — run ./logstotal docker:up,
which rebuilds the image and reloads the workflows as the web container starts; in a
development checkout, run ./logstotal sync-workflows. The two flags work together or on
their own; the parallel_execution switch stays in /admin/settings.
The output shows both your current peak (from the threads: your workflows set now)
and the recommended peak, so you can see the gap.
Related: Configuration · Worker operations · Fleet installation · Docs index