← All posts

Run Claude Code in Docker: Safer Agent Workflows

If you want to run Claude Code in Docker, use a dev container pattern with a bind-mounted repository, a non-root user, per-project Claude state, minimal host secrets, and explicit network rules. Treat it as a safer agent runtime for day-to-day development, not as a perfect security boundary.

That distinction matters. The productivity win comes from letting Claude Code run tests, edit files, install local tools, and use the same Linux environment every time. The risk comes from the same place: the agent can touch whatever the container can touch.

In practice, your job is to make the container useful enough that developers keep using it, while keeping the blast radius small enough that one bad prompt, dependency script, MCP server, or compromised project cannot casually reach the rest of the laptop.

Start with the right mental model

A Claude Code Docker setup is usually a development container. Your editor, shell tools, language servers, package managers, test runners, MCP stdio processes, and Claude Code run inside the container. Your project files still appear locally because the repository is bind-mounted into the container workspace.

That is the trade. You get consistent tooling and a cleaner place to run agent commands. You do not get a guarantee that host files are untouchable. Docker bind mounts are writable by default, so a process inside the container can create, modify, or delete files on the host path you mounted.

Concretely, if you mount your repo at /workspace, Claude Code can edit that repo. That is the point. If you also mount ~/.ssh, cloud credentials, a broad home directory, or a shared global Claude config, you have expanded the trust boundary.

Related reading: AI agent sandboxing patterns for coding agents.

How to run Claude Code in Docker without wrecking flow

The baseline I would standardize for a serious team is simple:

  • Run Claude Code inside a devcontainer, Docker Sandbox, or a thin custom image.
  • Bind-mount only the repository, not the developer's full home directory.
  • Run as a non-root user inside the container.
  • Store Claude Code state in a named volume scoped to the project.
  • Move .claude.json into that same volume with CLAUDE_CONFIG_DIR.
  • Use explicit network allow rules for Anthropic, package registries, Git hosts, and approved internal services.
  • Keep host secrets out of the container unless there is a documented exception.

Anthropic's current devcontainer guidance points in this direction. The official feature path is to add ghcr.io/anthropics/devcontainer-features/claude-code:1.0 to .devcontainer/devcontainer.json. That feature tag pins the installer, not necessarily the Claude Code release itself, because the CLI auto-updates by default unless your policy disables or pins it.

If you use Anthropic's reference container as a starting point, read it as an example, not a maintained base image. The reference setup uses a non-root node user, a workspace bind mount at /workspace, a named Claude config volume, and a firewall script started with extra network capabilities. That is useful design material, but your platform still owns patching, dependency choices, and image provenance.

The repo mount is the main host risk

The repository mount is where safety and productivity meet. Developers need Claude Code to edit real files, run local checks, inspect diffs, and commit changes. A writable bind mount gives them that. It also gives every process inside the container direct write access to the mounted host tree.

Keep the mount narrow. Mount the current repo, not ~/work. Do not mount the whole home directory for convenience. If a secondary path is only needed for reading docs, generated fixtures, or dependency caches, make it read-only where possible.

For higher-risk repos, consider a clone-mode workflow or a sandbox that starts from a copy and requires an explicit sync or review step before changes land on the host. That costs speed. It may be worth it for untrusted code, vendor drops, or security-sensitive repositories.

The rule is straightforward: anything mounted writable should be considered agent-writable.

Bypass mode needs tighter boundaries

Many teams reach for Docker because they want to use --dangerously-skip-permissions without giving an agent the full developer machine. That is reasonable, but the flag changes the risk model. It removes the review step before tool calls run.

Anthropic also documents an important constraint: Claude Code rejects bypass mode when launched as root. Run the CLI as a non-root user inside the container. This is good engineering anyway. It reduces accidental damage inside the image and aligns with how the official reference setup is structured.

Do not confuse non-root in the container with no host impact. If the non-root container user can write to the bind-mounted repo, it can still change host files in that repo. If it can read a mounted secret, it can still read that secret.

Related reading: Claude Code auto mode vs skip permissions.

Persist Claude auth without sharing one giant credential bucket

Claude Code state has a practical gotcha. The ~/.claude directory stores auth token, settings, and session history. But OAuth account state, personal MCP servers, project trust decisions, and global config keys also live in ~/.claude.json.

If you mount only ~/.claude, developers may still lose sign-in or trust state between container rebuilds. Anthropic's recommended pattern is to mount a named volume at ~/.claude and set CLAUDE_CONFIG_DIR to that same path, so .claude.json is kept in the volume too.

Use a separate volume per project or per devcontainer identity. The reference pattern uses a volume name based on the devcontainer id. That is safer than one shared global Claude state volume because a compromised or messy project gets a smaller credential surface.

Also decide what belongs in each Claude settings scope:

  • ~/.claude/settings.json for personal user settings inside the container volume.
  • .claude/settings.json for team-shared permissions, hooks, or plugin settings that should be reviewed in Git.
  • .claude/settings.local.json for personal per-repo settings that should normally stay out of Git.
  • Managed settings for enterprise policy, where your organization needs the highest precedence.

This is one of the places where platform ownership helps. If every team invents its own config persistence, you will get a mix of broken login flows, leaked local settings, and shared credentials that nobody intended to share.

Do not mount host secrets as the easy path

A common shortcut is to mount ~/.ssh, cloud credential directories, Git credential helpers, and personal config files into the container. It makes Git and deploy commands work. It also defeats much of the reason you moved the agent into Docker.

