better/compute

Requirements and sizing

What the control plane needs and how far the measured numbers go in sizing it, the PostgreSQL version, what a node must have before bcd can run (Linux, systemd, cgroup v2, the NVIDIA driver, KVM and IOMMU for VPS hosts), the GPUs the inventory knows, and the network ports.

experimental@better-compute/server@better-compute/gateway@better-compute/postgres@better-compute/inventory@better-compute/cliREFERENCE.mdmigrate.tsgpu-models.tsgateway.tsconfig.tsgateway.tsself-check.tsdoctor_host.godoctor_gpu.gobcd-host-real.test.ts

This page collects what each part of a deployment needs to run, and what is known about how big it has to be. The short version: the control plane is Node.js processes and one PostgreSQL database; a node is a Linux server with systemd and cgroup v2, plus the NVIDIA driver for GPUs and KVM with an IOMMU for virtual machines. Sizing beyond that is not validated yet, and the page says where the numbers come from.

No validated sizing yet

PLAN.md fixes reference machines for its performance gates, but no benchmark has run on them, and no run on real PostgreSQL is recorded. Every number below was measured on a development workstation, mostly on PGlite in one process, and is a floor, not a capacity plan. See scaling for every measurement and its conditions.

The control plane

Software

RequirementNotes
Node.js 22.12 or newerEvery package's engines. A TypeScript configuration file needs 22.18 or newer. CI runs Node.js 22 and 24
PostgreSQL 16 or newerSee below
PgBouncer, optionalTransaction mode is supported: no session state, no LISTEN/NOTIFY, no session advisory locks
Better IAMEmbedded in the api and worker processes, or remote; see installation

The control plane has no container image of its own; any host or container that runs Node.js runs better-compute serve (see containers).

PostgreSQL

RequirementWhy
PostgreSQL 16 or newermigrate() reads server_version_num and refuses anything below 160000 with INVALID_CONFIG. CI runs 17, and the reference machine runs 17
The btree_gist extensionThe first migration creates it; the booking exclusion constraint needs it. On a managed service that does not let the database owner create it, a superuser runs CREATE EXTENSION btree_gist once
CREATE on the databaseMigrations create tables, indexes and functions
One database per deploymentapi, workers and gateways all connect to it; it is the only source of truth

PGlite (PostgreSQL in WebAssembly) is for development, tests and the simulators: one connection, transactions serialized, inside one process. It cannot be shared by separate processes, so it cannot run a split or highly available deployment. Details are on database.

Connections. Each process holds its own pool, 10 connections by default (postgres({ max }), 1 to 100), so the total is roughly the pool size times the number of processes. PLAN.md lists connections among the first limits at scale and puts PgBouncer in front of PostgreSQL.

Reference machines

bench/REFERENCE.md fixes the machines the PLAN.md §16 gates are quoted on:

RoleCPUMemoryStorageSoftware
Gateway8 vCPU32 GiB—One gateway process on Node.js 24
PostgreSQL16 vCPU64 GiBNVMePostgreSQL 17

Not fixed yet (decision D15): the instance type or CPU model, the network between hosts, the hosts for the api, workers and load generators, PgBouncer's placement and the PostgreSQL configuration.

What has been measured

QuantityMeasured (workstation, mostly PGlite)Gate
Sessions one gateway holds5,000 mTLS sessions at 10 s heartbeats, dispatch p99 24 to 26 ms, 0 drops5,000 for 30 min, dispatch p99 below 500 ms
Gateway memory at 5,000 sessions1,240 MiB RSS for the gateway process over 60 s with PGlite inside it; 2,016 MiB after 30 min, the growth traced to PGlite's WebAssembly memory while the JS heap stayed at 235 MiBRSS below 2 GiB
Commandsack p99 378 ms at 100 commands/s on 1,000 nodesp99 below 1.5 s at v0.4
Placements1,092 placements/s at 1,000 nodes and 464/s at 10,000 nodes (latest recorded run), pool-lease placer end to end200/s at v0.4
Gateway killre-home p99 13.25 s at 10,000 nodes over 3 gateways (simulated time)below 60 s
Cold start10,000 nodes connected, adopted and schedulable at 80 s (simulated time)below 3 min
PostgreSQL CPU at 10,000 nodes and 100,000 GPUsnot measuredbelow 50% on the reference PostgreSQL

