SANDBOX/Docker in Sandbox

#Docker in Sandbox

Docker in Sandbox lets a Sandbox0 Cloud sandbox run a Docker daemon inside the sandbox. Use it when a coding agent or test workflow expects Docker commands such as docker run or docker build.

Typical uses:

  • Run Redis, PostgreSQL, or other service containers beside application tests.
  • Build a throwaway image from code generated inside the sandbox.
  • Run tools that require docker build, docker run, or docker compose style workflows.

/var/lib/docker is ephemeral. Images, containers, layers, and Docker volumes are discarded when the sandbox is deleted. Store source code and durable outputs in Sandbox volumes instead.

The built-in default template provides Docker binaries and writable Docker state, but it does not start dockerd before the sandbox is claimed. The examples below start Docker in a Sandbox0 Cloud-compatible mode before running containers.

Docker should run inside the sandbox boundary. Do not mount a host Docker socket into an agent sandbox just to make Docker commands work.


Cloud Usage Pattern#

For Sandbox0 Cloud, use host networking for service containers:

  • Start a sandbox-local Docker daemon with Docker-managed bridge networking disabled.
  • Run service containers with --network=host.
  • Connect to those services from commands in the same sandbox at 127.0.0.1:<port>.

Start Docker once per running sandbox:

bash
if ! docker info >/dev/null 2>&1; then rm -f /var/run/docker.sock /run/docker.sock /var/run/docker.pid /usr/bin/dockerd \ --host=unix:///var/run/docker.sock \ --data-root=/var/lib/docker \ --exec-root=/var/run/docker \ --storage-driver=vfs \ --iptables=false \ --ip6tables=false \ --bridge=none \ --ip-forward=false \ --ip-masq=false \ >/tmp/sandbox0-dockerd.log 2>&1 & until docker info >/dev/null 2>&1; do sleep 1; done fi

Start Test Databases#

Claim a default sandbox, start Redis and PostgreSQL with Docker, then run tests against localhost from inside the sandbox.

go
package main import ( "context" "fmt" "log" "os" "strings" "time" sandbox0 "github.com/sandbox0-ai/sdk-go" ) func main() { ctx := context.Background() client, err := sandbox0.NewClient( sandbox0.WithToken(os.Getenv("SANDBOX0_TOKEN")), sandbox0.WithBaseURL(os.Getenv("SANDBOX0_BASE_URL")), ) if err != nil { log.Fatal(err) } sandbox, err := client.ClaimSandbox(ctx, "default", sandbox0.WithSandboxHardTTL(1800)) if err != nil { log.Fatal(err) } defer client.DeleteSandbox(ctx, sandbox.ID) if _, err := sandbox.Cmd(ctx, "rm -f /tmp/sandbox0-test-databases.ready"); err != nil { log.Fatal(err) } startScript := strings.Join([]string{ "set -e", "if ! docker info >/dev/null 2>&1; then", "rm -f /var/run/docker.sock /run/docker.sock /var/run/docker.pid", "/usr/bin/dockerd --host=unix:///var/run/docker.sock --data-root=/var/lib/docker --exec-root=/var/run/docker --storage-driver=vfs --iptables=false --ip6tables=false --bridge=none --ip-forward=false --ip-masq=false >/tmp/sandbox0-dockerd.log 2>&1 &", "until docker info >/dev/null 2>&1; do sleep 1; done", "fi", "rm -f /tmp/sandbox0-test-databases.ready", "docker rm -f test-redis test-postgres >/dev/null 2>&1 || true", "docker pull redis:7-alpine", "docker pull postgres:16-alpine", "docker run -d --name test-redis --network=host redis:7-alpine", "docker run -d --name test-postgres --network=host -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=app_test postgres:16-alpine", "until docker exec test-redis redis-cli ping | grep -q PONG; do sleep 1; done", "until docker exec test-postgres pg_isready -U postgres >/dev/null; do sleep 1; done", "touch /tmp/sandbox0-test-databases.ready", "tail -f /dev/null", }, "\n") start, err := sandbox.Cmd(ctx, "sh", sandbox0.WithCommand([]string{"/bin/sh", "-lc", startScript}), sandbox0.WithCmdWait(false), sandbox0.WithCmdTTL(1800), ) if err != nil { log.Fatal(err) } defer sandbox.DeleteContext(ctx, start.ContextID) readyCommand := "test -f /tmp/sandbox0-test-databases.ready && printf READY" deadline := time.Now().Add(5 * time.Minute) for { result, err := sandbox.Cmd(ctx, "sh", sandbox0.WithCommand([]string{"/bin/sh", "-lc", readyCommand}), ) if err != nil { log.Fatal(err) } if strings.Contains(result.OutputRaw, "READY") { break } if time.Now().After(deadline) { log.Fatal("timed out waiting for test databases") } time.Sleep(2 * time.Second) } fmt.Println("REDIS_URL=redis://127.0.0.1:6379") fmt.Println("DATABASE_URL=postgres://postgres:[email protected]:5432/app_test?sslmode=disable") }