Prefer narrower options:

  • Use repo-scoped or short-lived tokens instead of personal long-lived credentials.
  • Use GitHub CLI or HTTPS tokens scoped for development tasks.
  • Use SSH agent forwarding only when the team understands the exposure.
  • Keep cloud credentials out unless the task truly needs them.
  • Move signing, release, and production deploy steps back to the host or CI when possible.

For Git identity, you usually need less than people think: a name, an email, and a way to authenticate pushes. Commit signing, credential helpers, and broad SSH keys can be separate policy decisions. Keep them separate.

Related reading: credential proxy patterns for AI coding agents.

Network policy should match the real toolchain

Network egress is the control that makes bypass mode more defensible. If Claude Code can run commands without prompts, then package downloads, API calls, MCP endpoints, telemetry, and browser automation all deserve explicit review.

Anthropic's reference firewall is a useful map. It preserves Docker DNS, allows DNS, SSH, localhost, GitHub IP ranges, Anthropic API access, npm registry access, selected telemetry and editor update hosts, and the host subnet. It then defaults input, forward, and output policies to drop, and verifies that a blocked site fails while GitHub API access works.

Do not copy that list blindly. Most real stacks need more: PyPI, Maven, NuGet, artifact stores, internal package mirrors, private Git hosts, databases, feature flag APIs, browser targets, or approved MCP endpoints. Some organizations need less, especially if SSH or host subnet access is too broad for the threat model.

Make network policy iterative. Start with the minimum known-good allowlist. Log blocked connections. Add domains only when a real build, test, or tool needs them. Docker Sandboxes troubleshooting describes the same operational pattern: outbound package or API failures often mean a missing network allow rule.

MCP and browser tools expand the boundary

MCP is powerful because it lets Claude Code talk to tools, databases, APIs, and browser automation. That also means each MCP server becomes part of the agent runtime.

Stdio MCP servers run as local processes inside the container. Their dependencies must be installed there. Remote MCP servers need network access to their domains. Both types can carry credentials. Both can pull in external content that creates prompt-injection risk.

In practice, separate MCP servers into three groups:

  • Approved project servers that can be declared in repo config and reviewed.
  • Personal local servers that should stay out of shared policy unless explicitly approved.
  • High-risk servers with production data, write access, or broad external content, which need stronger controls or a separate runtime.

Browser automation follows the same pattern. Teams commonly use Playwright or browser MCP servers installed inside the container rather than expecting Claude Code to control the host browser. That is cleaner, but it may require OS packages, browser downloads, shared memory settings, display support, or separate browser services.

Related reading: MCP security for AI coding agents.

Git problems are usually ownership problems

Containerized development often exposes Git ownership friction. If the host, container, WSL, or sandbox sees different owners for the same worktree, Git may refuse operations with a dubious-ownership or safe-directory error.

This is not a Claude Code bug. It is a normal side effect of shared filesystems and user identity boundaries.

Standardize the fix instead of letting every developer paste random commands. Align container UID and GID where your platform supports it. Configure safe directories narrowly. Document WSL-specific behavior if your team uses WSL clone mode. Decide whether commits and signing happen inside the container, through an agent-forwarded identity, or on the host after review.

Also test large repositories. Docker Sandboxes documentation notes that direct-mode filesystem operations can be slow in large repos, and that caching has tradeoffs. Platform teams should measure the repositories developers actually use, not only a small sample app.

Devcontainer, Docker Sandboxes, or a VM?

There is no single winner. Pick the runtime based on the risk and workflow.

Option Best fit Watch closely
Devcontainer Daily development with editor integration, language servers, normal tests, and team-managed config. Writable bind mounts, secret mounts, UID and Git ownership, network allowlists.
Docker Sandboxes Agent sessions where a sandbox template and policy workflow are a better fit than hand-rolled containers. Default bypass startup, separate Claude user config behavior, network policy tuning.
Custom docker run Small teams or CI-like tasks that need a thin repeatable wrapper. Ad hoc credential handling, missing editor integration, incomplete state persistence.
VM or remote workspace Hostile repos, sensitive data, regulated environments, or stronger isolation needs. Higher setup cost, slower feedback, more platform maintenance.

For most senior developers and platform teams, a devcontainer is the practical default. Move to a stronger boundary when the repo or data deserves it.

A practical baseline config checklist

Before you call a Claude Code Docker setup ready for team use, check these items:

  • The container runs Claude Code as a non-root user.
  • The workspace mount is limited to the intended repository.
  • Any extra mounts are justified, documented, and read-only where possible.
  • No host ~/.ssh, cloud credential directory, or full home directory is mounted by default.
  • Claude state is stored in a per-project named volume.
  • CLAUDE_CONFIG_DIR points at that volume so .claude.json persists with ~/.claude.
  • Shared Claude settings live in reviewed repo files, not hidden personal state.
  • Network egress starts from an allowlist and has a process for adding real toolchain domains.
  • MCP servers are approved by scope and credential level.
  • Git identity, push auth, safe-directory behavior, and commit signing are documented.
  • Browser automation runs inside the container or through an approved service, not through an accidental host dependency.
  • Bypass mode is allowed only in runtimes with the mount, secret, and network controls above.

The headline is simple: run Claude Code in Docker to make the agent's working environment smaller, clearer, and easier to govern. Keep the repo writable, because that is how developers get work done. Keep everything else intentional.

Get started

Deploy your fleet.

Put a fleet of sandboxed agents to work on your own infrastructure, provisioned in seconds and watched live from one console.

Get started →

Admin-provisioned · Self-host in one command · Your data never leaves your VM