published on Monday, Jul 13, 2026 by Pulumi
Per-Developer Agent Sandboxes on GKE with Pulumi
published on Monday, Jul 13, 2026 by Pulumi
A section-by-section walkthrough of this example is in the Pulumi blog post Kubernetes Agent Sandbox: what it is and how to deploy it with Pulumi.
This example deploys the kubernetes-sigs/agent-sandbox project onto a Google Kubernetes Engine (GKE) cluster and gives each developer a kernel-isolated, disposable coding-agent environment. It stands up:
- A GKE Standard cluster with a dedicated GKE Sandbox (gVisor) node pool, so every agent pod runs under the gVisor userspace kernel instead of sharing the host kernel.
- The Agent Sandbox controller and CRDs (
Sandbox, plus theSandboxTemplate/SandboxClaim/SandboxWarmPoolextensions), installed straight from the upstream release manifests. - One
Sandboxper developer, each with its own persistent workspace, its own credentials, and a default-deny egressNetworkPolicythat keeps the agent off the node metadata server and the cluster’s private ranges. - A Tailscale operator and per-sandbox
Ingress, so each box gets a private MagicDNS name and a real certificate that only its owner can reach — no public IP, no load balancer.
The tailnet ACL and the sandboxes are generated from the same developers list, so “who may open
which box” cannot drift from “which boxes exist.”
Note: Agent Sandbox is pre-1.0 (SIG Apps). Pin
agentSandboxVersionto a real release tag.
Prerequisites
- Install the Pulumi CLI.
- A Google Cloud account with the
gcloudCLI on your path (part of the GCP SDK), and Pulumi connected to GCP. Use a throwaway project — these sandboxes are meant to be wrecked. - The
gke-gcloud-auth-plugininstalled, so the generated kubeconfig can authenticate to the cluster. - A Tailscale tailnet with two OAuth clients: one for the operator
(scopes:
devices:core,auth_keys), and one scoped to editing the ACL (scope:policy_file). See Tailscale OAuth clients. - A Claude Code OAuth credential blob to run inside the sandboxes (
~/.claude/.credentials.jsonand~/.claude.jsonfrom a machine where you’ve logged into Claude Code).
Warning — this example takes over your tailnet ACL. It manages the tailnet’s access-control policy as a Pulumi resource:
pulumi upoverwrites your existing tailnet ACL with a policy generated from thedeveloperslist, andpulumi destroyresets the ACL to Tailscale’s default. Run it against a tailnet you’re willing to have rewritten (a personal or test tailnet), not one whose ACL you rely on. See theoverwriteExistingContent/resetAclOnDestroysettings intailscale.ts.
Running the Example
Clone the repo, cd gcp-ts-agent-sandbox, and install dependencies:
$ npm install
Create a new stack:
$ pulumi stack init devSet the GCP project and zone:
$ pulumi config set gcp:project [your-gcp-project] $ pulumi config set gcp:zone us-central1-aSet the developer list — one sandbox is created per entry:
$ pulumi config set --path 'developers[0].name' ada $ pulumi config set --path 'developers[0].email' ada@example.comSet the Tailscale OAuth clients (both clients, as secrets):
$ pulumi config set --secret tailscaleOauthClientId [operator-client-id] $ pulumi config set --secret tailscaleOauthClientSecret [operator-client-secret] $ pulumi config set --secret tailscaleAclClientId [acl-client-id] $ pulumi config set --secret tailscaleAclClientSecret [acl-client-secret]Set the Claude Code credentials (as secrets):
$ pulumi config set --secret claudeCredentials "$(cat ~/.claude/.credentials.json)" $ pulumi config set --secret claudeJson "$(cat ~/.claude.json)"Deploy everything with
pulumi up. This provisions the cluster, the gVisor pool, the Agent Sandbox controller, the Tailscale operator, and one sandbox per developer, in a single gesture:$ pulumi upOnce it’s up, the tailnet URL for each sandbox is a stack output. Open it in a browser (as the Tailscale-authenticated owner) to land in a VS Code IDE with Claude Code installed:
$ pulumi stack output sandboxUrlsTo talk to the cluster with
kubectl:$ pulumi stack output kubeConfig --show-secrets > kc.yaml $ KUBECONFIG=kc.yaml kubectl get sandboxes
Configuration
| Key | Description | Default |
|---|---|---|
gcp:project | GCP project to deploy into | (required) |
gcp:zone | GCP zone | us-central1-a |
developers | JSON array of { name, email }; one sandbox each | (required) |
nodeMachineType | Machine type for the gVisor sandbox pool | e2-standard-4 |
sandboxNodeCount | Nodes in the gVisor sandbox pool | 1 |
systemNodeCount | Nodes in the default (controller/system) pool | 1 |
agentSandboxVersion | kubernetes-sigs/agent-sandbox release tag to install | v0.5.2 |
masterVersion | GKE control-plane version | latest available |
tailscaleOauthClientId/Secret | Tailscale operator OAuth client (secret) | (required) |
tailscaleAclClientId/Secret | Tailscale ACL-editing OAuth client (secret) | (required) |
claudeCredentials | Claude Code OAuth blob mounted into each sandbox (secret) | (required) |
claudeJson | Claude Code .claude.json onboarding stub (secret) | (required) |
Cleaning Up
$ pulumi destroy
$ pulumi stack rm dev
pulumi destroy also restores Tailscale’s default ACL, so the tailnet isn’t left governed by the rules
of a demo that no longer exists.
Learn More
For the reasoning behind each piece of this program — why a container alone isn’t a security boundary,
what the Sandbox CRD actually gives you, and how the gVisor node pool and the tailnet ACL fit together —
read Kubernetes Agent Sandbox: what it is and how to deploy it with Pulumi.
published on Monday, Jul 13, 2026 by Pulumi