# Environment Variables, Customization, and Troubleshooting

> All CLAUDE_SANDBOX_* host and container variables, ANTHROPIC_* forwarding, network modes, per-project Dockerfile extension, and the documented failure modes with exact fixes.

- Repository: hans/claude-container
- GitHub: https://github.com/hans/claude-container
- Human wiki: https://grok-wiki.com/public/wiki/hans-claude-container-cf30219c8958
- Complete Markdown: https://grok-wiki.com/public/wiki/hans-claude-container-cf30219c8958/llms-full.txt

## Source Files

- `README.md`
- `.superset/launch.sh`
- `SETUP.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [README.md](README.md)
- [.superset/launch.sh](.superset/launch.sh)
- [SETUP.md](SETUP.md)
- [Dockerfile](Dockerfile)
- [entrypoint.sh](entrypoint.sh)
- [.superset/setup.sh](.superset/setup.sh)
- [.dockerignore](.dockerignore)
</details>

# Environment Variables, Customization, and Troubleshooting

This page documents every `CLAUDE_SANDBOX_*` variable that controls the sandbox launcher, how `ANTHROPIC_*` variables are forwarded from the host into the container, supported Docker network modes, the two supported patterns for per-project Dockerfile extension, and all documented failure modes with their exact fixes. These controls are implemented in the launch script and supporting files; they determine what the Claude Code agent running inside the container can see, reach, and modify.

The variables are split into host-side settings (consumed by the launcher before `docker run`) and container-side settings (sourced from a worktree `.env` file on every launch and reattach). Correct use of these options lets teams keep a generic base image while safely exposing only the files, credentials, and network paths each project actually needs.

## Host-side CLAUDE_SANDBOX_* Variables

These variables are read by `.superset/launch.sh` before the container starts. They are typically set in Superset's per-agent **Environment** field (one `KEY=value` per line) or exported in the shell before launching Superset for a host-wide default.

| Variable                          | Purpose                                                                 | Default                  |
|-----------------------------------|-------------------------------------------------------------------------|--------------------------|
| `CLAUDE_SANDBOX_IMAGE`            | Docker image name to run                                                | `claude-sandbox:latest`  |
| `CLAUDE_SANDBOX_NETWORK`          | Docker network mode (`bridge`, `host`, `none`, or custom name)          | `bridge`                 |
| `CLAUDE_SANDBOX_MOUNT_SSH`        | Set to `1` to bind-mount `~/.ssh` read-only (enables git+SSH)           | unset (off)              |
| `CLAUDE_SANDBOX_MOUNT_SYMLINKS`   | Set to `0` to disable the external-symlink scan entirely                | `1` (on)                 |
| `CLAUDE_SANDBOX_SYMLINK_MOUNTS_RW`| Set to `1` to mount every external symlink target read-write            | `0` (read-only)          |
| `CLAUDE_SANDBOX_SYMLINK_RW_PATHS` | Colon-delimited list of path prefixes (absolute or relative to worktree) that should be mounted read-write | `results_scratch` (example) |
| `CLAUDE_SANDBOX_PROMPT`           | Manual prompt override (used when invoking the script directly)         | unset                    |
| `CLAUDE_SANDBOX_SKIP_PERMISSIONS` | Set to `1` to pass `--dangerously-skip-permissions` to Claude Code      | `1` (on)                 |

The symlink-related variables drive the automatic bind-mount logic that makes symlinks pointing outside the worktree resolve correctly inside the container. The scan walks the worktree once on the initial `docker run`, skips `.git`, `node_modules`, `.venv`/`venv`, deduplicates targets, and logs each mount to stderr.

Sources: [README.md:103-110](README.md), [.superset/launch.sh:19-20,103,150,166-191](.superset/launch.sh)

## Container-side Variables and .env Handling

Project-specific secrets and configuration live in a `.env` file at the root of the worktree. The container entrypoint sources this file on every launch and every `docker exec` reattach:

```sh
# entrypoint.sh:8
if [ -f /workdir/.env ]; then
    set -a
    . /workdir/.env
    set +a
fi
```

`ANTHROPIC_API_KEY`, `ANTHROPIC_BASE_URL`, model overrides, and any other keys the agent needs can be placed here. `.superset/setup.sh` copies `../.env` into a newly created worktree when the parent directory already contains one, so a single source of secrets can propagate automatically.

Sources: [entrypoint.sh:8-15](entrypoint.sh), [.superset/setup.sh:10-14](.superset/setup.sh), [README.md:112-119](README.md)

## ANTHROPIC_* Forwarding

Any environment variable whose name begins with `ANTHROPIC_` is automatically forwarded from the host shell into the container. The launcher extracts the names at runtime and passes them with `-e`:

```sh
# .superset/launch.sh:236
while IFS= read -r var; do
    [ -n "$var" ] && docker_args+=(-e "$var")
