langsmith Helm release runs the Sandbox runtime in the same Kubernetes cluster as LangSmith. With sandboxes.external.enabled: true, the runtime is external: a second chart, langsmith-sandbox, installs it, here in a dedicated cluster. This guide covers the requirements, network rules, secrets, and Helm steps for that topology.
For the default topology and the settings both topologies share, see Enable Sandboxes. For upgrades, see Upgrade separate sandbox clusters.
Separate sandbox clusters require LangSmith Helm chart version 0.18.0-rc.51 or later, from the v0.18 preview, and a
langsmith-sandbox chart whose appVersion matches your langsmith chart. Earlier charts ignore sandboxes.external.enabled and deploy the embedded runtime. To list the runtime chart versions and their appVersions, run helm search repo langchain/langsmith-sandbox --versions --devel.Decide when to use a separate cluster
Use a separate cluster when:- Your LangSmith cluster cannot host sandbox nodes: The sandbox runtime needs KVM-capable nodes and privileged pods. For example, GKE Autopilot clusters cannot run the runtime, so a LangSmith installation on Autopilot needs a separate GKE Standard cluster for Sandboxes.
- You want to isolate sandbox workloads: Sandboxes run user code on privileged hosts. A separate cluster keeps those nodes away from the cluster that runs LangSmith and holds its application secrets. The runtime receives only the two secret values it needs.
Understand the topology
The LangSmith cluster keeps the Sandbox API and the control plane: authentication, placement, sandbox records in PostgreSQL, and the proxy for exec, file, and service URL traffic. The sandbox cluster runs the hosts and their microVMs.The LangSmith cluster keeps the API, placement, and sandbox records. The sandbox cluster runs the hosts, holds copies of two secrets, and reaches sandbox storage.
sandbox-host runs in the LangSmith cluster.
With an external runtime, the
langsmith release does not render sandbox-host, the JuiceFS formatter, the JuiceFS configuration Secret, or the proxy CA. The sandbox cluster reaches the JuiceFS Redis metadata store and the object storage; the LangSmith cluster does not need them.
The runtime chart uses the same settings as the embedded runtime, without the sandboxes. prefix:
The scaling and operations guides use the embedded runtime’s names. Apply the same settings in the runtime chart.
Check the requirements
Every cloud
- Nodes: KVM-capable
linux/amd64nodes, taintedsandbox.langsmith.com/host=true:NoScheduleand labeled to matchsandboxHost.deployment.nodeSelector. The node selector has no default, and the chart fails to render without it. The default toleration matches the taint above; for other taints, overridesandboxHost.deployment.tolerations. For node-level requirements, see the Enable Sandboxes prerequisites and Node requirements for bare-metal and on-premises clusters. - JuiceFS metadata Redis: Use
maxmemory-policy noeviction. Require AUTH, TLS, or IAM authentication whenever another cluster can reach it. The storage backends and bucket formats in the Enable Sandboxes prerequisites apply tojuicefs.storageandjuicefs.bucket. - LangSmith hostname: Set
config.hostnamein thelangsmithrelease to an address the sandbox cluster can reach. With an external runtime, the chart fails to render without it, or with a loopback or in-cluster Service address. config.endpoint: Set the runtime chart’sconfig.endpointtohttps://<LangSmith hostname>[/<base path>]/api, the externally reachable LangSmith API including the/apiprefix.- Private CA: If a private CA signs the LangSmith API certificate, the host ingress certificate, or an HTTPS egress proxy certificate, add that CA to the runtime chart’s
config.customCa. - Network: Open the paths in Configure the network.
- Secrets: Create the Secret in Create the runtime Secret.
sandbox-host image as described in Additional images for Sandboxes, and set images.sandboxHostImage.repository in the runtime chart to the mirrored repository. Keep the image’s release tag in the mirror and leave images.sandboxHostImage.tag empty, so the image follows the chart’s appVersion. A pinned tag does not change when you upgrade the chart.
Amazon EKS
- Identity: An IAM OIDC provider for the sandbox cluster, and an IAM role that trusts
system:serviceaccount:<namespace>:<service-account-name>. SetsandboxHost.serviceAccount.nameso the subject stays stable. SetworkloadIdentity.provider: awsandworkloadIdentity.aws.roleArn. - Storage grants:
s3:GetObject,s3:PutObject,s3:DeleteObject, ands3:AbortMultipartUploadonarn:aws:s3:::<bucket>/<juicefs.name>/*, ands3:ListBucketonarn:aws:s3:::<bucket>with the conditions3:prefixlike<juicefs.name>/*. A bucket that only JuiceFS uses needs no condition. - Nodes: Instance types that expose KVM, such as bare-metal instances.
- Cache: The JuiceFS cache defaults to
/var/cache/juicefson the root volume. On instance types with local NVMe, such asm5d.metal, mount the drives and pointjuicefs.hostMount.cacheDirsat them.
Google Kubernetes Engine
- Cluster: A Standard cluster, not Autopilot, with a node pool on
UBUNTU_CONTAINERDand nested virtualization (N2, N1, C3, or C4). Set the node pool’s OAuth scope tocloud-platform, so IAM grants decide access. - Identity: Set
workloadIdentity.provider: gcp.sandbox-hostuses the host network, and the chart runs the JuiceFS formatter there too, so both authenticate as the node pool’s service account and need no Workload Identity binding. LeavejuicefsFormatJob.nodeSelectorempty, so the formatter runs on the sandbox node pool. - Storage grants: Use a bucket that only JuiceFS uses, with uniform bucket-level access, and grant the node pool’s service account
roles/storage.objectAdminon it. If JuiceFS must share a bucket, turn on uniform bucket-level access and use the conditionresource.name.startsWith("projects/_/buckets/<bucket>/objects/<juicefs.name>/") || resource.type == "storage.googleapis.com/Bucket". A prefix-only condition blocks the list calls JuiceFS makes. The second clause lets sandbox nodes list the name and metadata of every object in the bucket. - Redis: With
juicefs.redis.iamAuthProvider: gcp, use Memorystore for Redis Cluster and grant the node pool’s service accountroles/redis.dbConnectionUser. - Cache: Point
juicefs.hostMount.cacheDirsat node-local SSD. - Namespace: If the sandbox cluster shares a Google Cloud project with the LangSmith cluster and has Workload Identity enabled, install the runtime in a namespace other than the LangSmith one. Workload Identity trusts a namespace and ServiceAccount name across every cluster in a project.
Azure Kubernetes Service
- Nodes: A VM size that supports nested virtualization.
- Identity: OIDC issuer and Workload Identity enabled, and a federated credential with the subject
system:serviceaccount:<namespace>:<service-account-name>. SetsandboxHost.serviceAccount.nameso the subject stays stable. SetworkloadIdentity.provider: azureandworkloadIdentity.azure.clientId, plustenantIdif it differs from the cluster’s tenant. The chart adds theazure.workload.identity/uselabel tosandbox-hostand the formatter. - Storage grants:
Storage Blob Data Contributoron the container. Setjuicefs.storage: wasbandjuicefs.storageAccountName. - Redis: Azure Managed Redis on database 0 over
rediss://, with theNoEvictioneviction policy set on the database. JuiceFS cannot change it. - Cache: Point
juicefs.hostMount.cacheDirsat the temporary disk under/mnt.
Configure the network
By default, the LangSmith control plane connects directly to the node IPs thatsandbox-host advertises, on TCP port 19190. Allow these paths:
For the first row, allow the address that LangSmith pods appear from on your network: the node security group, the pod CIDR, or a SNAT address, depending on the CNI.
The path from the sandbox nodes to
config.endpoint is new in this topology. With the embedded runtime, sandbox-host reaches LangSmith through in-cluster Service DNS. With an external runtime, it calls config.endpoint over the network, so the call must pass every control in front of that address:
- Load balancer allowlists: If the LangSmith load balancer restricts source addresses, allow the address sandbox node traffic arrives from. For an internal load balancer, that is usually the sandbox nodes’ private addresses. For an internet-facing one, it is usually the public address of the NAT gateway that the sandbox nodes use.
- Egress filtering: If a firewall filters egress from the sandbox cluster, allow the LangSmith hostname.
- DNS: The sandbox nodes must resolve the LangSmith hostname. An address that resolves only in the LangSmith cluster’s private DNS does not work. For an internal load balancer, add the record to a private DNS zone the sandbox network uses, before you install the runtime: a lookup that fails at startup can stay cached for several minutes.
Use the host ingress
The runtime chart can create an Ingress in front ofsandbox-host. Use it when the LangSmith cluster cannot reach the sandbox node IPs, or when that plain-HTTP traffic would leave a private network you trust.
To route control-plane traffic through the host ingress:
-
In the runtime values, enable
sandboxHost.ingresswith a hostname and TLS: -
In the
langsmithvalues, set the ingress origin, with no path or trailing slash:
19190, so keep that network private.
The Ingress routes only /internal/SandboxHost/ (host calls) and /guest-operation/ (exec, files, tunnels, sandbox links, and browser and service URLs). It does not route /metrics or /readyz; scrape and probe those on each sandbox-host pod at port 19190. Before you rely on the ingress, check:
- Clients: The LangSmith
platform-backendandingest-queuepods connect to the ingress, and so do the sandbox hosts, which dial it for sandbox links. If the ingress restricts source addresses, allow both. The sandbox hosts must also resolve the ingress hostname and trust its certificate. If a private CA signs it, add the CA to the runtime chart’scustomCaand to thelangsmithchart’sconfig.customCa. - Health checks: Load balancers check the
sandbox-hostpods directly, and many check/or the Ingress path by default, which returns404. Set the health check path to/readyz. With the AWS Load Balancer Controller, addalb.ingress.kubernetes.io/healthcheck-path: /readyzandalb.ingress.kubernetes.io/target-type: iptosandboxHost.ingress.annotations, because the Service isClusterIP. On GKE, leaveingressClassNameempty, set thekubernetes.io/ingress.class: gceannotation, and attach aBackendConfigwithhealthCheck.requestPath: /readyzandhealthCheck.port: 19190throughsandboxHost.ingress.service.annotations(cloud.google.com/backend-config). - Timeouts: LangSmith waits up to 310 seconds for an exec request. Exec streams, tunnels, sandbox links, and browser sessions are long-lived WebSocket or server-sent event connections. Set the controller’s read, idle, and backend timeouts above 310 seconds, and higher if long sessions must stay open. Defaults of 30 to 60 seconds cut them off. On GKE, set
timeoutSecin the sameBackendConfig. - Request size: File uploads go through the ingress. Raise the controller’s body size limit to at least your largest upload. On ingress-nginx, set
nginx.ingress.kubernetes.io/proxy-body-size, which defaults to1m. - WebSockets: The controller must allow WebSocket upgrades on Ingress routes. Azure Application Gateway for Containers supports WebSockets only through the Gateway API, so it cannot serve this Ingress. Use another controller, or leave
sandboxHost.ingressdisabled and route the two paths to port19190of thesandbox-hostpods with your own Service and Gateway APIHTTPRoute.
Create the runtime Secret
The runtime reads its credentials from a Secret that you create in the sandbox namespace. Set its name inconfig.existingSecretName. The Secret holds two values, copied from the LangSmith side:
- Sandbox service-auth secret: Signs calls between the control plane and the sandbox hosts. Store it under
sandbox_service_auth_secret, or the key thatserviceAuth.secretKeynames. - Callback signing JWK: The Ed25519 private JWK that signs sandbox callbacks. Store it under
sandbox_callback_signing_jwk, or the key thatconfig.callbackSigningJwkSecretKeynames.
With an external runtime, the Helm install notes of the
langsmith release print the key names for your configuration.
api_key_salt: that value also salts LangSmith API keys and signs other LangSmith service calls, so it must not leave the LangSmith cluster. With an external runtime, the langsmith chart refuses to render without a dedicated value. Generate one, for example with openssl rand -hex 32, and set it on the control plane:
- Chart-managed Secret: Set
sandboxes.serviceAuth.secret. It must differ fromconfig.apiKeySalt. config.existingSecretName: Add a key with the new value to your Secret, and name it insandboxes.serviceAuth.secretKey.
api_key_salt itself: that invalidates every existing API key. To change the service-auth value of a running installation, follow Rotate the service-auth secret.
Generate a callback signing JWK
If thelangsmith release has no callback signing JWK yet, generate one. Set it on the control plane as sandboxes.callbackSigningJwk, or as the sandbox_callback_signing_jwk key in config.existingSecretName, and copy the same value into the runtime Secret.
To generate an Ed25519 private JWK with OpenSSL 1.1.1 or later, run:
openssl genpkey reports Algorithm ED25519 not found, your openssl is an older LibreSSL build. Install OpenSSL and run the command with it.
Install with Helm
Install thelangsmith release first, then the langsmith-sandbox release. The examples use the namespace langsmith-sandbox and the release name langsmith-sandbox.
1
Configure the LangSmith release
In the LangSmith cluster, add the sandbox settings to With an external runtime, the chart ignores
langsmith_config.yaml, along with the callback signing JWK described in Create the runtime Secret:sandboxes.juicefs, sandboxes.sandboxHost, and sandboxes.proxyCa. It fails to render when sandboxes.authProxy.httpProxy or sandboxes.juicefs.redis.iamAuthProvider is set; configure those in the runtime chart.Apply the chart:2
Prepare the sandbox cluster
Label and taint the sandbox nodes, open the network paths, and create the namespace and the runtime Secret:To keep the JuiceFS Redis URL and its password out of your values file, also create a JuiceFS configuration Secret with the keys
name, metaurl, storage, and bucket, and set juicefs.existingSecretName instead of the inline juicefs values. On Azure, access-key holds the storage account name.3
Write the runtime values
Create Leave
langsmith_sandbox_config.yaml. Replace the placeholders with your values.- EKS
- GKE
- AKS
images.sandboxHostImage.tag empty. The chart then runs the sandbox-host image for its appVersion, the LangSmith release it pairs with.4
Install the runtime chart
Find the newest Install that exact version:
langsmith-sandbox chart whose appVersion equals the appVersion of the langsmith chart you installed:Install with Argo CD or Flux
Manage the two releases as two applications, one for each cluster:- Order: Deploy the
langsmithrelease first. Add the runtime after thelangsmithapplication has synced and is Healthy (Flux: the HelmRelease isReady). - Versions: Pin
langsmith-sandboxto the exact chart version whose appVersion matches. Never use a range,*, or*-0, and never omitversionin a Flux HelmRelease. - Proxy CA with Argo CD: Set
proxyCa.mode: existingSecretand pointproxyCa.existingSecretNameat a TLS Secret you create in the runtime namespace, with keystls.crtandtls.key, for examplekubectl create secret tls <name> --cert=ca.crt --key=ca.keywith a CA certificate you generate. The defaultgeneratedSecretmode reuses its CA through Helmlookup, which Argo CD and other tools that render without cluster access cannot run, so each render produces a new CA. Flux runs Helm in the cluster, so the default mode works there. - Install notes: Argo CD and Flux do not show Helm install notes. The rules that the notes print are on this page.
- Back to the Helm CLI: To stop managing the runtime with Flux, set
spec.suspend: trueon its HelmRelease before you delete it; deleting an active HelmRelease uninstalls the release. Helm 4 applies changes server-side, so the firsthelm upgradeafterwards fails with conflicts on fields that Flux’shelm-controllerowns. Run that one upgrade with--force-conflicts.
Verify the installation
Check that both releases run the same LangSmith release. TheAPP VERSION columns must match:
helm list shows nothing for an Argo CD install, or a stale record for a release that Helm installed before Argo CD took it over. Compare the image tags instead. Both must end in the same release:
NoSandboxHostsAvailable, the control plane has no ready hosts. Check that the sandbox nodes reach config.endpoint, which the hosts use to register, and the other paths in Configure the network.
The same error appears when the two releases hold different service-auth secrets: LangSmith and the sandbox hosts then reject each other’s calls. Compare the two values by checksum, without printing them:
<langsmith-app-secret> is the chart-managed Secret (langsmith-secrets for a release named langsmith) or your config.existingSecretName, and <service-auth-key> is sandbox_service_auth_secret or the key that sandboxes.serviceAuth.secretKey names. The two checksums must match. After you correct a value, restart the Deployments that read it on that side, listed in Rotate the service-auth secret, because Kubernetes does not restart pods when Secret data changes. The previous-secret settings only cover a planned rotation, not values that drift apart.
Run one runtime per installation
Run exactly onelangsmith-sandbox release for each LangSmith installation, and only while the langsmith release sets sandboxes.external.enabled: true. Running Sandboxes in more than one cluster for the same installation is not supported.
LangSmith accepts host reports from one sandbox runtime at a time, counting the langsmith release’s embedded runtime as one. While one runtime reports, LangSmith refuses any other: the refused runtime’s hosts never join the host pool, and the accepted runtime’s Sandboxes keep running. A refused runtime shows up in three places:
- Its elected
sandbox-hostpod turns not ready about 30 seconds after its first refused report and stays not ready. Ahelm install --waitorkubectl rollout statusthat finishes within the first seconds can still report success, so check the pod again after a minute. - Its
sandbox-hostlogs repeatthe platform refused this sandbox runtime because another runtime already reports to the same LangSmith install, with the error codesandbox_runtime_conflict, every 15 seconds. - LangSmith logs
refused host report from a second sandbox runtime, with the instance IDs of the accepted and the refused runtime.
sandboxes.clusterName only when you first install Sandboxes. Renaming it later hides existing Sandboxes, private registries, and host records.
Watch for a release mismatch
LangSmith compares its own release with the releases that its sandbox hosts report. When they drift apart, it warns Organization Admins and Organization Operators under Notifications in the sidebar of the LangSmith UI:
Apart from Sandbox hosts stopped reporting, no warning appears in the first 15 minutes after LangSmith moves to a new release, while its own rollout finishes. Each warning names the release to install. To act on it, upgrade the
langsmith-sandbox release to the chart that pairs with your langsmith release, or the langsmith release when the hosts are newer. Dismissing a warning hides it for 24 hours, or until the releases change.
Move an existing installation to a separate cluster
To move an installation from the embedded runtime to a separate cluster, switch thelangsmith release first, then install the runtime against the same JuiceFS metadata store and bucket. In the reverse order, LangSmith refuses the new runtime while the embedded one still reports. Sandboxes are unavailable between the two steps. Afterwards, existing Sandboxes and snapshots keep working.
1
Prepare the sandbox cluster
Meet the requirements and open the network paths. The sandbox cluster must reach the existing JuiceFS Redis and bucket, and have the storage grants for them. Create the runtime Secret with a dedicated service-auth secret and the callback signing JWK the control plane uses today. If the control plane still uses
api_key_salt as its service-auth secret, generate a new value. If it already uses a dedicated value, keep that one.2
Copy the proxy CA
Sandboxes that resume from saved memory keep trusting the egress proxy CA they started with. To keep auth proxy credential injection working for them, run the new runtime with the same CA. Copy the proxy CA Secret (
smithbox-proxy-ca by default, with keys tls.crt and tls.key) into the sandbox namespace, and set proxyCa.mode: existingSecret and proxyCa.existingSecretName in the runtime values. Copy it before the next step, which deletes a CA Secret that the langsmith chart generated:3
Switch the LangSmith release
Set The label assumes the release name
sandboxes.external.enabled: true, set the same dedicated service-auth secret as in the runtime Secret (sandboxes.serviceAuth.secret, or a key named in sandboxes.serviceAuth.secretKey), and upgrade the langsmith release. Keep sandboxes.clusterName unchanged. The release removes the embedded sandbox-host Deployment, and each host suspends its Sandboxes as it shuts down. A draining host first tries to live-migrate each running Sandbox to another ready host, but no other host is ready during this step, so every running Sandbox is suspended. Wait until the old host pods are gone:langsmith. Use a timeout longer than the hosts’ terminationGracePeriodSeconds.4
Install the runtime
Install
langsmith-sandbox as in Install with Helm, with juicefs settings that point at the same metadata Redis URL, bucket, and juicefs.name as before. LangSmith accepts the new runtime about 6 minutes after the embedded hosts stop reporting. Until then it refuses the new runtime, and the new host turns not ready shortly after it starts, even if helm --wait already returned. Wait until the host stays ready, then verify the installation. Stopped Sandboxes start again on the new hosts when a user or API action starts or wakes them.sandbox-host. Remove it when you no longer need it, along with the JuiceFS configuration Secret named in sandboxes.juicefs.existingSecretName, if you created one.
Rotate the service-auth secret
Rotate the sandbox service-auth secret in three steps. After each step, apply both releases and wait for both to finish rolling out before you start the next one:- On both sides, add the new value as the previous secret. In the
langsmithchart, setsandboxes.serviceAuth.previousSecret, or withconfig.existingSecretName, add a key and name it insandboxes.serviceAuth.previousSecretKey. In the runtime chart, add a key to the runtime Secret and name it inserviceAuth.previousSecretKey. - On both sides, make the new value current and the old value the previous secret. If you edit an existing Secret in place, this step changes no chart values, so restart the Deployments that read the Secret instead of applying the releases.
- On both sides, remove the previous secret. With an existing Secret, first unset the previous-secret key setting and apply the release, then delete the key from the Secret: pods that reference a deleted key cannot start.
langsmith release only.
Kubernetes does not restart pods when the data in an existing Secret changes. After you edit a Secret in place, restart the Deployments that read it, for example with kubectl rollout restart. In the LangSmith cluster, <release>-backend, <release>-platform-backend, <release>-queue, and <release>-ingest-queue read the service-auth secret; in the sandbox cluster, <runtime release>-sandbox-host does. Restarting sandbox-host suspends its Sandboxes, as in a rolling upgrade. Creating a Sandbox can fail with No sandbox hosts are available. Retry shortly. for a few seconds after the rollout finishes, until the new hosts report to LangSmith.
The callback signing JWK has no previous-key setting. Keep it stable: changing it on a running installation makes callbacks fail until both sides use the new value.
Route egress through a forward proxy
To send sandbox egress through a corporate forward proxy, configure the runtime chart instead of thelangsmith chart:
- Proxy URL: Add the URL to the runtime Secret under the key
sandbox_egress_proxy_url, or the key thatauthProxy.httpProxy.urlSecretKeynames. The URL can carry credentials, so the chart reads it only from the Secret. Without the key, Sandboxes reach destinations directly. - Bypass list: Set
authProxy.httpProxy.noProxy. - Private CA: For an HTTPS proxy with a private CA, add the CA to
config.customCa.
Uninstall
Uninstall the runtime release first, then uninstall or reconfigure thelangsmith release. The hosts report to the control plane while they shut down, so remove them while the control plane still runs:
See also
- Enable Sandboxes
- Sandbox architecture and lifecycle
- Operate self-hosted Sandboxes
- Upgrade an installation
- Release policy
Connect these docs to your agent of choice via MCP for real-time answers.

