In the sandbox lesson, Claude Code restricted shell commands. A goes further: it is a Docker container your editor connects to — on your computer or in a cloud service such as GitHub Codespaces. The whole Claude Code process lives inside it: the file-editing tools, MCP servers, hooks. Commands run in the container, and edits reach your through a mount.
Three layers
Each layer builds on the one before:
| Layer | What it gives you |
|---|---|
| The Claude Code feature | the CLI inside any dev container |
A named volume at ~/.claude | your login survives a rebuild |
| The reference container | a regular user and a firewall — a place for running without prompts |
Layer 1: the feature
The feature is added with one line in .devcontainer/devcontainer.json:
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "lts" },
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
},
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y bubblewrap socat"
}
The catch is the :1.0 tag. It pins the feature’s install script, not Claude Code: the feature installs the latest release, which then updates itself. postCreateCommand adds bubblewrap and socat — without them, the Bash sandbox does not work on Linux.
Layer 2: a login that stays
Log in to Claude Code, rebuild the container — and claude auth status returns code 1. A rebuild keeps the project folder but throws away the home folder, and ~/.claude with it. Codespaces is the same: after Rebuild Container, /workspaces is still there, but the login is gone.
The fix is a Docker named volume at ~/.claude, plus the CLAUDE_CONFIG_DIR with the same path. The volume lives separately from the container, so the login and settings in it survive a rebuild:
"mounts": [
"source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
],
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/node/.claude"
}
If the login still disappears, check that the volume’s target and CLAUDE_CONFIG_DIR name the same path.
Layer 3: the reference container
The Claude Code repository contains a reference .devcontainer/ of three files: devcontainer.json, Dockerfile and init-firewall.sh. It adds:
- a regular user,
node, instead of root; - a firewall: outbound traffic goes only to the addresses listed in
init-firewall.sh.
This is the container the documentation supports for runs with --dangerously-skip-permissions. As root, the CLI does not accept that flag at all.
Before the first build:
- Read all three files.
init-firewall.shruns as root on every container start — as thepostStartCommand. - Add the login domains. Next to
api.anthropic.com, the list must includeclaude.aiandplatform.claude.com, or logging in and refreshing the login will hit the firewall. If you need PyPI or a private package registry, add those to the list too and rebuild. - Pin the version. In the
CLAUDE_CODE_VERSIONbuild argument, replacelatestwith a specific version, and add"DISABLE_AUTOUPDATER": "1"tocontainerEnv.
Checking the firewall is simple: inside the container, a request to example.com should fail, and one to api.github.com should go through.
When everything checks out, one unattended run inside the container — with the same limits as in the level on running autonomously:
claude -p "Add a one-line description of the repository at the top of README.md" \
--dangerously-skip-permissions --model sonnet --max-turns 10
Cleaning up
The volume outlives the container — and so does your login in it. When you are done, run claude auth logout inside the container, then delete the container and the volumes whose names start with claude-code-config- and claude-code-bashhistory- (docker volume ls, then docker volume rm). Delete a codespace separately: even stopped, it still takes up space.
Quick check
The Claude Code feature is added with the :1.0 tag. Which version of Claude Code will be in the container a month from now?
Careful
A container does not stop the project from taking everything available inside it, including the Claude Code credentials in ~/.claude. The Dev Containers extension in VS Code also forwards your Git credentials and SSH agent into the container, and the reference firewall allows GitHub — so a run can push in your name. Use a container only with repositories you trust, and never mount ~/.ssh or files with cloud keys into it. After a run without prompts, read .git/config, the list of .git/hooks and the .devcontainer diff — before rebuilding and before git commit.
Practice in the terminal