Use those URLs from commands that run inside the same sandbox:

bash
REDIS_URL=redis://127.0.0.1:6379 \ DATABASE_URL='postgres://postgres:[email protected]:5432/app_test?sslmode=disable' \ go test ./...

Build And Run An Image#

Docker build cache also lives under /var/lib/docker, so this is best for temporary test images rather than durable artifacts.

go
script := strings.Join([]string{ "set -e", "if ! docker info >/dev/null 2>&1; then", "rm -f /var/run/docker.sock /run/docker.sock /var/run/docker.pid", "/usr/bin/dockerd --host=unix:///var/run/docker.sock --data-root=/var/lib/docker --exec-root=/var/run/docker --storage-driver=vfs --iptables=false --ip6tables=false --bridge=none --ip-forward=false --ip-masq=false >/tmp/sandbox0-dockerd.log 2>&1 &", "until docker info >/dev/null 2>&1; do sleep 1; done", "fi", "cat > main.go <<'EOF'", "package main", "import \"fmt\"", "func main() { fmt.Println(\"hello from docker in sandbox\") }", "EOF", "CGO_ENABLED=0 go build -o hello main.go", "cat > Dockerfile <<'EOF'", "FROM scratch", "COPY hello /hello", "ENTRYPOINT [\"/hello\"]", "EOF", "docker build -t sandbox0-docker-test .", "docker run --rm --network=none sandbox0-docker-test", }, "\n") result, err := sandbox.Cmd(ctx, "sh", sandbox0.WithCommand([]string{"/bin/sh", "-lc", script}), ) if err != nil { log.Fatal(err) } if !strings.Contains(result.OutputRaw, "hello from docker in sandbox") { log.Fatalf("docker build test failed:\n%s", result.OutputRaw) } fmt.Print(result.OutputRaw)

Nested Kubernetes And kind#

Nested Kubernetes tools such as kind and k3d need more than basic Docker command support. They create Kubernetes node containers and expect Docker to support cgroup namespaces plus host-like container networking. If the command fails with an error such as cgroup namespaces aren't enabled in the kernel, that sandbox cannot run kind reliably even if ordinary docker run and docker build commands work.

For projects that require kind, run that part of the workflow in a VM, a CI runner, or another environment with full Docker bridge networking and cgroup namespace support. See the kind known issues for the cgroup namespace requirement.


Operational Notes#

  • Containers launched by Docker are sandbox-local. In Sandbox0 Cloud, use --network=host for Redis, PostgreSQL, nginx, and similar service containers, then connect to 127.0.0.1 from commands inside the sandbox.
  • If you run against an environment where Docker port publishing works, -p hostPort:containerPort is also fine. The Cloud examples use --network=host because it avoids relying on Docker bridge/NAT.
  • Use Sandbox Services when a containerized HTTP service needs to be reachable from outside the sandbox.
  • Docker state is not durable. Use Sandbox Volumes for source, generated files, or database dumps that must survive sandbox cleanup.
  • Docker state uses the default template's ephemeral storage limit. Hosted default sandboxes keep the standard 500m CPU and 2Gi memory baseline, so large builds should request larger sandbox resources.
  • Pulling images can take longer than a single synchronous command request. Start long Docker setup commands asynchronously, then poll readiness with short commands.
  • kind and similar nested Kubernetes tools require cgroup namespace and networking support beyond ordinary Docker builds. Use a VM or CI runner when those kernel features are not available.
  • Long-running containers count against the sandbox resource limits. Stop containers when a test run finishes if the sandbox will stay alive.

Next Steps#

Sandbox Services

Expose named sandbox ports through public HTTP service routes.

Volumes

Persist source code, test fixtures, and generated artifacts beyond sandbox runtime cleanup.