Tips & Tricks (更新: 2026/6/2)

Claude Code Devcontainer完全ガイド: 再現性の高い開発環境を作る

Claude Codeをdevcontainerで安全に動かす実践手順。Dockerfile、権限、秘密情報、volume、port-forwardまで解説。

Claude Code Devcontainer完全ガイド: 再現性の高い開発環境を作る

Claude Codeで開発を進めると、環境差の小さなズレがすぐに大きな手戻りになります。自分のPCではnpm testが通るのに、チームメンバーのPCではNode.jsのバージョン、PostgreSQLクライアント、拡張機能、権限、ポート設定が違って失敗する。AIに修正を依頼しているのに、まず環境の説明から始める状態では、Claude Codeの強みを使い切れません。

そこで有効なのがDev Containerです。Dev Containerは「開発用コンテナ」のことで、VS Code、GitHub Codespaces、CursorなどのエディタがDockerコンテナに接続し、ターミナル、言語サーバー、テスト、Claude Codeを同じ箱の中で動かす仕組みです。この記事では、Claude Codeを使ったdevcontainer開発環境の再現性をテーマに、初心者でもそのまま試せる.devcontainer/devcontainer.jsonDockerfilepostCreateCommand、権限と秘密情報の注意点までまとめます。

2026年6月2日時点で、Claude Codeの公式ドキュメントにはDevelopment containersが用意されており、Claude Code Dev Container Feature、認証情報の永続化、ネットワーク制限、--dangerously-skip-permissionsの注意が整理されています。Dev ContainerそのものはVS Code Dev Containersの公式ガイドDev Container Specificationも合わせて確認してください。

なぜClaude Codeにdevcontainerが効くのか

Claude Codeはコードを読むだけでなく、ファイル編集、テスト実行、ビルド、Git操作、ドキュメント参照まで進められるエージェント型の開発ツールです。便利な一方で、実行環境があいまいだと「どのNode.jsで動いたのか」「どのPostgreSQLに接続したのか」「どのCLIが入っているのか」が見えにくくなります。

devcontainerを使うと、Claude Codeが見る世界をリポジトリで定義できます。Node.jsのバージョン、OSパッケージ、VS Code拡張、npm ci、Prisma生成、PostgreSQLやRedis、forward port、~/.claudeの永続化まで、チーム全員が同じ設定で再現できます。これは「便利なDocker化」ではなく、AIに触らせる開発面を明確にする設計です。

特に重要なのは、Claude CodeをホストPCではなくコンテナ内で動かすことです。ホストの~/.ssh、クラウド認証ファイル、別案件の環境変数を何となく見える状態にしない。コンテナ内に必要なツールだけを入れ、プロジェクト単位の設定だけを渡す。これだけで、AI開発のレビュー観点がかなり整理されます。

概念図にすると次のような関係です。

flowchart LR
  Host["ホストPC"] --> Editor["VS Code / Cursor"]
  Editor --> Container["Dev Container"]
  Container --> Claude["Claude Code CLI"]
  Container --> Tools["Node.js / npm / psql / redis-cli"]
  Container --> Services["PostgreSQL / Redis"]
  Container --> Repo["bind mountされたリポジトリ"]
  Claude --> Repo
  Claude --> Tools

この記事で作る構成

ここではNext.js + TypeScriptを例にします。実際にはReact、Astro、Express、NestJS、SvelteKitでも考え方は同じです。ポイントは「アプリ本体」「依存サービス」「Claude Codeの設定保存先」を分けることです。

ファイル役割レビューで見る点
.devcontainer/devcontainer.jsonエディタが読むdevcontainer設定remoteUserpostCreateCommand、ports、mounts
.devcontainer/Dockerfile開発ツールとClaude Codeを入れるNode.js、CLIバージョン、root実行の有無
.devcontainer/docker-compose.ymlアプリ、DB、Redisを起動するvolume、healthcheck、公開ポート
.devcontainer/post-create.sh初回作成後のセットアップlockfile優先、Prisma生成、失敗時の停止
.claude/settings.jsonClaude Codeの権限ルール.envやsecretsを読ませない設定

