Set Up OpenShell on Kubernetes

View as Markdown

The OpenShell Helm chart is experimental and under active development. Templates, values, and defaults can change between releases. Do not use it in production.

Use the Kubernetes deployment when the gateway should run on a shared cluster, in a cloud environment, or as part of team infrastructure. The Helm chart handles PKI bootstrap, RBAC, sandbox namespace setup, and the gateway workload. It uses a StatefulSet by default for the SQLite database, and can render a Deployment when server.externalDbSecret points at an external database.

Prerequisites

Make sure the following are in place before you install.

PrerequisiteRequiredNotes
Kubernetes 1.29+ with RBAC enabledYesNo additional notes.
Helm 3.xYesNo additional notes.
Agent Sandbox controller and CRDsYesInstall before the OpenShell chart. Refer to Install Agent Sandbox.
cert-managerNoRefer to Managing Certificates. Use cert-manager only if you prefer it over the built-in PKI job.
Kubernetes Gateway APINoRefer to Ingress. Use it only for external access without port-forwarding.

Install Agent Sandbox

OpenShell uses the Agent Sandbox Kubernetes SIG project to provision sandbox pods. Install the Agent Sandbox controller and its CRDs on your cluster before installing the OpenShell Helm chart.

Apply the latest release manifest:

$kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/manifest.yaml

This creates the agent-sandbox-system namespace, installs the sandboxes.agents.x-k8s.io CRD, and starts the controller.

Air-gapped clusters: mirror the manifest above and the registry.k8s.io/agent-sandbox/agent-sandbox-controller image referenced inside it to your internal registry, then point the manifest’s image reference at your mirror before applying. You will also need to mirror the OpenShell gateway and sandbox images — see the chart’s image.repository value for the gateway and server.sandboxImage / server.supervisorImage for the sandbox runtime.

Confirm the controller pod is running before proceeding:

$kubectl -n agent-sandbox-system get pods

The controller pod should reach Running status within a few seconds. For cluster-specific setup instructions, including KinD and GKE walkthroughs, refer to the Agent Sandbox getting started guide.

Install OpenShell

1

Create the namespace

$kubectl create namespace openshell
2

Install the chart

Install from the OCI registry on GHCR. Replace <version> with the chart version you want to install.

$helm upgrade --install openshell \
> oci://ghcr.io/nvidia/openshell/helm-chart \
> --version <version> \
> --namespace openshell

To use the latest development build instead of a stable release:

$helm upgrade --install openshell \
> oci://ghcr.io/nvidia/openshell/helm-chart \
> --version 0.0.0-dev \
> --namespace openshell

The chart automatically generates PKI secrets on first install using pre-install Helm hooks. No manual secret creation is required.

3

Wait for the gateway to be ready

$kubectl -n openshell rollout status statefulset/openshell

If you set workload.kind=deployment, wait on the Deployment instead:

$kubectl -n openshell rollout status deployment/openshell
4

Connect to the gateway

For local evaluation, use a port-forward:

$kubectl -n openshell port-forward svc/openshell 8080:8080

The port-forward is for local evaluation only. For shared environments, expose the gateway through your ingress controller or access proxy. Refer to Ingress for an external access option.

5

Install the TLS client bundle

The chart generates an mTLS bundle for transport security. Kubernetes deployments do not use that bundle as user authentication; configure OIDC or a trusted access proxy as described in Access Control. For local port-forwarded access, copy the generated bundle so the CLI can verify the gateway certificate:

$mkdir -p ~/.config/openshell/gateways/k8s/mtls
$kubectl -n openshell get secret openshell-client-tls \
> -o jsonpath='{.data.ca\.crt}' | base64 -d > ~/.config/openshell/gateways/k8s/mtls/ca.crt
$kubectl -n openshell get secret openshell-client-tls \
> -o jsonpath='{.data.tls\.crt}' | base64 -d > ~/.config/openshell/gateways/k8s/mtls/tls.crt
$kubectl -n openshell get secret openshell-client-tls \
> -o jsonpath='{.data.tls\.key}' | base64 -d > ~/.config/openshell/gateways/k8s/mtls/tls.key

The server certificate SANs include localhost and 127.0.0.1, so hostname verification passes over the port-forward without extra flags.

6

Register with the CLI

In another terminal, register the gateway with the user authentication mode you configured and verify it is reachable. For example, with OIDC:

$openshell gateway add https://127.0.0.1:8080 --local --name k8s \
> --oidc-issuer https://your-idp.example.com/realms/openshell \
> --oidc-client-id openshell-cli
$openshell status

Configure Chart Values

The most commonly changed values are:

