A container can make an AI agent easier to deploy without making it safe to operate.
The failure is usually not the image. It is the boundary around the image: bind mounts, Docker socket access, shared networks, environment variables, and persistent volumes. If an agent can reach the host filesystem or reuse a broad credential after a rebuild, running in Docker is only a packaging choice.
This article gives a repeatable mount-and-credential audit for OpenClaw-style agent runtimes and other tool-using workers.
Start with a threat model
Write down what the agent needs and what it must never reach.
| Capability | Safer boundary |
|---|---|
| Read workspace files | Read-only mount, or a dedicated worktree |
| Write artifacts | One output directory, not the home directory |
| Call a browser | Separate browser service and disposable profile |
| Send messages | Narrow, revocable delivery credential |
| Control Docker | Never mount the Docker socket into the agent |
| Read host secrets | No host home, SSH, or cloud metadata mount |
The useful question is not whether the container starts. It is what a prompt-injected tool call can reach when the model makes the wrong request.
Defaults that deserve an explicit review
- A whole-project bind mount when the directory contains secrets or other repositories.
- /var/run/docker.sock mounted into the agent.
- host networking, which removes useful network separation.
- Passing every host environment variable through an env file.
- Reusing a long-lived browser profile across unrelated tasks.
- One opaque writable volume for both durable state and disposable cache.
These patterns are not automatically wrong in every development environment. They are risky because they make the effective permission set larger than the task.
A smaller Compose baseline
Start with a non-root UID, a read-only root filesystem, dropped capabilities, and a temporary directory for scratch space:
services:
agent:
image: example/agent:latest
user: "10001:10001"
read_only: true
cap_drop: ["ALL"]
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,size=256m
volumes:
- ./agent-workspace:/workspace:rw
- agent-state:/var/lib/agent:rw
environment:
AGENT_WORKSPACE: /workspace
DELIVERY_TOKEN_FILE: /run/secrets/delivery_token
secrets:
- delivery_token
secrets:
delivery_token:
file: ./secrets/delivery_token
volumes:
agent-state:
This is not a complete security policy. It is a boundary that can be inspected. Add outbound access through a small egress proxy or separate delivery worker when the agent does not need general network access.
For an OpenClaw deployment that must stay available, a managed runtime such as managed OpenClaw hosting on Ampere can reduce the amount of host maintenance you own. It does not remove the need to review mounts, credentials, browser profiles, or recovery behavior.
Test the boundary from inside the container
Do not stop at reading YAML. Run tests against the running process and record the result.
1. Check identity and capabilities
docker compose exec agent id
docker compose exec agent sh -c 'grep CapEff /proc/1/status'
docker inspect agent --format '{{json .HostConfig.CapDrop}}'
The process should not run as root and should not retain unnecessary Linux capabilities.
2. Enumerate mounts
docker inspect agent --format '{{range .Mounts}}{{println .Source "->" .Destination "rw=" .RW}}{{end}}'
Fail the deployment if a source is broader than the task requires, if a sensitive host path appears, or if a cache is accidentally writable.
3. Probe for host-control paths
docker compose exec agent sh -c 'test ! -S /var/run/docker.sock && test ! -d /root/.ssh && test ! -f /proc/1/root/etc/shadow'
A failed probe is not proof of compromise. It is proof that the boundary needs review.
4. Test credential replacement
Create a short-lived test credential with a unique identifier. Revoke it, restart the agent, and confirm that the old credential is rejected, the agent cannot read the host shell environment to find a replacement, logs record the denial without printing the secret, and a clean rebuild does not silently restore the revoked credential.
A credential file should have an owner, scope, expiry, and revocation procedure. Being in a secret store is not a procedure.
Persistence needs two inventories
Rebuildable: image layers, package caches, temporary browser data, exported logs, and generated files that can be recreated.
Durable: agent identity, pending work, idempotency keys, policy version, provider cursors, approved destinations, and encrypted credential references.
If both categories share one opaque volume, operators restore too much or too little. A clean-room restore should prove that durable state returns while stale browser cookies, abandoned locks, and revoked tokens do not.
Failure-injection checklist
Run this before calling the deployment recoverable:
- Delete the container while a tool call is running.
- Remove the workspace mount and verify the agent fails closed.
- Revoke the delivery credential during a retry.
- Restore the state volume into a new container with a different image digest.
- Start two workers against the same state volume.
- Interrupt the process after it records intent but before the external side effect.
- Interrupt it after the provider accepts the request but before the response is stored.
For every case, record confirmed, not started, or unknown. Unknown is a first-class state. Retrying it blindly is how a recovery script sends duplicate messages or repeats a destructive tool call.
A deployment gate for CI
A lightweight gate can catch regressions:
- no Docker socket mount;
- no host network mode;
- no sensitive host-path mounts;
- non-root user;
- dropped capabilities;
- explicit writable mounts;
- secrets passed by file or workload identity, not broad environment inheritance;
- state and cache volumes named separately;
- image digest recorded;
- restore and revocation tests attached to the release.
The goal is not to claim perfect isolation. It is to make the effective boundary visible, narrow, and testable.
If you build or host AI agents, follow for practical failure tests around state, identity, delivery, and recovery. The model is only one component. The container boundary decides what happens when the model, tool, or operator is wrong.
Top comments (0)