Sizing each tier

Recommendations, derived from the defaults and the numbers above, not validated on production hardware:

TierScales byStarting point
GatewaysSessions: maxStreams is 5,000 per process by default, and doctor warns at 80% (fleet.gateway-headroom)Enough gateways that the region's nodes fit in the survivors after losing one, and at least three per region (PLAN.md). For 10,000 nodes: three gateways hold about 3,333 each, and two survivors hold 5,000 each, exactly at the limit, so run four. Give each the reference 8 vCPU and 32 GiB until measured otherwise
apiRequests; statelessThree behind an L7 balancer for a highly available region (PLAN.md)
WorkersReconcile throughput; 256 shards split among themTwo or more, so a lost worker fails over (see high availability); add scheduler processes for the pool-lease placer
PostgreSQLWrites per second, connectionsThe reference 16 vCPU, 64 GiB and NVMe is what the 10,000-node gates are to be measured on; PLAN.md targets below 50% CPU there

PLAN.md's design point is 10,000 nodes and 100,000 GPUs per region on one PostgreSQL, and its estimate of the load at that size (liveness writes, reconnect storms, commands, events, placements) and the order in which limits are expected to bite are on what breaks first.

Storage. audit_events, inventory_snapshots and usage_windows grow with time and are partitioned by month; the retention module prunes and drops what every cursor has passed (see retention). No growth rate per node has been measured.

Nodes

Platform

RequirementChecked byNotes
Linux on amd64 or arm64The install scriptuname -m of x86_64/amd64 or aarch64/arm64; releases build only those two
Root for the installThe install scriptAlso systemctl, sha256sum, and curl or wget with HTTPS
systemd as initbcd doctor systemd (fail)bcd's hold slices, the helper's units and libvirt's scopes are systemd units
cgroup v2 with cpuset, cpu and memorybcd doctor cgroups (fail)Holds with limits are confined in cgroup slices; without hugetlb (warn) a hold's huge page limits are not enforced
Controllers delegated at the cgroup rootbcd doctor delegation (warn)cpu, cpuset, memory, io, pids, hugetlb in cgroup.subtree_control; systemd usually enables them once bcd's slices exist
A synchronized clockbcd doctor clock (fail when the kernel reports it unsynchronized)Certificates, exec tickets and signed intents compare times; run an NTP client such as systemd-timesyncd or chrony (e2a0cf0)
A persistent data_dir—/var/lib/bcd by default: the node key, identity, journal and, on VPS hosts, instance disks and volumes
Outbound HTTPS to the gatewaybcd doctor gateway (fail when no TCP connection within 5 s)Nodes only dial out; see ports
TPM 2.0, optional/dev/tpmrm0With one, the node key is made in the TPM, and the tpm attestor can join the machine without a token; see attestation

No minimum kernel version is checked anywhere in bcd. The full list of bcd doctor checks is on the daemon overview; run it before the first join (the install script does, and only warns).

GPU hosts

bcd reads GPUs through nvidia-smi and needs no NVML library or cgo (cb987e5): a host build without nvidia-smi reports no GPUs, and bcd doctor's nvml check warns.

RequirementChecked byNotes
The NVIDIA driver, with nvidia-sminvml (warn without it), gpu-busThe nvidia-driver host unit can install it from desired state
nouveau not loadednouveau (fail)Blacklist it and rebuild the initramfs so the NVIDIA driver can bind the GPUs
Every GPU on the bus bound and seengpu-bus (fail when nvidia-smi sees fewer GPUs than are bound to nvidia)A GPU that fell off the bus (Xid 79) needs gpu.reset or a reboot
Persistence modepersistence (warn)Without nvidia-persistenced, every first CUDA call re-initializes the GPU
nvidia-fabricmanager on NVSwitch systemsfabric-manager (fail when NVSwitches are present and the unit is not active)CUDA fails on every GPU until it runs; the fabric-manager host unit manages it
The NVIDIA Container Toolkit, for containers—CDI specs for holds; see the container-toolkit host unit
A healthy GPUgpu per GPUA pending row remap or MIG change warns; failed row remapping or uncorrectable ECC errors fail

