Documentation/docs/sandbox/docker-in-sandbox

#Docker in Sandbox

Docker in Sandbox lets a sandbox run a Docker daemon inside the sandbox. Use the dins template when tests, build steps, or agent tools expect a local Docker socket.

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 examples assume your workspace has a dins template. If claiming dins returns not found, ask your Sandbox0 operator to enable Docker in Sandbox for your environment.

The dins template provides Docker binaries and writable Docker state, but it does not start dockerd before the sandbox is claimed. Start /usr/local/bin/sandbox0-dockerd-entrypoint from a sandbox command before using docker.


Start Test Databases#

Claim a dins 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, "dins", 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", "/usr/local/bin/sandbox0-dockerd-entrypoint >/tmp/sandbox0-dockerd.log 2>&1 &", "until docker info >/dev/null 2>&1; do sleep 1; done", "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 -p 6379:6379 redis:7-alpine", "docker run -d --name test-postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=app_test -p 5432:5432 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", "/usr/local/bin/sandbox0-dockerd-entrypoint >/tmp/sandbox0-dockerd.log 2>&1 &", "until docker info >/dev/null 2>&1; do sleep 1; done", "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-dins-test .", "docker run --rm sandbox0-dins-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)

Operational Notes#

  • Containers launched by Docker are sandbox-local. Start test dependencies with -p and connect to 127.0.0.1 from commands inside the sandbox.
  • 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.
  • Pulling images can take longer than a single synchronous command request. Start long Docker setup commands asynchronously, then poll readiness with short commands.
  • Nested Kubernetes tools such as kind can run in a Docker in Sandbox environment, but node images and cluster data consume the sandbox's ephemeral disk.
  • 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.