ValuePurpose
image.repository / image.tagGateway container image. Defaults to ghcr.io/nvidia/openshell/gateway:latest.
replicaCountNumber of gateway replicas. Leave at 1 unless you are explicitly testing multi-replica behavior.
workload.kindGateway workload controller. Use statefulset for SQLite or deployment with server.externalDbSecret.
workload.allowMultiReplicaStatefulSetAllow replicaCount > 1 with workload.kind=statefulset. Prefer Deployment for external database-backed multi-replica gateways.
server.sandboxNamespaceNamespace where sandbox pods are created. Defaults to the Helm release namespace when left empty.
server.externalDbSecretSecret containing a PostgreSQL connection URI in the uri key. Use when the database is managed outside the chart.
server.sandboxImageDefault sandbox image used when a sandbox does not specify one.
server.sandboxImagePullSecretsImage pull secrets attached to sandbox pods. Referenced Secrets must exist in the sandbox namespace.
server.grpcEndpointEndpoint that sandbox supervisors use to call back to the gateway. Must be reachable from inside the cluster.
server.appArmorProfileAppArmor profile requested for sandbox agent containers. Defaults to Unconfined.
server.disableTlsRun the gateway over plaintext HTTP. Use only behind a trusted transport.
server.auth.allowUnauthenticatedUsersAccept user-facing calls without OIDC or mTLS credentials. Use only for trusted local development or a fully trusted access proxy.
server.enableLoopbackServiceHttpEnable local plaintext HTTP for loopback sandbox service URLs. Defaults to true.
pkiInitJob.serverDnsNames / certManager.serverDnsNamesAdditional gateway server DNS SANs. Wildcard SANs also enable sandbox service URLs under that domain.
supervisor.sideloadMethodHow the supervisor binary is delivered into sandbox pods. Leave empty to auto-detect based on cluster version: clusters running Kubernetes 1.35 or later use image-volume (ImageVolume GA in 1.36); older clusters use init-container. Set explicitly to image-volume on Kubernetes 1.33 or 1.34 with the ImageVolume feature gate enabled, or to init-container to force the legacy path on any version.

Use a values file for repeatable deployments:

$helm upgrade --install openshell \
> oci://ghcr.io/nvidia/openshell/helm-chart \
> --version <version> \
> --namespace openshell \
> --values my-values.yaml

The chart defaults server.appArmorProfile to Unconfined because runtime/default AppArmor profiles can block the supervisor’s network namespace mount setup on AppArmor-enabled nodes. Set server.appArmorProfile to an empty string to omit the field, RuntimeDefault to force the runtime default, or Localhost/<profile-name> when you load and manage a localhost profile on each node.

To use private sandbox images, create a kubernetes.io/dockerconfigjson Secret in the sandbox namespace and reference its name:

$kubectl -n openshell create secret docker-registry regcred \
> --docker-server=registry.example.com \
> --docker-username="$REGISTRY_USER" \
> --docker-password="$REGISTRY_TOKEN"
1server:
2 sandboxImage: registry.example.com/team/openshell-sandbox:latest
3 sandboxImagePullSecrets:
4 - name: regcred

RBAC

The chart creates the following RBAC resources in the release namespace:

ResourceScopeName
ServiceAccountNamespaceopenshell
ServiceAccountNamespaceopenshell-sandbox (for sandbox pods)
Role + RoleBindingNamespaceopenshell-sandbox
ClusterRole + ClusterRoleBindingClusteropenshell-node-reader

The namespaced Role covers sandbox lifecycle and identity:

API GroupResourceVerbs
agents.x-k8s.iosandboxes, sandboxes/statuscreate, delete, get, list, patch, update, watch
""eventsget, list, watch
""podsget

The ClusterRole grants node inspection and token validation:

API GroupResourceVerbs
authentication.k8s.iotokenreviewscreate
""nodesget, list, watch

To use an existing ServiceAccount instead of creating one, set serviceAccount.create=false and supply its name:

$helm upgrade --install openshell \
> oci://ghcr.io/nvidia/openshell/helm-chart \
> --version <version> \
> --namespace openshell \
> --set serviceAccount.create=false \
> --set serviceAccount.name=my-existing-sa

The ServiceAccount must already have the Role and ClusterRole bindings described above.

Probes

The gateway exposes /healthz for process liveness and /readyz for dependency-aware readiness on the health port. The Helm chart wires both into Kubernetes probes:

  • startupProbe and livenessProbe use /healthz.
  • readinessProbe uses /readyz, which reflects the latest result of an in-process background database check.

Next Steps

  • To enable automatic certificate rotation with cert-manager, refer to Managing Certificates.
  • To expose the gateway externally without port-forwarding, refer to Ingress.
  • To configure OIDC or reverse-proxy authentication, refer to Access Control.
  • To create your first sandbox, refer to Manage Sandboxes.