ユースケースは少なくとも3つあります。1つ目は新メンバーのオンボーディングです。Docker DesktopとVS Code拡張が入っていれば、リポジトリを開いてReopen in Containerするだけで、Node.js、PostgreSQL、Redis、Claude Codeがそろいます。2つ目はGitHub Codespacesやリモート開発です。ローカルPCの性能やOSに依存せず、同じ.devcontainerで作業できます。3つ目はAIレビューと修正の再現性です。Claude Codeに「この環境でnpm run testまで確認して」と依頼したとき、全員が同じコマンド結果を期待できます。

コピペで動くdevcontainer.json

まず.devcontainer/devcontainer.jsonを置きます。JSONなのでコメントは入れません。この記事の例ではDocker Composeを使い、appサービスへエディタが接続します。

{
  "name": "claude-code-next-dev",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspaces/app",
  "remoteUser": "node",
  "shutdownAction": "stopCompose",
  "waitFor": "postCreateCommand",
  "postCreateCommand": "bash .devcontainer/post-create.sh",
  "postStartCommand": "git config --global --add safe.directory /workspaces/app || true",
  "forwardPorts": [3000, 5432, 6379],
  "portsAttributes": {
    "3000": {
      "label": "Next.js",
      "onAutoForward": "notify"
    },
    "5432": {
      "label": "PostgreSQL",
      "onAutoForward": "silent"
    },
    "6379": {
      "label": "Redis",
      "onAutoForward": "silent"
    }
  },
  "mounts": [
    "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
  ],
  "containerEnv": {
    "NODE_ENV": "development",
    "DISABLE_AUTOUPDATER": "1",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "anthropic.claude-code",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-azuretools.vscode-docker",
        "GitHub.copilot"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "typescript.tsdk": "node_modules/typescript/lib",
        "terminal.integrated.defaultProfile.linux": "bash"
      }
    }
  }
}

remoteUserは必ず非rootユーザーにします。Claude Code公式ドキュメントでも、権限プロンプトをスキップするモードを使う場合は非rootであることが前提として説明されています。ただし、--dangerously-skip-permissionsは「コンテナだから安全」ではありません。bind mountされたワークスペースはホスト側のファイルでもあるため、Claude Codeが削除したファイルはホストでも消えます。使うとしても信頼済みリポジトリ、短時間の検証、Gitで戻せる状態に限定します。

mountsでは/home/node/.claudeをnamed volumeにしています。これによりコンテナをrebuildしてもClaude Codeの設定やセッション情報を保持できます。一方で、このvolumeには認証情報が含まれる可能性があるため、全プロジェクトで同じvolume名を使い回さない方が安全です。${devcontainerId}を入れるとプロジェクト単位で分離しやすくなります。

Docker Composeで依存サービスをそろえる

次に.devcontainer/docker-compose.ymlです。アプリ、PostgreSQL、Redisを同じネットワークで起動します。ローカルで直接PostgreSQLを入れている人、Docker Desktopだけの人、Codespacesの人で結果がずれないようにします。

services:
  app:
    build:
      context: ..
      dockerfile: .devcontainer/Dockerfile
    command: sleep infinity
    volumes:
      - ..:/workspaces/app:cached
      - node_modules:/workspaces/app/node_modules
      - claude_code_config:/home/node/.claude
    environment:
      DATABASE_URL: postgresql://app:app_password@db:5432/app
      REDIS_URL: redis://redis:6379
      NEXT_TELEMETRY_DISABLED: "1"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app_password
      POSTGRES_DB: app
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 5s
      retries: 20

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 20

volumes:
  node_modules:
  claude_code_config:
  postgres_data:
  redis_data:

ここでの失敗例は、portsでDBをホストに公開しすぎることです。開発中に5432:5432を公開すると、ホストのPostgreSQLと競合したり、社内ネットワークで不要に見えたりします。VS Codeから使うだけならforwardPortsで十分です。外部ツールから接続したい場合だけ、明示的にpublishします。