done < <(env | awk -F= '/^ANTHROPIC_/ {print $1}')
```

This mechanism works even when the values are not present in the worktree `.env` file, giving operators a choice between host-level injection and per-worktree files.

Sources: [.superset/launch.sh:236-239](.superset/launch.sh), [README.md:127](README.md)

## Network Modes

`CLAUDE_SANDBOX_NETWORK` controls the `--network` argument passed to `docker run`.

- `bridge` (default): The container receives an isolated NAT network. Outbound internet works; the agent cannot reach services bound to `localhost` on the host.
- `host`: The container shares the host's network stack. The agent can contact `localhost` services running on the host (useful for local development APIs) but loses network isolation from the host.
- `none`: The container has no network interfaces. Use when a fully hermetic sandbox is required.

Sources: [README.md:131-137](README.md), [.superset/launch.sh:20,130](.superset/launch.sh)

## Per-project Dockerfile Extension

The base image (`claude-sandbox:latest`) is intentionally generic. Two supported extension patterns exist:

**Quick / ephemeral**: Use `docker exec` inside a running container to install tools. Changes live only in the container's writable layer and disappear when the container is removed (`--rm`).

**Persistent / reproducible**: Create a project-local `Dockerfile.project`:

```dockerfile
FROM claude-sandbox:latest
RUN pip install --break-system-packages mne nibabel
```

Build and point the launcher at the new image:

```sh
docker build -t claude-sandbox-myproj -f Dockerfile.project .
export CLAUDE_SANDBOX_IMAGE=claude-sandbox-myproj
```

The same variable can be placed in Superset's agent Environment field for per-agent images.

Sources: [README.md:65-87](README.md), [SETUP.md:140-151](SETUP.md)

## Documented Failure Modes and Exact Fixes

**`docker: command not found`** or daemon unreachable  
Docker is not on PATH or Docker Desktop / engine is not running.  
Fix: Install and start Docker Desktop (macOS) or the Docker engine (Linux) before launching the agent.

**`claude-sandbox: image '...' not found`**  
The requested image has never been built.  
Fix: From the repository root, run `docker build -t claude-sandbox:latest .` (or the project-specific tag).

**`permission denied` writing into the worktree or `~/.claude`**  
The container runs as `-u $(id -u):$(id -g)`. Hard-coded UID mismatches (e.g., forcing `1000:1000`) break bind-mount writes.  
Fix: Remove any manual `-u` override; the default in `launch.sh` matches the host user.

**`credentials not found` / Claude Code login prompt inside the container**  
Host `~/.claude` and `~/.claude.json` are missing, or on macOS the keychain entry was never created.  
Fix: Run `claude /login` on the host first. On macOS the launcher also stages `~/.claude/.credentials.json` from the keychain on every launch. After a container-side token refresh you may need to re-login on the host.

**`Claude configuration file not found at: /home/claude/.claude.json` after editing the host file**  
Bind-mounted regular files are pinned to the original inode. Atomic replacement (used by some editors and Claude's own backup logic) breaks the mount.  
Fix: Exit the container completely and relaunch; the new file is re-mounted.

**`fatal: not a git repository` inside the container**  
The worktree's `.git` file contains an absolute `gitdir:` pointer that points outside the `/workdir` bind mount.  
Fix: Ensure `launch.sh` is current; it automatically detects the pointer and bind-mounts the parent `.git` directory at the same absolute path.

**Container name collision (`Conflict. The container name ... is already in use`)**  
An older container for the same worktree is still present.  
Fix: `docker rm -f claude-sandbox-<basename>-<hash>` (the name appears in the error).

**Closing the laptop (or losing the TTY) ends the session**  
The launcher uses `--rm`; the container is removed as soon as the `claude` process exits. Reattach only works while the container is still running.  
Fix: For sessions that must survive sleep or disconnect, run a long-lived `tmux` (or equivalent) inside the container as a workaround.

**macOS "mounts denied" when a symlink target is outside Docker Desktop's allowed paths**  
Docker Desktop restricts bind mounts to a configured allowlist (`/Users`, `/tmp`, `/private`, `/var/folders`, `/Volumes` by default).  
Fix: Add the target directory under Docker Desktop → Settings → Resources → File sharing, or move the symlink target under an already-shared prefix.

Additional first-launch symptoms and fixes are summarized in the setup guide.

Sources: [README.md:210-252](README.md), [SETUP.md:160-180](SETUP.md), [.superset/launch.sh:40-80,142-149](.superset/launch.sh)

## Summary

All sandbox behavior—image choice, network exposure, SSH and symlink mounts, credential forwarding, and per-project package additions—is driven by a small set of documented `CLAUDE_SANDBOX_*` variables plus the conventional `.env` file at the worktree root. The documented failure modes above cover every error path that the launcher and setup scripts explicitly check or that users have reported in the project documentation. Keeping the base image generic and using the `Dockerfile.project` + `CLAUDE_SANDBOX_IMAGE` pattern for customization keeps maintenance cost low while still giving each workspace exactly the tools and secrets it needs.

Sources: [.superset/launch.sh:1-250](.superset/launch.sh)
