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:

bash
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-10

If 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:

b4-sandbox-infra-values.yaml
orchestrator:
  subjects:
    - kind: ServiceAccount
      name: b4-app
      namespace: b4-app

Install the sandbox infrastructure first:

bash
helm upgrade --install b4-sandbox-infra \
  oci://ghcr.io/cacheplane/charts/b4-sandbox-infra \
  --namespace b4-app \
  --create-namespace \
  --values b4-sandbox-infra-values.yaml

You 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:

bash
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-10

Use the same namespace and image values for upgrades:

bash
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-10

image.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:

bash
helm upgrade b4-app oci://ghcr.io/cacheplane/charts/b4-app \
  --namespace b4-app \
  --reuse-values \
  --set secretName=my-b4-secrets

The 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-app for the canonical release);
  • sandboxNamespace=b4-sandboxes, which is informational only;
  • automountServiceAccountToken=true on 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:

bash
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.yaml

Edit 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:

b4-sandbox-infra-rbac-values.yaml
orchestrator:
  subjects:
    - kind: ServiceAccount
      name: b4-app
      namespace: b4-app

Inspect the complete file, then apply it. Helm replaces arrays as values, so do not assign a guessed numeric subject index:

bash
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.yaml

Keep 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:

bash
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=false

No 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.