Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
features:
- claude-code
- claude-code-mounts
baseImage:
- debian:latest
- ubuntu:latest
Expand All @@ -24,6 +26,15 @@ jobs:
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli

- name: "Create test prerequisites"
run: |
mkdir -p ~/.claude/agents
mkdir -p ~/.claude/commands
mkdir -p ~/.claude/hooks
touch ~/.claude/settings.json
touch ~/.claude/CLAUDE.md


- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}'"
run: devcontainer features test --skip-scenarios -f ${{ matrix.features }} -i ${{ matrix.baseImage }} .

Expand Down
56 changes: 56 additions & 0 deletions src/claude-code-mounts/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Using Claude Code Mounts in devcontainers

## Requirements

This feature requires:

1. The Claude Code CLI to be installed (use the `claude-code` feature first)
2. Claude Code configuration files to exist on your host machine at `~/.claude/`
3. HOME environment variable to be properly set on the host system

Note: On Windows, ensure the HOME environment variable is set (e.g. using "Edit user environment variables" in the system settings).

## Recommended configuration

For most setups, we recommend using this feature together with the main Claude Code feature:

```json
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/anthropics/devcontainer-features/claude-code:1": {},
"ghcr.io/anthropics/devcontainer-features/claude-code-mounts:1": {}
}
```

## What gets mounted

This feature mounts the following Claude Code configuration files and directories from your host machine:

- `~/.claude/CLAUDE.md` - Global project instructions (read-only)
- `~/.claude/settings.json` - Claude Code configuration (read-only)
- `~/.claude/agents/` - Custom agent configurations (read-only)
- `~/.claude/commands/` - Custom command definitions (read-only)
- `~/.claude/hooks/` - Event-driven shell commands (read-only)

All mounts are read-only to prevent accidental modification from within the container.

## Installation order

This feature uses `installsAfter` to ensure it runs after:
1. Node.js feature (if used)
2. Claude Code feature

This ensures the Claude Code CLI is available before mounting configuration files.

## Troubleshooting

### Errors when building the dev container

- Ensure that mounted files and directories exist on host machine
- Check that the HOME environment variable is set correctly
- Ensure the devcontainer has been rebuilt after adding this feature

### Permission issues

- All mounts are read-only by design
- If you need to modify configuration, do so on the host machine and restart the container

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a github codespaces this is not possible (as far as I know), and this was my main motivation to use a devcontainer features. This use case most be consider here.

Copy link
Author

@DarkWanderer DarkWanderer Aug 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature is designed to be enabled in machine configuration rather than in project itself - on workstations, remote devboxes, CI agents etc.

For Codespaces, "host" configuration is ephemeral anyway and it does not make sense to mount it - so you just don't need this feature there

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Codespaces, "host" configuration is ephemeral anyway and it does not make sense to mount it - so you just don't need this feature there

I would like to persist the claude configuration after rebuilds, so in this case I believe it is ok if the mounts are rw. Thanks for the explanations, I see the issues now more clearly in a machine configuration.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I believe you are not the target audience of this feature. This is specifically for devcontainers on developer/CI machines, which can contain common secure state, including Claude configs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is actually the other way around. Devcontainer features can and are used in GitHub Codespaces as well, without exceptions. I was highlighting this limitation in your proposal, which is why this PR might not be accepted. It is not a proper devcontainer feature because it lacks general applicability.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.Features are optional by design, and every feature is not expected to work in every possible devcontainer environment
2.Not supporting a particular workflow you, in your words, would like to have is not "lack of general applicability".

Appreciate you taking the time to leave feedback, however, it unfortunately does not help to improve the proposal

20 changes: 20 additions & 0 deletions src/claude-code-mounts/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Claude Code CLI Mounts",
"id": "claude-code-mounts",
"version": "1.0.5",
"description": "Mounts the directories and files required for Claude Code",
"options": {},
"documentationURL": "https://github.com/anthropics/devcontainer-features/tree/main/src/claude-code",
"licenseURL": "https://github.com/anthropics/devcontainer-features/blob/main/LICENSE",
"installsAfter": [
"ghcr.io/devcontainers/features/node:1",
"ghcr.io/anthropics/devcontainer-features/claude-code:1"
],
"mounts": [
"source=${localEnv:HOME}/.claude/CLAUDE.md,target=/home/vscode/.claude/CLAUDE.md,type=bind,ro",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be enough one line with ${localEnv:HOME}/.claude folder?

"source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind,ro",

Btw, shouldn't it be rw instead of ro?

Copy link

@pablospe pablospe Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the claude-code repo there is a reference implementation, see these lines, maybe it helps:
"mounts": [
"source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
"source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
],

here: https://github.com/anthropics/claude-code/blob/e499db6e9edd1a6e8085b0392e62cec005fe9049/.devcontainer/devcontainer.json#L43C1-L46C5

Copy link
Author

@DarkWanderer DarkWanderer Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the simple solution worked, it would be in this PR. Unfortunately it does not, because of following issues:

anthropics/claude-code#4478
anthropics/claude-code#2350

Readonly mounts specifically address the security concern of escaping the devcontainer sandbox via prompt injection, which is stated in PR description

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification! One more question: if it is always read-only, how do you set the credentials the first time?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Credentials are not included, as I haven't figured out a way to make it work - including .credentials.json seems to not be enough. claude will prompt for setup on first use after container build/rebuild

Copy link

@pablospe pablospe Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you also need the ~/.claude.json, the userID seems to be important. You could do a quick test by starting the login from scratch with claude (in an empty devcontainer or so), and see what files the process generated. It works for me with the .credentials.json and the minimal ~/.claude.json (that one you would get starting from scratch).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the minimal ~/.claude.json:

{
  "numStartups": 1,
  "userID": "...",
  "projects": {},
  "oauthAccount": {
    "accountUuid": "...",
    "emailAddress": "...",
    "organizationUuid": "..."
  },
  "hasCompletedOnboarding": true
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the security concern of escaping the devcontainer sandbox via prompt injection

I wondering how such attack would work?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondering how such attack would work?

  • adding a hook that would exfiltrate code and/or SSH keys after every task completion.
  • adding an instruction to CLAUDE.md to download and run a script through |sudo /bin/bash pipe
  • any modification of agent's own security constraints (.claude/settings.json), followed by RCE

One such vulnerability was recently patched by GitHub, for example: https://embracethered.com/blog/posts/2025/github-copilot-remote-code-execution-via-prompt-injection/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you give a try to the minimal ~/.claude.json. I am curious how it went :)

"source=${localEnv:HOME}/.claude/settings.json,target=/home/vscode/.claude/settings.json,type=bind,ro",
"source=${localEnv:HOME}/.claude/agents,target=/home/vscode/.claude/agents,type=bind,ro",
"source=${localEnv:HOME}/.claude/commands,target=/home/vscode/.claude/commands,type=bind,ro",
"source=${localEnv:HOME}/.claude/hooks,target=/home/vscode/.claude/hooks,type=bind,ro"
]
}
7 changes: 7 additions & 0 deletions src/claude-code-mounts/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
set -eu

echo "Activating feature 'claude-code-mounts'"

mkdir -p $_REMOTE_USER_HOME/.claude
chown $_REMOTE_USER:$_REMOTE_USER $_REMOTE_USER_HOME/.claude