> ## Documentation Index
> Fetch the complete documentation index at: https://e2b-docs-k3s-template.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Run Kubernetes with k3s

> Create a disposable Kubernetes cluster for agents, CI, previews, and integration testing.

The `e2b/k3s` template turns an E2B Sandbox into a preconfigured, single-node [k3s](https://k3s.io) cluster. `kubectl`, cluster DNS, networking, storage, and the standard k3s components are configured when the sandbox starts.

Use it when an agent or CI job needs a real Kubernetes API without provisioning or sharing a permanent cluster.

<Warning>
  The k3s template is in preview and is not yet battle-tested across every Kubernetes workload. It is intended for ephemeral development and testing workflows, not as a highly available environment for hosting production traffic.
</Warning>

## Quickstart

Create and connect to a k3s sandbox from the CLI:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create e2b/k3s
```

`kubectl` is already configured. On a fresh or resumed sandbox, wait for the node and cluster DNS objects to appear and become ready before deploying:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready node --all --timeout=120s
timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready pod \
  --namespace=kube-system \
  --selector=k8s-app=kube-dns \
  --timeout=120s
kubectl get nodes
kubectl get pods --all-namespaces
```

You can also create the sandbox with the standard E2B SDK:

<CodeGroup>
  ```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
  import { Sandbox } from 'e2b'

  const sandbox = await Sandbox.create('e2b/k3s', {
    timeoutMs: 15 * 60_000,
  })

  try {
    await sandbox.commands.run(
      `
        set -euo pipefail
        timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
        kubectl wait --for=condition=Ready node --all --timeout=120s
        timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
        kubectl wait --for=condition=Ready pod \
          --namespace=kube-system \
          --selector=k8s-app=kube-dns \
          --timeout=120s
      `,
      { timeoutMs: 300_000 },
    )

    const nodes = await sandbox.commands.run('kubectl get nodes -o wide')
    console.log(nodes.stdout)
  } finally {
    await sandbox.kill()
  }
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
  from e2b import Sandbox

  sandbox = Sandbox.create("e2b/k3s", timeout=15 * 60)

  try:
      sandbox.commands.run(
          """
          set -euo pipefail
          timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
          kubectl wait --for=condition=Ready node --all --timeout=120s
          timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
          kubectl wait --for=condition=Ready pod \
            --namespace=kube-system \
            --selector=k8s-app=kube-dns \
            --timeout=120s
          """,
          timeout=300,
      )

      nodes = sandbox.commands.run("kubectl get nodes -o wide")
      print(nodes.stdout)
  finally:
      sandbox.kill()
  ```
</CodeGroup>

## Deploy and preview an application

This example creates an nginx Deployment and Service, forwards the Service to a sandbox port, and prints an E2B URL that you can open in a browser.

<CodeGroup>
  ```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
  import { Sandbox } from 'e2b'

  const sandbox = await Sandbox.create('e2b/k3s', {
    timeoutMs: 15 * 60_000,
  })

  await sandbox.commands.run(
    `
      set -euo pipefail
      timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
      kubectl wait --for=condition=Ready node --all --timeout=120s
      timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
      kubectl wait --for=condition=Ready pod \
        --namespace=kube-system \
        --selector=k8s-app=kube-dns \
        --timeout=120s
      kubectl create deployment web --image=nginx:alpine
      kubectl scale deployment web --replicas=2
      kubectl expose deployment web --port=80
      kubectl rollout status deployment/web --timeout=180s
    `,
    { timeoutMs: 300_000 },
  )

  await sandbox.commands.run(
    'kubectl port-forward --address=0.0.0.0 service/web 8080:80',
    { background: true },
  )

  await sandbox.commands.run(
    'until curl -fsS http://127.0.0.1:8080 >/dev/null; do sleep 1; done',
  )

  const url = `https://${sandbox.getHost(8080)}`
  console.log(`Open the application: ${url}`)
  console.log(`Stop it with: e2b sandbox kill ${sandbox.sandboxId}`)
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
  from e2b import Sandbox

  sandbox = Sandbox.create("e2b/k3s", timeout=15 * 60)

  sandbox.commands.run(
      """
      set -euo pipefail
      timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
      kubectl wait --for=condition=Ready node --all --timeout=120s
      timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
      kubectl wait --for=condition=Ready pod \
        --namespace=kube-system \
        --selector=k8s-app=kube-dns \
        --timeout=120s
      kubectl create deployment web --image=nginx:alpine
      kubectl scale deployment web --replicas=2
      kubectl expose deployment web --port=80
      kubectl rollout status deployment/web --timeout=180s
      """,
      timeout=300,
  )

  sandbox.commands.run(
      "kubectl port-forward --address=0.0.0.0 service/web 8080:80",
      background=True,
  )

  sandbox.commands.run(
      "until curl -fsS http://127.0.0.1:8080 >/dev/null; do sleep 1; done"
  )

  url = f"https://{sandbox.get_host(8080)}"
  print(f"Open the application: {url}")
  print(f"Stop it with: e2b sandbox kill {sandbox.sandbox_id}")
  ```
</CodeGroup>

The sandbox remains available for up to 15 minutes so you can open the URL. Kill it earlier with the command printed by the script.

To verify in-cluster DNS and ClusterIP routing, run a client pod against the Service:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
kubectl run cluster-check \
  --rm -i \
  --restart=Never \
  --image=curlimages/curl:latest \
  -- curl -fsS http://web
```

## Where to use it

* Give a coding agent an isolated cluster for a repository that expects Kubernetes.
* Validate Helm charts, operators, CRDs, manifests, Services, and DNS in CI.
* Create a cluster per pull request for integration tests or browser previews.
* Reproduce Kubernetes issues without provisioning EKS, GKE, or AKS.
* Test untrusted workloads without sharing a staging cluster.

The useful production pattern is **cluster per task**: create a sandbox, deploy and test the workload, collect the results, and remove the sandbox. Promote the same images, charts, and manifests to your managed production cluster after validation.

<Steps>
  <Step title="Create an isolated k3s sandbox">
    The agent, CI job, or preview service gets its own Firecracker microVM and Kubernetes control plane.
  </Step>

  <Step title="Deploy the production artifacts">
    Apply the same container images, Helm charts, CRDs, or manifests that will be promoted later.
  </Step>

  <Step title="Test the complete workload">
    Exercise Pods, Services, DNS, storage, and browser-visible endpoints without affecting a shared cluster.
  </Step>

  <Step title="Promote and clean up">
    Send validated artifacts to the production cluster, save any required test output, and kill the sandbox.
  </Step>
</Steps>

## Limitations

* The template runs one Kubernetes node in one sandbox. It does not provide high availability.
* A sandbox is disposable. Do not treat its local Kubernetes storage as a durable system of record.
* Multi-node clusters across sandboxes are not supported.
* Overlay CNIs that require VXLAN, Geneve, or macvlan are not supported. The template uses Flannel's `host-gw` backend.
* IPVS kube-proxy mode and nested-virtualization runtimes such as Kata Containers or KubeVirt are not supported.

For production application hosting, use a managed or otherwise highly available Kubernetes cluster with durable storage, backups, monitoring, and load balancing. Use E2B k3s to develop and validate what you deploy there.
