Kubernetes
To deploy on Kubernetes, build the Node target image first, then install the b4-app chart. The chart runs an image you've already built, and it reads nothing from b4.config.ts.
PrerequisitesCopy link to section: Prerequisites
Build and publish a Node image as described in Node and Docker. Add the secret-safe .dockerignore from that guide first, and keep .b4/build in the context:
b4 check
b4 build
docker build -t ghcr.io/you/my-b4-app:2026-08-10 .
docker push ghcr.io/you/my-b4-app:2026-08-10If the app configures kubernetesSandbox, keep the infrastructure Helm release, the B4.run app and application credentials in the separate b4-app management namespace. Sandbox resources live in b4-sandboxes. Before the first install, prepare the complete intended cross-namespace subject list:
orchestrator:
subjects:
- kind: ServiceAccount
name: b4-app
namespace: b4-appInstall the sandbox infrastructure first:
helm upgrade --install b4-sandbox-infra \
oci://ghcr.io/cacheplane/charts/b4-sandbox-infra \
--namespace b4-app \
--create-namespace \
--values b4-sandbox-infra-values.yamlYou need both charts: the application chart runs the app, and the sandbox infrastructure chart sets up the sandbox side. See Kubernetes Sandbox for the provider and cluster-side security setup.
Install or upgrade the applicationCopy link to section: Install or upgrade the application
After the infrastructure RoleBinding contains the app ServiceAccount, install the app in the b4-app management namespace:
helm install b4-app oci://ghcr.io/cacheplane/charts/b4-app \
--namespace b4-app \
--set image.repository=ghcr.io/you/my-b4-app \
--set image.tag=2026-08-10Use the same namespace and image values for upgrades:
helm upgrade b4-app oci://ghcr.io/cacheplane/charts/b4-app \
--namespace b4-app \
--set image.repository=ghcr.io/you/my-b4-app \
--set image.tag=2026-08-10image.repository is required. Pin an immutable tag or set image.digest in production.
Health probesCopy link to section: Health probes
The chart sends startup, readiness and liveness HTTP probes to healthPath, which defaults to /healthz. B4.run's response there is process liveness, not dependency readiness. It touches no store and never queries the model, Postgres or the sandbox provider.
The runtime also serves GET /readyz. It probes the durable stores (threads, checkpointer, permissions) and answers 503 naming each failing dependency. The chart uses one path for all three probes. So point healthPath at /readyz only if you want every probe, liveness included, to fail when the database is unreachable. Neither endpoint checks the model or sandbox provider, so add your own check if rollout safety needs one.
Some stores are built while the Node runtime starts up, so a store failure at boot can stop the listener from starting at all. You'll see that as a startup failure, before /healthz ever answers.
Environment and secretsCopy link to section: Environment and secrets
Set ordinary environment entries through env or envFrom. For an existing Kubernetes Secret, use secretName:
helm upgrade b4-app oci://ghcr.io/cacheplane/charts/b4-app \
--namespace b4-app \
--reuse-values \
--set secretName=my-b4-secretsThe chart references the Secret, and you create it. Put model credentials and DATABASE_URL in an operator-managed Secret, and apply the tenant and authentication controls described in Security Architecture.
Shared Postgres stores and local .b4 files have different lifetimes. Configure each kind of state with Persistence and Tenancy before you rely on restarts or replicas.
Filesystem durabilityCopy link to section: Filesystem durability
The b4-app chart mounts only an emptyDir at /tmp. The app's .b4 directory lives in the container, so local SQLite checkpoints, threads, permissions and other .b4 data are lost when a Pod is replaced.
Use external durable stores for any state that must survive replacement. Sandbox-provider workspaces have their own volume lifecycle, separate from the app Pod's /tmp mount.
ServiceAccount setupCopy link to section: ServiceAccount setup
The chart defaults are:
serviceAccount.create=true;serviceAccount.name="", which resolves to the release-scoped chart fullname (b4-appfor the canonical release);sandboxNamespace=b4-sandboxes, which is informational only;automountServiceAccountToken=trueon the app Pod.
For an app that uses kubernetesSandbox, use the chart's default ServiceAccount in the management namespace. Add that planned ServiceAccount as a cross-namespace subject of the sandbox infrastructure chart's orchestrator Role before you install the application. A RoleBinding subject is a name reference, so it can refer to a future ServiceAccount. Granting access first avoids a Ready-but-sandbox-broken window, where the app Pod is running but its sandbox API calls are still forbidden.
First, capture the installed infrastructure chart version and export the release's effective values, including every existing RoleBinding subject:
INFRA_CHART_VERSION="$(helm get metadata b4-sandbox-infra --namespace b4-app | awk '$1 == "VERSION:" { print $2 }')"
test -n "$INFRA_CHART_VERSION" || { printf '%s\n' "unable to determine installed infrastructure chart version" >&2; exit 1; }
helm get values b4-sandbox-infra --all --output yaml \
--namespace b4-app \
> b4-sandbox-infra-rbac-values.yamlEdit b4-sandbox-infra-rbac-values.yaml so orchestrator.subjects contains the complete intended subject list: preserve every existing entry and append the planned application ServiceAccount as a cross-namespace subject. If the release has no extra subjects yet, that section looks like this:
orchestrator:
subjects:
- kind: ServiceAccount
name: b4-app
namespace: b4-appInspect the complete file, then apply it. Helm replaces arrays as values, so do not assign a guessed numeric subject index:
helm upgrade b4-sandbox-infra oci://ghcr.io/cacheplane/charts/b4-sandbox-infra \
--version "$INFRA_CHART_VERSION" \
--namespace b4-app \
--values b4-sandbox-infra-rbac-values.yamlKeep the provider's configured namespace, the infrastructure chart namespace and the informational sandboxNamespace value aligned. sandboxNamespace is informational. Changing it alone leaves RBAC and sandbox Pod placement as they were.
An app without kubernetesSandbox needs no Kubernetes API credentials. Install it with its own ServiceAccount and turn off token mounting:
helm upgrade --install b4-app oci://ghcr.io/cacheplane/charts/b4-app \
--namespace my-app --create-namespace \
--set image.repository=ghcr.io/you/my-b4-app \
--set image.tag=2026-08-10 \
--set automountServiceAccountToken=falseNo orchestrator RoleBinding is needed in this mode, because the app never calls the Kubernetes API through kubernetesSandbox. If the app needs some unrelated Kubernetes API permission, grant it separately. Keep the sandbox orchestrator Role for sandbox work.
Replicas and run coordinationCopy link to section: Replicas and run coordination
Postgres can provide shared durable stores for checkpoints, thread metadata and permission decisions. Long-term memory is configured separately. Even with all of those shared, the one-run-per-thread gate and the cancellation registry stay process-local.
So more than one replica requires guaranteed thread-aware routing to one owning process, or distributed per-thread serialization plus cancel routing. An HPA changes the replica count and a PodDisruptionBudget limits disruption. Neither one coordinates B4.run runs or sends a cancel request to the right process. Keep the one-replica default until you have that coordination.
See Production Topology for request routing, cancellation and failure-mode design.
Rollouts and shutdownCopy link to section: Rollouts and shutdown
The generated Node server.mjs leaves serveRuntime signal handlers off. The runtime has a drainable close() path, but the generated entry doesn't currently call it on SIGTERM. Set realistic termination grace periods, test streaming requests through a rollout, and write your own Node entry when you need coordinated signal handling.
Chart referenceCopy link to section: Chart reference
Review the released chart's README.md and values.yaml before you install. The chart exposes image, probe, ServiceAccount, environment, ingress, autoscaling, disruption-budget and security-context values. Meeting B4.run's replica and persistence requirements is still up to you.