🛠️

Claude Code の開発用コンテナ設定(ターミナルから実行用)

に公開

はじめに

Anthropic 公式の Claude Code DevContainer 設定 は VS Code での利用を前提としています。私は VS Code を使っていないため、これをベースに以下の改善を加えました。

  • ターミナルから直接起動できるようにアレンジ
  • Node.js のバージョン自動同期
  • Rust/Gemini CLI などの追加ツール
  • 動的ファイアウォール管理
  • ホスト macOS で効果音再生

本記事では、公式版を拡張した scripts/claude_dev_container のカスタマイズ内容と実装方法を紹介します。

ファイル構成

公式版(Dockerfile、devcontainer.json、init-firewall.sh)をベースに、以下のファイルを追加・変更しました。

scripts/
├── claude_dev_container/
│   ├── claude-sandbox.sh          # ターミナル起動用スクリプト(新規)
│   ├── Dockerfile                 # 公式版を拡張(Rust等を追加)
│   ├── init-firewall.sh          # 公式版を拡張(JSON設定対応)
│   ├── claude_dev_container.json  # 許可ドメイン設定(新規)
│   └── play-sound.sh             # macOS 音声再生(新規)

1️⃣ ターミナルからの起動方法

目的

ターミナルから直接 Claude Code の開発環境を起動したい。

実装ポイント

  • ラップスクリプト claude-sandbox.sh 作成。以下実装イメージ
docker build

docker run --rm -it \
  --cap-add=NET_ADMIN --cap-add=NET_RAW \
  -v "$PWD":"$container_path" \
  -v "$HOME/.ssh":/home/node/.ssh:ro \
  -v "$HOME/.claude":/home/node/.claude \
  -e CLAUDE_CONFIG_DIR=/home/node/.claude \
  ...
  "$image_tag" \
  zsh -c "
      # Continue with normal startup
      sudo /usr/local/bin/init-firewall.sh && cd $container_path && claude --dangerously-skip-permissions $claude_args
  "

マウントを一括指定し、claude --dangerously-skip-permissions を実行します。

2️⃣ "現在のディレクトリ名" をそのままワークスペース名に

目的

Claude Code の履歴は 実行時のパスに依存する。
そのため、コンテナ内のルートディレクトリが同じだと正しく履歴参照できない
そのため 一意なマウントポイント を自動生成。

実装スニペット

current_path="$PWD"
relative="${current_path#$HOME/}"
workspace_name="workspace_${relative//\//_}"
container_path="/$workspace_name"
docker run ... -v "$PWD":"$container_path" -w "$container_path"

カレントパスが ~/src/foo/bar なら
コンテナ側は /workspace_src_foo_bar になります。

3️⃣ Node.js バージョンをプロジェクトごとに自動同期

目的

fnm.node-version で指定したバージョンを
Docker イメージタグに反映し、キャッシュを効かせる。

主要コード

# build()
if command -v fnm &>/dev/null; then
  node_version=$(fnm current | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
elif [[ -f ".node-version" ]]; then
  node_version=$(cat .node-version)
else
  node_version="20"   # fallback
fi

docker build \
  --build-arg NODE_VERSION="$node_version" \
  -t claude_devcontainer:node$node_version \
  -t claude_devcontainer .

お好みのバージョン管理ツールでアレンジしてください。

4️⃣ 追加インストール(Rust 関連ツール etc.)

目的

フロントは TS、バックエンドは Rust という構成でも
単一コンテナで開発したい。わざわざ切り替えるのは面倒、一度ビルドしてしまえば問題ない。

Dockerfile 抜粋

ENV CARGO_HOME=/home/node/.cargo
ENV RUSTUP_HOME=/home/node/.rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal \
 && rustup component add rust-analyzer clippy rustfmt

Tip: build-essential / pkg-config / libssl-dev
apt で入れているのでネイティブ依存の npm パッケージもビルド可。

5️⃣ Claude Code CLI & Gemini CLI ホストと同じ認証情報で使う

目的

  • 私は Claude Code に Gemini で検索させたりしている。コンテナ内でも認証情報へアクセスできるようにする。

手順

  1. ホスト側
# ~/.claude/settings.json に Anthropic API KEY を保存
claude login

# ~/.gemini/.credentials.json に Gemini KEY を保存
gemini login
  1. Docker 起動

クレデンシャルを含む対象ディレクトリをマウントします。

-v ~/.claude:/home/node/.claude \
-v ~/.gemini:/home/node/.gemini \
-e CLAUDE_CONFIG_DIR=/home/node/.claude
  1. コンテナ内で確認
claude whoami
gemini whoami

6️⃣ 作業完了を音で通知:play-sound

目的

  • 長い作業が終わったら 耳で把握したい。そのため Claude Code の Hooks 機能で音の鳴らしている。
  • これをコンテナからホスト環境にアクセスして同様の操作をさせたい

スクリプト概要

#!/usr/bin/env bash
HOST_USER=${HOST_USER:?}
HOST_ADDR=${HOST_ADDR:-host.docker.internal}
SOUND=${1:-/System/Library/Sounds/Glass.aiff}

ssh -oBatchMode=yes -T \
    "$HOST_USER@$HOST_ADDR" "afplay '$SOUND'"
  1. コンテナ起動時に -e HOST_USER=$(whoami) を渡す
  2. ホスト側 ~/.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Glass.aiff || play-sound" // コンテナ内では afpaly は失敗するので play-sound が実行される
          }
        ]
      }
    ]
  }
}

7️⃣ ドメイン許可リストを JSON で動的管理

目的

API 追加時に コード修正無しでホワイトリスト更新

config.json

{
  "allowed_domains": [
    "registry.npmjs.org",
    "crates.io",
    "api.anthropic.com",
    "generativelanguage.googleapis.com"
  ]
}

init-firewall.sh 抜粋

CONFIG_FILE=/usr/local/etc/claude_dev_container.json
allowed_domains=$(jq -r '.allowed_domains[]' "$CONFIG_FILE")
for domain in $allowed_domains; do
  ips=$(dig +short "$domain")
  ipset add allowed-domains $ips
done
# 最後に OUTPUT を allowed-domains へ制限
iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT

まとめ

公式の Claude Code DevContainer 設定を拡張することで、以下を実現しました。

  1. ターミナルからの利用: コマンド1つで開発環境を起動
  2. プロジェクト固有の設定自動化: Node.js バージョンや作業ディレクトリを自動認識
  3. 開発体験の向上: 音声通知やセキュリティ設定の柔軟な管理

やはりコンテナ内でエージェントをオートパイロットで動作させることは快適です。
よりよい環境のための改善を続けていきたいです。

Discussion