Skip to main content

Two independent axes — pick each separately

A deployment is defined by two orthogonal choices. Conflating them is the most common sizing mistake:

Availability tier (T0 → T4)

The failure domain you survive — node → zone → region. This is what the tiers below set, and it drives your SLA. It changes topology, not capacity.

Concurrency (sizing)

How many users/requests you serve. This sizes CPU, memory, disk, replicas, and shardswithin any tier. The platform and inference layers scale independently (see Hardware Requirements).
The per-tier user counts below are typical pairings, not caps. 10,000 users doesn’t force T4 — you go to T4 to survive a region outage, not to hold more users. Need 10,000 users but single-region resilience is acceptable? Run a T2/T3 topology sized for 10,000. Pick the tier for failure domains; size for concurrency separately.

The tier ladder

TierSurvivesTarget SLATypical concurrencyStatus
T0 Single-Nodepod restart~99%~100dev / PoC only — no backup
T1 Essentialdisk loss~99.5%~100available
T2 Standard HAany node99.9%~1,000available (chart default)
T3 Multi-AZzone loss99.95–99.99%few thousandaspirational (multi-AZ is an IaC change)
T4 Multi-Regionregion loss99.99%+10,000+aspirational (second region is operator-built)

Per-tier reference architectures

AI-in-a-Box — dev / PoC only. Every component on one node. No HA and no backup by design — data is disposable (RPO unbounded). Use only where outage and potential data loss are acceptable.T0 Single-Node reference architecture
ResourceBare minimum
Nodes1
CPU / memory16–32 cores / 96–128 GiB
Node NVMe1–2 TiB (holds every DB + the object store)
Backupnone
Install-f values.minimal.yaml on every dependency (1× each)

Sizing the storage — the bare-minimum volume math

The dominant, variable storage driver is observability traces, and it scales with throughput × retention, not the tier. Total storage is the sum of four components — don’t size on traces alone:
ComponentScales withRough size
Platform baseline (queue, object-store metadata, working set)~fixed~200–500 GiB
Database working set (PG · ClickHouse hot · Kafka · Mongo · Valkey)throughput~1.5–3 TiB (node-local)
Analytics / observability traces — the dominant termreq/s × spans × retention × 3see below
Model registrymodel catalog, not the tier50 GiB → 2–5 TiB+

From concurrency to terabytes

1

Concurrency → req/s

req/s = concurrency ÷ avg request seconds. An agent request runs ~15 s (multi-step), so 1,000 concurrent users ≈ 67 req/s. Use your own measured request duration if it differs.
2

req/s → TB

TB ≈ (spans/req × 385 B × req/s × 86,400 × retention-days × 3 (replicas) × 1.3 (merge)) ÷ 10¹² (the ÷ 10¹² converts bytes to TB). Agent invocations average ~15 spans/req; traces are ~90% of per-request storage.
Baseline (agent workload, ~15 s/request, 90-day traces, ×3):
Concurrency≈ req/sTrace volume (×3)
~100~7~1 TB
~1,000~67~12 TB
~3,000~200~36 TB
~10,000~667~120 TB
Sized for the agent workload (~15 s/request, ~15 spans). The two levers that move these numbers most are the retention window (linear) and spans per request (fewer spans → proportionally less) — see the knobs below. Plug your own measured request duration and span count into the formula for an exact figure.

What the “external object store” actually is

Bud reaches object storage through a plain S3 API (externalServices.s3: endpoint + keys + bucket). By default it’s SeaweedFS, in-cluster — running on the same nodes’ NVMe, so there is no external store until you add one. The “external” store is simply a separate S3-compatible object store you stand up and operate yourself, then point that config at:
  • Self-hosted MinIO (most common on-prem) — a small cluster of cheap-disk nodes (HDD / standard-SSD, erasure-coded ≈ 1.5× raw). Bud ships an optional in-cluster MinIO toggle, or you run it separately.
  • Ceph / RADOS-GW — if you already run Ceph, its object gateway works too.
  • Cloud bucket (AWS S3 / Azure Blob) — only where the deployment isn’t air-gapped.
It is additional infrastructure to provision and operate — more (cheaper) nodes, not magic and not free. It exists to hold the cold analytics tier and off-site backups on inexpensive media, away from premium NVMe.
Cold-tiering ClickHouse to an external store is operator-configured (a ClickHouse storage_policy plus you provisioning the store) — not a Bud default flag. And the inference model volume is not shared storage: Bud pulls model weights from SeaweedFS to node-local disk (VOLUME_TYPE=local), so no NFS/CephFS is required on the model path.

Scaling up or down — the knobs

LeverEffectHow to turn it
Retention windowLinear — 30-day is ⅓ of 90-dayThe biggest lever. Shorten trace retention (default 30-day traces / 90-day metadata).
Request durationInverse — shorter requests = more req/s = more tracesSet by your workload; measure it. The baseline assumes ~15 s agent requests.
Spans / requestLinear — fewer spans, proportionally less storageEnable span sampling; keep only the orchestration spans you need.
Per-node NVMenodes = volume ÷ per-node diskStack more NVMe into vg1 (8 / 16 / 24 TB) before adding nodes.
Model catalogSets registry size independentlyPrune stale variants; size the registry volume to your model list.
Content logging+~2–3 KB per content-bearing spanOff by default. If on, bound it with a short retention window.
CPU / concurrencyCPU is the binding resource for agent/event loadSize vCPU to peak req/s; add nodes/HPA for throughput (memory rarely the limit).
Through T3 you stay at the 3-node HA floor — you scale by fitting bigger NVMe into each node’s vg1 pool, not by adding nodes. Only at T4 does trace volume start to drive node count. And even at T4 you can stay all-NVMe (~5–6 big-NVMe nodes) — you tier cold traces to external object storage to save cost, not because it won’t fit.

Next steps

Hardware Requirements

The underlying compute, memory, storage, and inference capacity model

Helm Configuration

Configure resources, retention, replicas, and the S3 endpoint