Version 3.0
Cluster and node requirements

Cluster and node requirements

What your Kubernetes cluster and its node must provide before the platform can run.

Audience: IT (the enabler)

This page is platform independent: it describes what the cluster must look like whatever you build it on. For the AWS specifics, see Deploy on AWS EKS.

Version 3 does not install into a generic cluster, because agent code runs inside a hardware-isolated microVM per run (see Safe execution and isolation). That puts three requirements on the cluster, and a cluster missing any of them cannot run agent work:

  • Nested virtualization. The sandbox starts a virtual machine per run, so the node's kernel must expose KVM.
  • A sandbox runtime. Kata Containers, installed once per cluster and registered with containerd. See Install the sandbox runtime.
  • Raw block storage for the data root. A StorageClass whose provisioner hands the platform a raw block device, which it formats and mounts itself. See The data root.

Kubernetes requirements

  • Kubernetes 1.36 or newer. On AWS that means an EKS 1.36 cluster.
  • A working Metrics Server: kubectl top must return data.
  • A CSI driver that provisions volumeMode: Block volumes and supports volume expansion, with a StorageClass on it for the data root (on AWS the chart creates that class itself). See Storage for the data root.
  • An ingress controller or load balancer controller that you operate.
  • A secrets operator for delivering credentials into the cluster.
  • Two namespaces: one for the application, one for bootstrap work.

Node requirements

  • Exactly one node, in a node group fixed at a size of one. Every workload is pinned to it, and adding nodes does not add capacity.
  • Any availability zone. Nothing ties the node to a zone; a replacement can come up wherever the node group spans.
  • Architecture: x86_64 only. There is no arm64 build.
  • Nested virtualization: required, and enabled explicitly in the launch template before the node group exists. It is off by default even on instance families that support it, and a node that joined without it is replaced rather than fixed.
  • Sizing: 32 vCPU and 128 GiB. On AWS that is an m8i.8xlarge, the instance type we primarily test against, and thus support. The node hosts the whole application plus every concurrent sandbox run, and sandbox concurrency is a fixed runner pool, which makes the node the hard ceiling on how much agent work can run at once.
  • Nothing to prepare on the node. The node image is the one your Kubernetes distribution ships: no bootstrap script, no packages, no filesystem to format.

The data root

The platform keeps its node-local state in one btrfs filesystem, mounted at /var/lib/blockparty/<release>, and owns it end to end: a Pod of the release claims a raw block volume from its StorageClass, formats it on first use, mounts it, and lets it go when the Pod is removed — on helm uninstall and on every node replacement, Kubernetes then deletes the claim and the disk.

This filesystem is a cache, not the system of record. Durable data lives in your database and your object storage bucket; the data root holds materialized workspaces and the caches that make them fast. Losing it costs a cold cache, not data — so size it for working set rather than total data volume, and treat it as replaceable rather than something to back up.

For the mount contract, resize semantics, and changing the class later, see the Operations reference.

Storage for the data root

On AWS (infrastructure.platform: aws in the values file) the chart creates the StorageClass itself: gp3 on the EBS CSI driver, encrypted with your KMS key. On other clusters — or on AWS when you need tuned IOPS or throughput — you provide the class and name it in the values file (vfs.dataRoot.storageClassName), which also suppresses the chart-created one; see Advanced configuration. The values file requests the size either way (vfs.dataRoot.size, 100 GiB by default). The class must provide:

RequirementWhy
volumeMode: Block volumesThe platform formats the device itself; a filesystem-only provisioner cannot serve the claim.
volumeBindingMode: WaitForFirstConsumerThe volume must be created in the zone of the node the workload lands on.
allowVolumeExpansion: trueGrowing the data root is a change to vfs.dataRoot.size and an upgrade.
reclaimPolicy: DeleteThe volume is a cache that dies with the Pod that claims it. With Retain, every replaced node would orphan a disk that nothing can reattach.
Encryption at restMaterialized workspaces are your data. Encryption is a property of the class, so the class (or your account's default encryption) is where the key is chosen.
Provisioned IOPSMaterialization is metadata-heavy, so IOPS matter more than throughput.

Size it generously: the sandbox keeps sizeable image and bundle caches here alongside materialized workspaces, and a volume that is too small shows up as slower runs rather than as an error.

Verify the data root

Once the platform is installed, check the claims and the workload that owns the mount:

kubectl -n <namespace> get pvc -l app.kubernetes.io/name=blockparty,app.kubernetes.io/component=data-root
kubectl -n <namespace> logs daemonset/<release>-blockparty-data-root -c data-root-manager

There is one claim per node, and each must be Bound. The log ends with data root ready. Helm collapses the <release>-blockparty prefix to just the release name when the release name already contains blockparty, so a release named blockparty has daemonset/blockparty-data-root. A claim that stays Pending is a StorageClass problem: kubectl describe pvc carries the provisioner's reason, and the Operations reference covers the common KMS cause.

To confirm the mount from the node itself:

findmnt --target /var/lib/blockparty/<release> --output TARGET,FSTYPE,OPTIONS,PROPAGATION

TARGET must be the data root itself rather than a parent mount, FSTYPE must be btrfs, OPTIONS must include user_subvol_rm_allowed, and PROPAGATION must be shared.

Network egress

The deployment needs outbound HTTPS to a small set of Sema4.ai services, the registries that serve the sandbox runtime, and everything you connect it to: model providers, MCP servers, integrations, and data sources. Unrestricted outbound HTTPS is the simplest configuration and what we recommend; for the explicit allow-list, see Network endpoints.

Caveats and limits

Horizontal scalability and redundancy are planned for a future release. Until then, this version runs the application on a single node by design:

  • Run exactly one node. Adding nodes does not serve more load, and there is no multi-node or multi-AZ topology for the application tier.
  • The node is a single point of failure. Losing it makes the workspace unavailable until a replacement joins; your data is safe in the database and object storage, so plan your recovery time around it, not data recovery.
  • Do not install an autoscaler. Cluster Autoscaler and Karpenter are free to drain and replace the node, which takes the platform down and costs its cache. Sandbox concurrency is a fixed pool size, not a function of load.
  • Use x86_64. There is no arm64 build.

What this article will cover

  • The validated add-on set and versions
  • Node sizing against a target concurrency, with the per-run CPU and memory budget