もう1つの落とし穴はnode_modulesです。ホストのWindowsやmacOSで作ったnode_modulesをLinuxコンテナで共有すると、ネイティブモジュールが壊れることがあります。上の例ではnode_modulesをnamed volumeに逃がし、コンテナ内でインストールします。これにより初回は少し時間がかかりますが、OS差による失敗が減ります。

DockerfileでClaude Codeのバージョンを固定する

公式のClaude Code Dev Container Featureは便利ですが、Featureのタグはインストールスクリプトのバージョンであり、Claude Code本体のバージョン固定とは別です。再現性を優先するならDockerfileで@anthropic-ai/claude-code@2.1.160のように固定します。この記事では2026年6月2日にnpm view @anthropic-ai/claude-code versionで確認した2.1.160を使っています。公開後に読む場合は、公式ドキュメントとnpmの最新値を確認して、チームで固定するバージョンを決めてください。

FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm

ARG CLAUDE_CODE_VERSION=2.1.160

ENV DISABLE_AUTOUPDATER=1
ENV NEXT_TELEMETRY_DISABLED=1

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
      jq \
      postgresql-client \
      redis-tools \
      ripgrep \
    && rm -rf /var/lib/apt/lists/*

RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
    && npm cache clean --force

USER node
WORKDIR /workspaces/app

RUN mkdir -p /home/node/.claude

DISABLE_AUTOUPDATER=1を入れているのは、rebuildのたびにClaude Codeの挙動が変わることを避けるためです。個人の検証では自動更新が便利ですが、チームで「昨日と同じ結果」を確認したい場合は固定が向いています。更新するときはDockerfileのARGを上げ、claude --versionnpm run lintnpm test、実ブラウザ確認をセットで実行します。

postCreateCommandで初回セットアップを自動化する

postCreateCommandは、コンテナ作成後に実行するコマンドです。依存関係のインストール、Prismaの生成、簡単なバージョン確認をここに寄せます。長い処理をJSONの1行に詰め込むとレビューしづらいので、シェルスクリプトに分けます。

.devcontainer/post-create.shを作成してください。

#!/usr/bin/env bash
set -euo pipefail

cd /workspaces/app

echo "==> enabling corepack"
corepack enable

echo "==> installing dependencies"
if [ -f pnpm-lock.yaml ]; then
  pnpm install --frozen-lockfile
elif [ -f yarn.lock ]; then
  yarn install --immutable
elif [ -f package-lock.json ]; then
  npm ci
elif [ -f package.json ]; then
  npm install
else
  echo "No package.json found. Skipping dependency install."
fi

if [ -f prisma/schema.prisma ]; then
  echo "==> generating Prisma client"
  npx prisma generate
fi

echo "==> versions"
node --version
npm --version
claude --version || true

このスクリプトはlockfileを優先します。Claude Codeに「依存関係を入れて」と頼むと、慣れていない場合はnpm installを雑に実行し、package-lock.jsonを意図せず更新することがあります。pnpm-lock.yamlyarn.lockpackage-lock.jsonの順で判定しておくと、チームのパッケージマネージャーがぶれません。

postCreateCommandには失敗を隠さない設計が必要です。set -euo pipefailを入れて、インストールや生成に失敗したらコンテナ作成時点で止めます。初回セットアップで失敗したままClaude Codeを起動すると、AIが存在しない依存関係や古い生成物を前提に修正を始めるため、原因の切り分けが難しくなります。

権限と秘密情報の境界を決める

devcontainerは安全装置の1つですが、秘密情報を雑に渡すと意味がありません。特に避けたいのは、ホストの~/.ssh~/.aws~/.config/gcloud、本番用.envをそのままmountすることです。Claude Codeが読むつもりがなくても、ツール実行やログ出力で露出する可能性があります。

Claude Codeの権限は.claude/settings.jsonでプロジェクト単位に調整できます。以下は、よく使う開発コマンドを許可しつつ、環境変数ファイルやsecretsディレクトリの読み取りを拒否する例です。

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run test *)",
      "Bash(npm run typecheck)",
      "Bash(node --version)",
      "Bash(claude --version)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(printenv *)",
      "Bash(git push *)",
      "Bash(docker system prune *)"
    ]
  }
}

この設定は「Claude Codeへの依頼文」ではなく、Claude Code側で評価される権限ルールです。とはいえ、万能ではありません。たとえばBash(npm run test *)を許可すると、テストスクリプトの中で何が実行されるかはプロジェクト次第です。危険なスクリプトをpackage.jsonに置かない、CIで検出する、権限設定をレビュー対象にする、という基本は残ります。

チーム運用では、管理者がserver-managed settingsやエンドポイント管理でpermissions.disableBypassPermissionsModeallowManagedPermissionRulesOnlyを使う選択肢もあります。個人開発なら.claude/settings.jsonで十分なことが多いですが、法人の受託開発や顧客データに触れるリポジトリでは、リポジトリ内の設定だけに頼り切らない方が安全です。

volumeとport-forwardの注意点

volumeは便利ですが、何を永続化するかを決めないとトラブルになります。永続化してよいものは、node_modules、PostgreSQLの開発データ、Redisの検証データ、Claude Codeのプロジェクト別設定です。永続化しない方がよいものは、本番秘密鍵、個人のクラウド認証、別案件の履歴、古いビルドキャッシュです。

開発DBのvolumeは、スキーマ変更で詰まることがあります。PrismaやDrizzleのmigrationを何度も試したあと、DBだけ古い状態で残り、Claude Codeがコードを正しく直してもテストが落ちる。こういうときはdocker compose down -vでvolumeを消す判断が必要です。ただし、このコマンドはDBデータを消します。チームで使うなら「開発DBを消してよい条件」をREADMEに書いておきます。

port-forwardも同じです。Next.jsの3000番はブラウザ確認に必要ですが、PostgreSQLの5432やRedisの6379は常に開く必要がないかもしれません。VS CodeのforwardPortsはローカルから見える便利な入口ですが、外部公開とは違います。Docker Composeのports0.0.0.0へpublishすると、ネットワーク設定によっては他の端末から見えることがあります。初心者ほど「つながらないから全部公開する」としがちなので、まずはforwardPorts、必要な場合だけportsに進む順番にしてください。

Claude Codeへの依頼テンプレート

環境がそろったら、Claude Codeには具体的な境界を渡します。雑に「devcontainerを作って」ではなく、何を固定し、何をmountし、何を公開しないかを指示します。

このリポジトリにClaude Code用のdevcontainerを追加してください。
要件:
- Node.js 22のDev Containerイメージを使う
- Claude Code CLIはDockerfileでバージョン固定する
- app、PostgreSQL、Redisをdocker-composeで起動する
- remoteUserはnodeにする
- ~/.claudeはプロジェクト別named volumeにする
- .env、secrets、git push、docker system pruneをClaude Code権限で拒否する
- postCreateCommandはbashスクリプトに分け、lockfile優先で依存関係を入れる
- 変更後に構文エラー、ポート、volume、秘密情報の漏れをレビューする

レビュー依頼も分けると精度が上がります。

実装はせず、devcontainer設定だけレビューしてください。
観点:
1. この設定はチーム全員が同じNode.jsとCLIで再現できますか
2. Claude Codeの認証情報や.envが不要に露出していませんか
3. node_modules、DB、~/.claudeのvolume分離は妥当ですか
4. forwardPortsとdocker-compose portsの使い分けに危険はありませんか
5. postCreateCommandは失敗を隠さず、lockfileを守っていますか

よくある失敗例

失敗例1は、Claude Codeをrootで動かすことです。rootユーザーだとファイル所有者が崩れ、ホスト側で編集できないファイルが生まれることがあります。さらに権限プロンプトをスキップしたときの被害も大きくなります。remoteUsernodeなどの非rootにし、Dockerfileの最後もUSER nodeにします。

失敗例2は、Claude Codeのバージョンを固定しないことです。個人なら最新追従でよい場合もありますが、チーム記事、教材、受託案件では「昨日のClaude Codeでは通った」が再現不能になります。DockerfileのARG CLAUDE_CODE_VERSIONを上げる変更を1つのレビュー単位にすると、差分を追いやすくなります。

失敗例3は、.envをコンテナに入れたあと権限設定で守った気になることです。権限ルールは大事ですが、最初から渡さない方が強いです。開発用のDATABASE_URLはComposeの内部サービス向けにし、本番APIキーはCodespaces secret、短命トークン、または手動入力に分けます。

失敗例4は、postCreateCommandで開発サーバーを起動することです。npm run devのような常駐プロセスをpostCreateCommandに入れると、コンテナ作成が終わらなかったり、ログが見えづらくなったりします。初回セットアップはpostCreateCommand、毎回の通知や軽い準備はpostStartCommand、開発サーバーはターミナルで手動起動かVS Code taskに分けます。

失敗例5は、DB volumeを消す手順を用意しないことです。migration検証を繰り返す環境では、古いvolumeが原因で「コードは正しいのに動かない」状態が起きます。READMEにdocker compose -f .devcontainer/docker-compose.yml down -vの意味と注意を書き、消してよいのは開発データだけだと明記します。

公開前のチェックリスト

  • Dev Containers: Rebuild Containerで最後まで作成できる
  • コンテナ内でnode --versionnpm --versionclaude --versionが表示される
  • npm ciまたは利用中のパッケージマネージャーがlockfileを壊さない
  • npm run lintnpm run typechecknpm testの少なくとも1つをClaude Codeに実行させて結果を確認する
  • http://localhost:3000がport-forward経由で開ける
  • .env.env.localsecrets/**がClaude Codeの読み取り対象から外れている
  • ~/.sshやクラウド認証ファイルをmountしていない
  • docker-compose.ymlportsでDBやRedisを不用意に外部公開していない
  • node_modulesがホストとコンテナで混ざっていない
  • updatedDate、公式リンク、内部リンク、CTA、アイキャッチ画像が記事側でそろっている

Docker Composeの考え方を深掘りしたい場合はClaude Code Docker Compose開発環境ガイドを、AI開発の検証フローをCIに載せる場合はClaude Code CI/CDセットアップガイドも合わせて読むと流れがつながります。Claude Codeの導入全体はClaude Code開発環境セットアップも参考になります。

マネタイズCTA

devcontainerは一度作ると終わりではありません。チームの権限、レビュー観点、CI、教材、記事制作、顧客案件の秘密情報管理まで含めて運用ルールに落とすことで価値が出ます。ClaudeCodeLabでは、Claude Codeの導入テンプレート、CLAUDE.md、権限設定、検証チェックリスト、記事品質レビューをtraining / consultationで整理できます。個人開発なら無料チェックリストやテンプレート、チームならリポジトリ単位のdevcontainerレビューから始めるのが現実的です。

この記事で紹介した内容を実際に試した結果

この記事の構成をNext.jsの小さな検証リポジトリで試した結果、効果が大きかったのはClaude Codeそのものの導入よりも、postCreateCommandをスクリプト化し、node_modules~/.claudeをvolumeで分けた点でした。初回rebuild後にclaude --versionnpm ci、Prisma生成、3000番のport-forwardまで同じ手順で確認できるため、「誰のPCで動いたのか」という説明がほぼ不要になります。一方で、DB volumeを残したままmigrationを何度も試すと古い状態に引っ張られました。つまり、Claude Code向けdevcontainerの再現性は、Dockerfileだけでなく、秘密情報を渡さない設計、volumeを消す判断、port-forwardの範囲、権限ルールをセットでレビューして初めて実用品になります。

#Claude Code #Dev Container #VS Code #Docker #開発環境
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。