> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langchain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Enable Sandboxes on self-hosted LangSmith

> Configure Kubernetes nodes, storage, and Helm values for self-hosted LangSmith Sandboxes.

[Sandboxes](/langsmith/sandboxes) provide isolated environments for running code and exposing services. Install them when you need sandbox workloads or a feature that requires them, such as [Engine](/langsmith/engine-self-hosted).

<Info>
  Sandboxes require an [Enterprise](https://langchain.com/pricing) plan.
</Info>

Sandboxes are disabled by default. After installation, see [LangSmith Sandboxes](/langsmith/sandboxes) for user workflows in the LangSmith UI and APIs.

For the infrastructure model and production planning, see [Sandbox architecture](/langsmith/self-host-sandbox-architecture), [scaling and capacity](/langsmith/self-host-sandbox-scaling), and [upgrades and operations](/langsmith/self-host-sandbox-operations).

## Supported platforms

Self-hosted Sandboxes are supported on:

* Amazon Elastic Kubernetes Service (EKS)
* Google Kubernetes Engine (GKE)
* Azure Kubernetes Service (AKS)

<Note>
  Self-hosted Sandboxes on Azure require LangSmith Helm chart v17 (`0.17.x`).
</Note>

## Components

Enabling Sandboxes provisions the following resources:

* Sandbox runtime pods that run sandbox workloads on KVM-capable nodes.
* A JuiceFS metadata store backed by Redis and object storage backed by S3, GCS, or Azure Blob Storage.
* Optional wildcard ingress for services exposed from inside Sandboxes.

## Prerequisites

<Steps>
  <Step title="Install the base LangSmith platform">
    Install LangSmith on Kubernetes before enabling Sandboxes. See [Self-host LangSmith on Kubernetes](/langsmith/kubernetes).

    Sandboxes run in the same Kubernetes cluster and namespace as the LangSmith release.

    If your cluster cannot pull from public registries, also mirror the sandbox runtime image. See [Additional images for Sandboxes](/langsmith/self-host-mirroring-images#additional-images-for-sandboxes).
  </Step>

  <Step title="Add KVM-capable nodes">
    Your cluster must include dedicated nodes that can run nested workloads with Linux KVM available at `/dev/kvm`.

    These can be bare-metal machines or supported cloud instances with nested virtualization enabled. On AWS and GCP, use x86\_64 Linux instances that expose `/dev/kvm` to the sandbox runtime.

    <Warning>
      On EKS, the VPC CNI addon must be **v1.21 or later**. `v1.20.0` crashes on 8th-generation
      Intel instances (for example `m8i`): `aws-node` enters `CrashLoopBackOff`, the node reports
      `cni plugin not initialized`, and the managed node group eventually fails with
      `NodeCreationFailure: Unhealthy nodes in the kubernetes cluster`.
    </Warning>

    The default Helm scheduling values expect these nodes to have the following label and taint:

    ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    label:
      sandbox.langsmith.com/host: "true"
    taint:
      key: sandbox.langsmith.com/host
      value: "true"
      effect: NoSchedule
    ```

    If your nodes use different labels or taints, override `sandboxes.sandboxHost.deployment.nodeSelector` and `sandboxes.sandboxHost.deployment.tolerations`.
  </Step>

  <Step title="Configure JuiceFS storage">
    Sandboxes require JuiceFS-backed shared storage. You must provide:

    * A Redis-compatible metadata store.
    * An object storage bucket or bucket root.
    * A JuiceFS configuration Secret, or enough Helm values for the chart to create one.

    Use these object storage backends for `sandboxes.juicefs.storage` and `sandboxes.juicefs.bucket`:

    | **Platform** | **Storage value** | **Bucket format** |
    | - | - | - |
    | AWS | `s3` | Region-explicit HTTPS S3 endpoint, such as `https://bucket-name.s3.us-west-2.amazonaws.com` |
    | GCP | `gs` | GCS URL, such as `gs://bucket-name` |
    | Azure | `wasb` | Azure Blob Storage URL, such as `https://container-name.core.windows.net` |

    Do not use object-store subpaths in `sandboxes.juicefs.name`. Use a flat name, such as `sandbox-juicefs`. JuiceFS stores objects under that name inside the configured bucket.

    <Tip>
      For the Redis metadata store, we recommend setting `maxmemory-policy` to `noeviction`. This avoids evicting JuiceFS metadata under memory pressure. Monitor Redis capacity and scale it before it reaches memory limits.

      With `noeviction`, Redis writes can fail when the instance reaches max memory, so keep enough memory headroom for sandbox metadata growth.
    </Tip>
  </Step>

  <Step title="Configure sandbox secrets">
    Sandboxes need additional secret material for service-to-service authentication and callback signing.

    <Tabs>
      <Tab title="Using Kubernetes secrets (recommended)">
        If you use `config.existingSecretName`, add the sandbox keys to the same LangSmith app Secret. Do not set the secret values directly in Helm.

        ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
        stringData:
          sandbox_callback_signing_jwk: '<ed25519-private-jwk>'
          # Optional, only during service-auth secret rotation:
        ```
      </Tab>

      <Tab title="Using inline values">
        If the Helm chart manages your LangSmith app Secret, set the sandbox secret values directly in your config file. Avoid committing this file to version control.

        ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
        sandboxes:
          callbackSigningJwk: '<ed25519-private-jwk>'
        ```
      </Tab>
    </Tabs>

    The callback signing value must be an Ed25519 private JWK. Keep it stable across upgrades.
  </Step>

  <Step title="Choose a proxy CA mode">
    The sandbox egress authentication proxy uses this CA for TLS interception and credential injection.

    The chart supports two proxy CA modes:

    | Mode | Use when |
    | - | - |
    | `generatedSecret` | You want Helm to create a self-signed CA Secret. This is the default. |
    | `existingSecret` | You manage the CA Secret outside the LangSmith chart. The Secret can be created manually, by cert-manager, or by another external process. |

    In GitOps workflows that render manifests without live cluster access, prefer `existingSecret`. The `generatedSecret` mode uses Helm's live `lookup` behavior to reuse the generated Secret on upgrades; pure render workflows cannot read the live Secret and may produce new cert material on each render.
  </Step>
</Steps>

## Enable with Helm

Enable Sandboxes by setting Helm values directly, as shown in this section. On AWS and GCP, you can instead [enable Sandboxes with Terraform](#enable-with-terraform), which provisions the infrastructure and generates the Helm values.

Add the following values to your `langsmith_config.yaml`, along with the sandbox secret values described in the [Prerequisites](#prerequisites). Replace placeholders with your deployment-specific values.

<Tabs>
  <Tab title="AWS">
    ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    images:
      sandboxHostImage:
        tag: "<same-release-tag-as-your-langsmith-images>"

    sandboxes:
      enabled: true
      juicefs:
        name: "sandbox-juicefs"
        storage: "s3"
        bucket: "https://bucket-name.s3.us-west-2.amazonaws.com"
        redis:
          metaURL: "redis://redis-host:6379/1"
      sandboxHost:
        deployment:
          nodeSelector:
            kubernetes.io/arch: "amd64"
            sandbox.langsmith.com/host: "true"
        serviceAccount:
          annotations:
            eks.amazonaws.com/role-arn: "<role_arn>"
    ```
  </Tab>

  <Tab title="GCP">
    ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    images:
      sandboxHostImage:
        tag: "<same-release-tag-as-your-langsmith-images>"

    sandboxes:
      enabled: true
      juicefs:
        name: "sandbox-juicefs"
        storage: "gs"
        bucket: "gs://bucket-name"
        redis:
          metaURL: "redis://redis-host:6379/1"
      sandboxHost:
        deployment:
          nodeSelector:
            kubernetes.io/arch: "amd64"
            sandbox.langsmith.com/host: "true"
        serviceAccount:
          annotations:
            iam.gke.io/gcp-service-account: "<gsa_name>@<project_id>.iam.gserviceaccount.com"
    ```
  </Tab>

  <Tab title="Azure">
    ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    images:
      sandboxHostImage:
        tag: "<same-release-tag-as-your-langsmith-images>"

    sandboxes:
      enabled: true
      juicefs:
        name: "sandbox-juicefs"
        storage: "wasb"
        bucket: "https://container-name.core.windows.net"
        storageAccountName: "<storage-account-name>"
        redis:
          metaURL: "redis://redis-host:6379/1"
      juicefsFormatJob:
        labels:
          azure.workload.identity/use: "true"
      sandboxHost:
        deployment:
          labels:
            azure.workload.identity/use: "true"
          nodeSelector:
            kubernetes.io/arch: "amd64"
            sandbox.langsmith.com/host: "true"
        serviceAccount:
          annotations:
            azure.workload.identity/client-id: "<client_id>"
    ```
  </Tab>
</Tabs>

Apply the updated chart:

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
helm upgrade -i langsmith langchain/langsmith \
  --values langsmith_config.yaml \
  --version <version> \
  --namespace <namespace> \
  --wait
```

## Enable with Terraform

<div className="my-6 rounded-xl border border-gray-200 bg-gray-50 p-6 dark:border-gray-700 dark:bg-gray-900/40">
  The LangSmith Terraform modules can provision the required AWS and GCP infrastructure and generate the corresponding Helm values.

  <Tabs>
    <Tab title="AWS">
      In `modules/aws/infra/terraform.tfvars`, enable Sandboxes and configure the sandbox node capacity:

      ```hcl theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      enable_sandboxes = true
      redis_source     = "external"

      sandbox_juicefs_redis_instance_type             = "cache.m6g.large"
      sandbox_juicefs_redis_snapshot_retention_limit  = 7

      sandbox_host_node_count               = 1
      sandbox_host_instance_types           = ["m5d.metal"]
      sandbox_host_configure_instance_store = true

      sandbox_host_image_tag = "<same-release-tag-as-your-langsmith-images>"
      ```

      AWS Sandboxes require `redis_source = "external"`. The Terraform module:

      * Creates a dedicated ElastiCache Redis instance for JuiceFS sandbox metadata.
      * Configures that dedicated instance with the recommended `noeviction` policy.
      * Reuses the LangSmith S3 bucket for sandbox object storage.
      * Creates the JuiceFS configuration Secret.
      * Adds the expected node label and taint.

      The AWS setup script generates the sandbox service-auth secret, callback signing JWK, and dedicated JuiceFS Redis auth token through the normal SSM-backed setup flow. Run the infra setup script before applying Terraform if those values do not exist yet.

      If you deploy the Helm release with the Terraform app module, set the sandbox app values in `modules/aws/app/terraform.tfvars` as well:

      ```hcl theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      enable_sandboxes      = true
      chart_version          = "~0.17.0"
      sandbox_host_image_tag = "<same-release-tag-as-your-langsmith-images>"
      ```

      When `enable_sandboxes = true`, the Terraform app module requires an explicit LangSmith Helm chart v17 release and a sandbox runtime image tag.

      Run the normal AWS flow:

      ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      make apply
      make init-values
      CHART_VERSION="~0.17.0" make deploy
      ```
    </Tab>

    <Tab title="GCP">
      In `modules/gcp/infra/terraform.tfvars`, enable Sandboxes and configure a Standard GKE node pool:

      ```hcl theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      enable_sandboxes = true
      redis_source     = "external"

      gke_use_autopilot     = false
      enable_gcp_iam_module = true

      sandbox_juicefs_redis_memory_size       = 5
      sandbox_juicefs_redis_high_availability = true

      sandbox_host_node_count     = 1
      sandbox_host_min_node_count = 1
      sandbox_host_max_node_count = 5
      sandbox_host_machine_type   = "n2-standard-8"

      sandbox_host_image_tag = "<same-release-tag-as-your-langsmith-images>"
      ```

      GCP Sandboxes require `redis_source = "external"`. The Terraform module:

      * Creates a dedicated Memorystore Redis instance for JuiceFS sandbox metadata.
      * Configures that dedicated instance with the recommended `noeviction` policy.
      * Reuses the LangSmith GCS bucket for sandbox object storage.
      * Creates the JuiceFS configuration Secret.
      * Adds the expected node label and taint.

      The GCP setup script generates the sandbox service-auth secret and callback signing JWK through the normal Secret Manager setup flow. Run the infra setup script before applying Terraform if those values do not exist yet.

      If you deploy the Helm release with the Terraform app module, set the sandbox app values in `modules/gcp/app/terraform.tfvars` as well:

      ```hcl theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      enable_sandboxes      = true
      chart_version          = "~0.17.0"
      sandbox_host_image_tag = "<same-release-tag-as-your-langsmith-images>"
      ```

      When `enable_sandboxes = true`, the Terraform app module requires an explicit LangSmith Helm chart v17 release and a sandbox runtime image tag.

      Run the normal GCP flow:

      ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      make apply
      make init-values
      CHART_VERSION="~0.17.0" make deploy
      ```
    </Tab>
  </Tabs>
</div>

## Optional: enable service URLs

Set `sandboxes.serviceUrlBaseUrl` when users need browser or programmatic access to HTTP services running inside Sandboxes.

```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
sandboxes:
  serviceUrlBaseUrl: "https://sandbox-services.example.com"
```

This requires wildcard DNS and TLS for `*.sandbox-services.example.com`. When `ingress.enabled` is `true`, the chart also adds a wildcard ingress rule that routes these service URLs to the LangSmith platform backend.

Service URLs also require an Ed25519 private JWKS with a nonempty `kid` on its first key. Set `config.signingJwks`, or add `langsmith_signing_jwks` to your [existing LangSmith app Secret](/langsmith/self-host-using-an-existing-secret). This is separate from `sandboxes.callbackSigningJwk`. For key generation instructions, see [Configure a signing JWKS](/langsmith/langsmith-remote-mcp#enabling-remote-mcp).

Service URLs are required to [build and edit custom apps with chat](/langsmith/custom-apps#configure-self-hosted-chat), even though they are optional for other sandbox workflows.

## Verify the installation

After the upgrade completes, verify that the sandbox runtime pods and JuiceFS volumes are ready:

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
kubectl rollout status deployment/sandbox-host -n <namespace>
kubectl get pods,pvc -n <namespace>
```

Then run a sandbox smoke test:

1. Create a sandbox from a public image, such as a Python image.
2. Start a Python HTTP server inside the sandbox.
3. Snapshot the sandbox with memory enabled.
4. Create a new sandbox from the snapshot.
5. Verify that the HTTP server is still running in the restored sandbox.

## Upgrade notes

Sandbox runtime image changes roll out through the `sandbox-host` Kubernetes Deployment. The chart uses a no-surge rolling update strategy by default, so hosts are replaced one at a time.

During a normal Helm upgrade, a terminating host stops accepting new Sandboxes, attempts to save each running Sandbox's VM memory to JuiceFS, and then stops those VMs before the pod exits. This shutdown is bounded by the `sandbox-host` pod termination grace period, which defaults to 300 seconds. This is not live migration: Sandboxes on that host are interrupted during the restart.

Sandboxes are not proactively restarted. They start again when a user or API action starts the Sandbox, or when a request path wakes it. LangSmith then places the Sandbox on an available host and restores from the saved memory image if the shutdown capture completed. If the memory image is absent or incomplete, the Sandbox starts from the saved root filesystem.

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to your agent of choice via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/enable-self-hosted-sandboxes.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
