Security
12 min

How We Sandbox OpenClaw Agents with Per-Tenant Docker Socket Proxies

OpenClaw agents need Docker access to run tools. We give them scoped access through per-tenant socket proxies instead of mounting docker.sock. Here is why and how.

Clawctl Team

Product & Engineering

How We Sandbox OpenClaw Agents with Per-Tenant Docker Socket Proxies

OpenClaw agents need Docker access. They spin up sandboxed tool-execution containers, run code, process files. Without Docker, half the agent capabilities disappear.

The default approach? Mount /var/run/docker.sock into the container.

This is root access to your host. Every container on the machine. Every volume. Every network. One prompt injection and the agent owns your server.

42,665 exposed OpenClaw instances on Shodan. Most of them had this exact setup.

We had to find a better way.

The Problem: Docker Socket = Root

When you mount docker.sock into a container, you are giving that container full Docker API access. The container can:

  • List and inspect every container on the host
  • Create new containers with host volume mounts
  • Execute commands in other containers
  • Pull and run arbitrary images
  • Access the host filesystem through volume mounts
  • Modify networks and expose ports

For a personal dev setup, this is fine. For a multi-tenant platform where each tenant runs an autonomous AI agent? This is a catastrophe waiting to happen.

One compromised agent can:

  1. Escape its container by creating a new container with -v /:/host
  2. Read other tenants' secrets by inspecting their containers
  3. Modify other tenants' agents by exec-ing into their containers
  4. Take down the host by removing critical containers

Our Solution: Per-Tenant Docker Socket Proxy

Instead of mounting docker.sock, each tenant gets a wollomatic/socket-proxy sidecar. The proxy sits between the agent and the Docker API, filtering every request.

┌─────────────────────────────────────────────────┐
│ Tenant A                                        │
│                                                 │
│  ┌──────────┐     ┌──────────────┐     ┌─────┐ │
│  │ OpenClaw │────▶│ Socket Proxy │────▶│Docker│ │
│  │ Gateway  │     │ (scoped API) │     │ API  │ │
│  └──────────┘     └──────────────┘     └─────┘ │
│                                                 │
│  Can only see: openclaw-sbx-tenant-a-*          │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│ Tenant B                                        │
│                                                 │
│  ┌──────────┐     ┌──────────────┐     ┌─────┐ │
│  │ OpenClaw │────▶│ Socket Proxy │────▶│Docker│ │
│  │ Gateway  │     │ (scoped API) │     │ API  │ │
│  └──────────┘     └──────────────┘     └─────┘ │
│                                                 │
│  Can only see: openclaw-sbx-tenant-b-*          │
└─────────────────────────────────────────────────┘

Each proxy is configured with regex-scoped API filtering. Tenant A's proxy only allows operations on containers matching openclaw-sbx-tenant-a-*. It cannot see, inspect, or modify Tenant B's containers.

How It Works

1. Proxy Configuration

Each tenant's Docker Compose includes a socket-proxy sidecar:

docker-proxy:
  image: wollomatic/socket-proxy:1
  restart: unless-stopped
  environment:
    # Read-only by default
    LOG_LEVEL: warn
    # Allow container lifecycle for sandbox operations
    ALLOW_START: 1
    ALLOW_STOP: 1
    ALLOW_RESTARTS: 1
    # Regex-scope to this tenant's sandbox containers only
    MATCH_CONTAINER_NAME: "openclaw-sbx-TENANT_SLUG-*"
    # Allow bind mounts only from tenant workspace
    ALLOW_BIND_MOUNT_FROM: "/data/tenants/TENANT_SLUG/workspace"
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
  healthcheck:
    test: ["CMD", "wget", "--spider", "-q", "http://localhost:2375/_ping"]
    interval: 10s
    timeout: 5s
    retries: 3

2. Gateway Configuration

The OpenClaw gateway connects to the proxy instead of docker.sock:

gateway:
  environment:
    DOCKER_HOST: tcp://docker-proxy:2375
  depends_on:
    docker-proxy:
      condition: service_healthy

No socket mount. No host access. The gateway only sees Docker through the proxy's filtered lens.

3. Network Isolation

Each tenant runs on an internal bridge network. The proxy and gateway communicate over this network. Only the gateway's Traefik labels are exposed to the external network for HTTP routing.

networks:
  internal:
    driver: bridge

What the Proxy Blocks

OperationRaw docker.sockSocket Proxy
List all containers❌ Only tenant's sandbox containers
Inspect other tenants❌ Regex filter rejects
Create container with host volume❌ Bind mount restricted to tenant workspace
Exec into other containers❌ Container name filter blocks
Pull arbitrary images❌ Disabled by default
Modify networks❌ Disabled by default
Stop/remove other containers❌ Scoped to tenant only

The Healthcheck Problem

