Configure Claude Code Sandboxing in a Project Container#
Use this guide to create a sandbox configuration repository for Claude Code and include it in a project container build.
For the full list of quickstarts, see Quickstart Guides.
In this quickstart, you will:
Prerequisites - Verify you have what you need.
Key Concepts - Understand the configuration files and how they work.
Create a Sandbox Configuration Repository - Create a configuration repository with settings, hooks and skills.
Include the Configuration in the Build - Clone the repository into the container during the build.
Prerequisites#
Before starting, ensure that:
You have completed Install Claude Code in a Project Container.
You have a GitHub or GitLab account where you can create a public repository.
Key Concepts#
- settings.json
Configuration file that Claude reads on session start. Settings follow a precedence hierarchy: managed (server/MDM/file) > CLI args >
.claude/settings.local.json(gitignored) >.claude/settings.json(committed) >~/.claude/settings.json(user level). Arrays merge across scopes (concatenate and deduplicate). Scalars use the highest-priority scope. Deny always wins over allow at any level.- Sandbox Block
Block in
settings.jsonthat restricts what files and network endpoints Claude’s processes can access. Usesbubblewrapfor filesystem and network isolation andsocatfor proxied network access through domain allowlists. In container environments, bubblewrap runs in a reduced capability mode (enableWeakerNestedSandbox) that retains filesystem, network and process isolation but cannot mount a fresh/proc.- Permissions Block
Block in
settings.jsonthat gates Claude’s tool calls, including Bash. Rules are evaluated in order: deny > ask > allow, first match wins. Bash patterns are glob-style and shell-operator-aware. Read/Edit patterns use gitignore-style globs.Deny rules on Read and Edit only block Claude’s built-in file tools, not bash subprocesses. For example,
Read(./.env)blocks the Read tool but does not preventcat .env. Use sandboxdenyReadrules to enforce OS-level file access restrictions.- Hooks Block
Block in
settings.jsonthat defines commands or scripts that run on agent lifecycle events. Claude Code supports 26 hook events. Key events for containers includeSessionStart,PreToolUse,PostToolUse,UserPromptSubmit,PermissionRequestandStop. Hooks can block execution, modify tool input (viaupdatedInputin the output JSON) and override permission decisions. Matchers use regex syntax, not glob. See the full hooks reference.- Hook Scripts
Standalone bash scripts referenced in the hooks block in
settings.json. Easier to use for more complex logic or processes than the individual commands set insettings.json.- AI Workbench Skills Folder
A sub-directory of
~/.claude/skills/containing a skill package for AI Workbench. Has aSKILL.mdfile and areferences/folder with supporting documents about the project container and AI Workbench. Claude loads skills automatically when the activation condition in the skill’s frontmatter is met.
Create a Sandbox Configuration Repository#
- Step One: Create a public repository with the following structure.
The repository will be cloned to
~/.claude/inside the container.nvwb-claude/ ├── settings.json # Permissions, sandbox, hooks, model ├── hooks/ │ ├── startup-claude.sh # SessionStart: inject project context │ └── block-secrets-in-bash.sh # PreToolUse: block secret exfiltration └── skills/ └── ai-workbench-container/ ├── SKILL.md # Project container rules and constraints └── references/ └── config-files.md # AI Workbench file reference for Claude- Step Two: Create a settings.json file.
The settings file configures the model, process sandbox, permissions and hooks. Protect it from agent modification by including it in the sandbox
denyWritelist.{ "model": "claude-opus-4-6", "sandbox": { "enabled": true, "enableWeakerNestedSandbox": true, "allowUnsandboxedCommands": false, "filesystem": { "denyWrite": [ "~/.claude/settings.json", "~/.claude/skills/ai-workbench-container", "~/.claude/hooks", "~/.claude.json", "~/.claude/credentials.json", "~/setup.sh", "/project/.claude/settings.local.json", "/project/.claude/hooks" ], "denyRead": [ "~/.claude.json", "~/.claude/credentials.json" ] }, "autoAllowBashIfSandboxed": false }, "permissions": { "additionalDirectories": [ "~/", "/tmp/", "/project/" ], "ask": [ "Bash(git add *)", "Bash(git commit *)" ], "deny": [ "Bash(pip install *)", "Bash(pip3 install *)", "Bash(python -m pip install *)", "Bash(python3 -m pip install *)", "Bash(sudo *)", "Bash(docker *)", "Bash(podman *)" ] }, "hooks": { "SessionStart": [ { "matcher": "", "hooks": [ { "type": "command", "command": "$HOME/.claude/hooks/startup-claude.sh" } ] } ], "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "$HOME/.claude/hooks/block-secrets-in-bash.sh" } ] } ] } }
- ``model`` sets the default model for sessions in the container.
Users can override it per session.
- ``sandbox`` example block protects the agent’s own configuration and credentials from modification.
The
denyWritelist covers everything that defines the agent’s constraints. ThedenyReadlist blocks access to authentication tokens.autoAllowBashIfSandboxedis false so bash commands still require approval even with the sandbox active.enableWeakerNestedSandboxactivates the container-compatible sandbox mode. Filesystem, network and process isolation still apply; the only limitation is that/procis inherited from the container rather than mounted fresh.allowUnsandboxedCommandsis false to prevent Claude from retrying failed sandboxed commands without the sandbox.- ``permissions`` example block prevents commands that would modify the container environment.
Package installation, sudo and container runtime commands are denied. Git staging and committing require approval.
additionalDirectoriesextends scope to paths the container uses for configuration and mounted files.- ``hooks`` example injects project context at session start, blocks secret access and logs tool calls.
SessionStartoutputs the project spec, settings and GPU information into the session.PreToolUseblocks commands that reference secret environment variables fromspec.yaml.
Step Three: Create hook scripts.
The SessionStart hook checks whether the container is an AI Workbench project and injects context into the session. It checks the project repository and creates a
CLAUDE.mdfile and.claudesubfolder if they don’t exist. It also enters the project spec and settings, and reports GPU availability.#!/bin/bash # Check if Claude is in a project container if [ ! -f /project/.project/spec.yaml ]; then echo "Not in a Workbench project container; Ignore Workbench skills" exit 0 fi # Create CLAUDE.md if it doesn't exist mkdir -p /project/.claude if [ ! -f /project/CLAUDE.md ]; then cat << 'EOF' > /project/CLAUDE.md # Context for this project ## This is an AI Workbench Project - This is a container - There are relevant skills in `~/.claude/skills/ai-workbench-container` - The project repository is at `/project` EOF fi # Output project context cat << 'EOF' ==============Claude Settings Information=============== This is an AI Workbench Project container. The project Git repository is located at `/project`. The project structure can be found in `/project/.project/spec.yaml`. Use the Workbench skills in `~/.claude/skills/ai-workbench-container` EOF echo -e "This is the project spec.yaml file.\n" cat /project/.project/spec.yaml echo "These are the settings from ~/.claude/settings.json." cat ~/.claude/settings.json # Report GPU availability if command -v nvidia-smi &>/dev/null && nvidia-smi &> /dev/null; then echo -e "\nThere are GPUs available in this container.\n" nvidia-smi --query-gpu=index,name,memory.total --format=csv,noheader fiThe PreToolUse hook blocks bash commands that reference secret environment variables defined in spec.yaml. It parses the project’s
spec.yamlfor secret variable names and rejects any bash command that references them. It also blocks environment-dumping commands (env,printenv,set,declare -p) when secrets are present.#!/bin/bash INPUT=$(cat) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command') SPEC="/project/.project/spec.yaml" if [ ! -f "$SPEC" ]; then exit 0 fi # Extract secret variable names from spec.yaml SECRETS=$(python3 -c " import yaml, sys try: spec = yaml.safe_load(open('$SPEC')) secrets = spec.get('execution', {}).get('secrets', []) for s in secrets: print(s['variable']) except Exception: sys.exit(0) ") if [ -z "$SECRETS" ]; then exit 0 fi # Block env-dumping commands when secrets exist if echo "$COMMAND" | grep -qxE '\s*(env|printenv|set|declare -p|export -p)\s*'; then echo "Blocked: command dumps all environment variables" >&2 exit 2 fi if echo "$COMMAND" | grep -qE 'cat\s+/proc/self/environ'; then echo "Blocked: reading /proc/self/environ exposes secrets" >&2 exit 2 fi # Block commands that reference any secret by name while IFS= read -r secret; do if echo "$COMMAND" | grep -qF "$secret"; then echo "Blocked: command references secret variable '$secret'" >&2 exit 2 fi done <<< "$SECRETS" exit 0
- Step Four: Create the SKILL.md file.
The skill tells Claude about the constraints of the project container.
--- name: nvwb-project description: Apply when working in a Linux environment that has a `/project/.project/spec.yaml` file. This skill enforces rules about AI Workbench project and container structure, environment configuration, and rebuild/restart requirements. user-invocable: false --- # NVIDIA AI Workbench — In-Container Project Awareness ## What NOT to Do - Do NOT use `sudo` - Do NOT install packages - Do NOT edit `/project/.project/*` without consulting the user - Do NOT run `nvwb` commands (Workbench CLI not available here) - Do NOT print or log any secret environment variables listed in the `execution.secrets` section of `/project/.project/spec.yaml` ## What To Do - Tell the user to rebuild the container after editing: `requirements.txt`, `apt.txt`, `postBuild.bash`, `preBuild.bash` - Tell the user to restart the container after editing: `variables.env`, mount or app fields in `spec.yaml` - Tell the user to restart the Compose application after editing: `compose.yaml` or `docker-compose.yml` - For persistent storage, tell the user to add a mount (Claude cannot configure mounts from within the container)
- Step Five: Optionally, create a references/config-files.md file.
This gives Claude detailed knowledge about each AI Workbench configuration file. It documents location, format, edit constraints and effect of changes for files like
spec.yaml,apt.txt,requirements.txtand build scripts. See the full file in the template repository.- Step Six: Publish the repository.
Push the repository to GitHub or GitLab. It should be accessible from inside the project container during the build.
Include the Configuration in the Build#
- Step One: Add the clone to postBuild.bash.
Add the following command after the Claude Code installation commands from Install Claude Code in a Project Container.
# Clone your sandbox configuration to the ~/.claude folder git clone <url-to-your-config-repo> ~/.claude
- Step Two: Build the container.
Select Project Tab > Project Container > Build
- Step Three: Verify the configuration is in place.
Open a terminal in the container
Run
cat ~/.claude/settings.jsonto verify the settings file is presentRun
claudeto start a session and confirm the SessionStart hook outputs project context
Success: The sandbox configuration repository is cloned into the container at build time. Claude Code starts with your settings, hooks and skills in place.
Persisting runtime changes with a volume mount.
The build clones a fresh copy of the configuration every time. If you want runtime changes (such as conversation history) to persist across restarts, set a volume mount for
~/.claude/. Select Project Tab > Project Container > Mounts > Add with Type > Volume Mount and Target Directory set to the configuration directory. Note that volume mount contents take precedence over build output — to reset to the build-time state, remove the volume mount and rebuild.