On WSL 2 (/dev/dxg without /dev/nvidiactl), doctor says the paravirtual GPU runs CUDA but supports no VFIO passthrough, gpu.reset, MIG or DCGM, and the nvidia-driver unit never installs a Linux driver there (2e71a60).

VPS and sandbox hosts

RequirementChecked byNotes
/dev/kvmkvm (warn)Doctor says why it is missing: VT-x/AMD-V off in the firmware, the kvm_intel/kvm_amd module not loaded, or a VM without nested virtualization
An IOMMU, for GPU passthroughiommu (warn)The iommu host unit sets the kernel arguments
vfio-pcivfio (warn)Loaded on first passthrough; doctor checks it is available
Each GPU in an IOMMU group of its owniommu-groups (warn)Passthrough hands the VM the whole group, so a NIC or NVMe drive sharing a GPU's group makes that GPU unusable for VMs; move it to a CPU-attached slot or enable ACS
libvirt-kvm: virsh, qemu-img, genisoimage or xorriso, nftDoctor fails a host missing qemu-img, both ISO tools, or nftUnder bcd helper, also dynamic_ownership = 0 and the group setup of the libvirt check
cloud-hypervisor: the binary, /usr/share/cloud-hypervisor/CLOUDHV.fd, systemd-run, systemd 247 or newer, nftcloud-hypervisor, systemdOne confined systemd unit per VMM
Huge pages, for flavors that ask for themhugepagesThe hugepages host unit reserves them
Disk for volumes—A volume must fit in the data directory's free space less a reserve of 5% (at least 2 GiB) less every volume's unwritten space

Site agents and Kubernetes

A bare-metal site agent needs its provisioning network, the iPXE builds and a ramdisk image you supply, and its own systemd unit for the privileged ports; see site agents and the ramdisk. bcd on an existing Kubernetes cluster runs as a DaemonSet with its own host mounts; see bcd on Kubernetes.

What has run on real hardware

HardwareStatus
A WSL 2 host with one GeForce RTX 5090 (driver R616)bcd built without fakehw joins a dev stack over mTLS, is adopted, reports the real inventory and GPU, and answers bcd status and bcd doctor (tests/e2e/bcd-host-real.test.ts, opt in with BCD_E2E_REAL=1; 0915ab7, 92e3385, 898a23c)
Data-center GPUs (H100, A100 and the rest)Not run. The fake hardware build reports NVIDIA H100 80GB HBM3; the nvidia-smi parsing is tested against fixtures
libvirt-kvm and cloud-hypervisor on a real KVM hostNot run; tested against fakes
Host config units on a real machineNot run, apart from TestRealKernel's sysctl, transparent huge pages and 2 MiB pages
TPM joinsTested with the software TPM only

Supported GPUs

The inventory normalizes each GPU's NVML name into a product and a family through a versioned table (gpuModels, version 2, in packages/inventory/src/gpu-models.ts). Pools, policies and meters use the family. A name the table does not know maps to the family other, which raises the GpuModelUnknown condition and keeps the node from scheduling until a release adds it. The normalization rules are on GPU naming.