Early on, we hit a race condition. The OpenClaw gateway starts, tries to create a sandbox container, gets "Docker is not available", and crashes. The Docker socket proxy wasn't ready yet.

The fix was a healthcheck on the proxy sidecar:

healthcheck:
  test: ["CMD", "wget", "--spider", "-q", "http://localhost:2375/_ping"]
  interval: 10s
  timeout: 5s
  retries: 3

Combined with depends_on: condition: service_healthy, the gateway waits until the proxy is actually serving requests before starting.

Simple fix. Took us a day to figure out. The gateway's error message ("Docker is not available") pointed at Docker, not at the proxy that sits in front of it.

GID Resolution on Kubernetes

On a standard Docker host, the socket proxy needs the Docker group GID to access docker.sock. We resolve it automatically:

function resolveDockerGid() {
  // Try getent first (works on EC2, standard Linux)
  // Fall back to DOCKER_GID env var (required in K8s — no socket/getent)
  // Last resort: GID 0 (will fail with permission denied)
}

On Kubernetes, there's no docker.sock and no getent command. The GID must come from an environment variable. We set DOCKER_GID: "988" in the K8s ConfigMap. Without this, every proxy sidecar gets GID 0 and fails silently with permission denied.

What This Doesn't Solve

The socket proxy is one layer. It prevents container escape and cross-tenant access. But a complete agent sandbox also needs:

  • Egress filtering — The agent can still make HTTP requests to any domain. We add domain allowlists at the network level.
  • Secret encryption — API keys must be encrypted at rest, not stored in environment variables readable by docker inspect.
  • Audit logging — Every agent action must be logged. The proxy doesn't log at the application level.
  • Human approvals — High-risk actions (sending emails, modifying databases) need human sign-off before execution.

The socket proxy is the foundation. The rest is built on top.

Why Not Just Use Firecracker/gVisor/Sysbox?

We evaluated all three:

  • Firecracker (microVM) — Strongest isolation. But operational complexity is extreme. Custom AMIs, no Docker Compose, limited ecosystem.
  • gVisor — Good compromise. But adds latency to every syscall. OpenClaw agents make thousands of syscalls during tool execution.
  • Sysbox — Unprivileged containers with nested Docker. Closest to what we need. But requires a custom EKS AMI and RuntimeClass configuration. On our backlog for future upgrade.

The socket proxy gives us 90% of the security benefit with 10% of the operational complexity. For a startup at our stage, that tradeoff is correct. We'll move to Sysbox when we need it.

Results

Since deploying per-tenant socket proxies in February 2026:

  • Zero cross-tenant access incidents
  • Zero container escape attempts (that succeeded — we log blocked API calls)
  • Gateway startup reliability went from ~85% to 99.5% (healthcheck fix)
  • Tenant provisioning time unchanged — the proxy adds <100ms to Docker operations

Try It

If you're self-hosting OpenClaw and want to add socket proxy isolation yourself, the key components are:

  1. wollomatic/socket-proxy — the proxy image
  2. Regex container name scoping — MATCH_CONTAINER_NAME environment variable
  3. Bind mount restrictions — ALLOW_BIND_MOUNT_FROM limits volume access
  4. Health-gated startup — depends_on: condition: service_healthy

Or skip the infrastructure work entirely. Clawctl deploys OpenClaw with socket proxy isolation in 60 seconds.

FAQ

What is a Docker socket proxy?

A Docker socket proxy is a reverse proxy that sits between a container and the Docker API socket. Instead of giving a container full Docker access via docker.sock, the proxy filters API requests — allowing only specific operations on specific containers. This prevents container escape and cross-tenant access.

Why can't you just use Docker's built-in security?

Docker's security model assumes trust within the Docker API. Any container with docker.sock access can control all other containers. Docker does not support per-container API scoping natively. The socket proxy adds this missing layer.

Is this the same as rootless Docker?

No. Rootless Docker runs the Docker daemon as a non-root user, reducing host-level privilege escalation. The socket proxy is orthogonal — it scopes API access at the request level. You can use both together for defense in depth.

How does this compare to Kubernetes pod security?

Kubernetes has PodSecurityPolicies and SecurityContexts that restrict pod capabilities. The socket proxy serves a similar purpose but works with Docker Compose, which is how OpenClaw deploys sandbox containers. On Kubernetes, we combine the socket proxy with pod-level security policies.

What happens if the socket proxy crashes?

The OpenClaw gateway loses Docker access and cannot create sandbox containers. Tool execution fails gracefully — the agent reports that Docker is unavailable. Our auto-recovery cron detects the unhealthy state and restarts the proxy within 5 minutes.

This content is for informational purposes only and does not constitute financial, legal, medical, tax, or other professional advice. Individual results vary. See our Terms of Service for important disclaimers.

Ready to deploy your OpenClaw securely?

Get your OpenClaw running in production with Clawctl's enterprise-grade security.