FamilyProductNVML names
H100H100-SXM5-80GBNVIDIA H100 80GB HBM3
H100H100-PCIE-80GBNVIDIA H100 PCIe
H100H100-NVL-94GBNVIDIA H100 NVL
H200H200-SXM5-141GBNVIDIA H200
H200H200-NVL-141GBNVIDIA H200 NVL
B200B200-SXM-180GBNVIDIA B200
GB200GB200-SUPERCHIP-186GBNVIDIA GB200
GH200GH200-SUPERCHIP-96GBNVIDIA GH200 480GB, NVIDIA GH200 96GB HBM3
GH200GH200-SUPERCHIP-144GBNVIDIA GH200 144G HBM3e
A100A100-SXM4-40GBNVIDIA A100-SXM4-40GB
A100A100-SXM4-80GBNVIDIA A100-SXM4-80GB, NVIDIA A100-SXM-80GB
A100A100-PCIE-40GBNVIDIA A100-PCIE-40GB
A100A100-PCIE-80GBNVIDIA A100 80GB PCIe
L40SL40S-PCIE-48GBNVIDIA L40S
L40L40-PCIE-48GBNVIDIA L40
L4L4-PCIE-24GBNVIDIA L4
A40A40-PCIE-48GBNVIDIA A40
A30A30-PCIE-24GBNVIDIA A30
A10A10-PCIE-24GBNVIDIA A10
RTX-PRO-6000RTX-PRO-6000-SERVER-96GBNVIDIA RTX PRO 6000 Blackwell Server Edition
RTX-PRO-6000RTX-PRO-6000-WORKSTATION-96GBNVIDIA RTX PRO 6000 Blackwell Workstation Edition
RTX-PRO-6000RTX-PRO-6000-MAXQ-96GBNVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition
RTX-6000-ADARTX-6000-ADA-PCIE-48GBNVIDIA RTX 6000 Ada Generation
RTX-A6000RTX-A6000-PCIE-48GBNVIDIA RTX A6000
RTX-5090RTX-5090-PCIE-32GBNVIDIA GeForce RTX 5090
RTX-5080RTX-5080-PCIE-16GBNVIDIA GeForce RTX 5080
RTX-4090RTX-4090-PCIE-24GBNVIDIA GeForce RTX 4090

"Supported" here means the inventory names the card and pools can hold it. What a card can do beyond whole-GPU holds depends on the card: MIG needs a MIG-capable part (see fractional GPUs), passthrough needs the IOMMU setup above. Only NVIDIA GPUs are inventoried.

Network and ports

Nodes only dial out. Everything the control plane sends a node travels down the session the node opened.

FromToPort, defaultProtocolNotes
Clients, browsersapi, through an L7 balancer--port, 3000HTTP; the balancer terminates TLSserve never terminates TLS for the API; baseURL is the public origin. /healthz, /readyz and /metrics are on the same listener
DaemonsGateways, through an L4 balancer with TLS passthroughThe port in gateway_url; the gateway listens on 8443 by default (host.gateway.listen)gRPC over HTTP/2, mutual TLS 1.3A balancer that terminates TLS strips the client certificate
Daemons behind a TLS-inspecting proxyGateway WebSocket fallback443 (host.gateway.ws.listen)wss://…/v1/sessionIdentity proven in-band; see WebSocket fallback
apiEach gateway's control API8444 (host.gateway.internal.listen)HTTP/2, mutual TLS from the internal CAPokes, tunnels, revocations; each gateway advertises its internal_url
api, workers, gatewaysPostgreSQL (or PgBouncer)Your database'sPostgreSQL
NodesThe control plane's downloadsHTTPSGET {basePath}/downloads/bcd/{version}/linux-{arch}Install script and self-update; the digest is checked against the signed manifest
VPS hostsImage sources, snapshot URLsHTTPSBase images and snapshots are SHA-256 verified; http:// is refused
Nodes with a telemetry sectionYour OTLP collectorIts endpointOTLP/HTTPSee node metrics
Machines on the provisioning networkSite agentUDP 67 and 4011 (proxy DHCP), UDP 69 (TFTP), [boot] listen (HTTP)Privileged ports; the install script's unit does not grant CAP_NET_BIND_SERVICE
Site agentBMCsHTTPS for RedfishRedfish, or IPMI over LAN through bcx-ipmiName the BMC networks in [site] bmc_networks so site.httpProxy never reaches into them
InstancesTheir host's metadata service[imds] listen, for example 169.254.169.254:80HTTPAnswers only its own running instances
Hosts and servicesThe workload identity issuer443HTTPSThe issuer URL must be https on 443

Was this page helpful?

Better Compute is created by Sean Filimon

Last updated

On this page