devAlice
← Multi-OS

Dev Container — Mac / Windows / Linux 統一開発環境

VS Code Dev Containers で OS 差異を消す。Docker ベースで再現可能な開発環境を devcontainer.json 1ファイルでチーム全員が共有できる。

「自分の環境では動く(It works on my machine)」問題は、たいていOS差異から始まる。Mac には brew と Apple Silicon があり、Windows には PowerShell と UTF-16 があり、Linux マシンには systemd がある。全員が同じバージョンの Node とパッケージをインストールしても、微妙なズレは必ず生まれる。

Dev Containers(VS Code Dev Containers + 標準の devcontainer.json)は、OS 差異を Docker コンテナの中に封じ込める。チーム全員が同じ Linux 環境の内側で作業し、IDE だけがホスト側で動く。このガイドでは、Mac・Windows・Linux どのホストでも同じように動く単一の Dev Container をセットアップする。

TL;DR

  1. Docker ランタイムをインストール(Mac:OrbStack/Docker Desktop、Win:Docker Desktop/WSL Docker、Linux:docker)
  2. VS Code + Dev Containers 拡張機能(ms-vscode-remote.remote-containers
  3. プロジェクトルートに .devcontainer/devcontainer.json を1ファイル置く
  4. Cmd/Ctrl+Shift+PDev Containers: Reopen in Container — IDE がコンテナ内で再起動
  5. チームメンバー:clone → そのコマンドを1回実行 → 同一環境

前提条件

  • VS Code 1.95+
  • Docker ランタイム — OS 別ガイド:mac/docker-setup · windows/docker-wsl2
  • Git 管理下のプロジェクト(devcontainer.json を共有するため)

1. Dev Container を使う理由

解決する問題

  • 「自分の環境では動く」問題 — コンテナ内では OS とツールチェーンが同一
  • オンボーディングの速さ — 新メンバーが30行の README を読まなくても1クリックで環境が揃う
  • バージョン衝突の解消 — プロジェクト A は Node 18、B は Node 22 — ホストの mise/nvm なしで分離できる
  • OS 固有のビルド問題 — Apple Silicon ARM 問題、Windows の長いパス問題などを回避
  • CI との一致 — GitHub Actions ワークフローと同じベースイメージを使えばギャップがなくなる

Dev Container が弱い場面

  • GUI アプリ開発 — Mac ネイティブや Windows ネイティブの UI ビルドはコンテナに向かない
  • GPU 負荷の高いワークロード — GPU パススルーは可能だが、ホストで直接動かす方が安定
  • 低スペックマシン — Docker 自体が 1〜2GB 余分に RAM を消費する(16GB なら問題なし)

2. 最小構成の devcontainer.json

.devcontainer/devcontainer.json

{
  "name": "My Project Dev",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm",
  "features": {
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  },
  "forwardPorts": [3000, 5173],
  "remoteUser": "node"
}

主要フィールド:

フィールド役割
imageDocker ベースイメージ(Node / Python / Go / Rust など)
features追加ツール(git、gh、docker-in-docker、awscli など — カタログから選択)
postCreateCommandコンテナ作成後に1回実行(依存関係インストールなど)
customizations.vscode.extensionsコンテナ内に自動インストールされる VS Code 拡張機能
forwardPortsホストに公開するポート
remoteUserコンテナ内のユーザー(セキュリティ上 non-root 推奨)

3. 初回起動

VS Code コマンドパレット(Cmd/Ctrl+Shift+P)→ Dev Containers: Reopen in Container

  • 初回:〜1〜3分(イメージ取得 + postCreateCommand
  • VS Code の左下に Dev Container: My Project Dev と表示される
# コンテナ内ターミナル(VS Code Terminal)
uname -a
# Linux 1234abcd 6.10.x #1 SMP Debian ...
 
which node
# /usr/local/share/nvm/versions/node/v22.x/bin/node

ホスト OS に関係なく同じ Linux 環境になる。


4. ベースイメージの選び方

devcontainer.jsonimage:。公式カタログ:

言語 / スタックイメージ
Node.jsmcr.microsoft.com/devcontainers/javascript-node:1-{18,20,22}-bookworm
Pythonmcr.microsoft.com/devcontainers/python:1-{3.11,3.12,3.13}-bookworm
Gomcr.microsoft.com/devcontainers/go:1-{1.22,1.23}
Rustmcr.microsoft.com/devcontainers/rust:1-bookworm
Javamcr.microsoft.com/devcontainers/java:1-{17,21}-bookworm
.NETmcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm
Universal(多言語)mcr.microsoft.com/devcontainers/universal:2-linux

推奨:単一言語プロジェクト → その言語のイメージ。多言語 → ベースイメージ + features。

独自の Dockerfile を使うことも可能:

{
  "name": "My Custom Dev",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  }
}

5. features — 1行でツールを追加

features ブロックは公式カタログ(https://containers.dev/features)のモジュールを使う。1行でツールがインストールされる。

よく使う選択肢:

"features": {
  "ghcr.io/devcontainers/features/git:1": {},
  "ghcr.io/devcontainers/features/github-cli:1": {},
  "ghcr.io/devcontainers/features/docker-in-docker:2": {},   // コンテナ内で docker を実行
  "ghcr.io/devcontainers/features/aws-cli:1": {},
  "ghcr.io/devcontainers/features/terraform:1": {},
  "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
}

6. Docker-in-Docker — コンテナ内のコンテナ

Dev Container 内で docker run が必要な場合(docker-compose のテスト、ビルド検証など):

"features": {
  "ghcr.io/devcontainers/features/docker-in-docker:2": {
    "version": "latest",
    "dockerDashComposeVersion": "v2"
  }
}

あるいは、ホストの Docker ソケットを共有する方法もある(軽量だがセキュリティが低下する):

"mounts": [
  "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
]

7. ホスト ↔ コンテナのファイルマウント

ワークスペースフォルダは /workspaces/{project} に自動マウントされる。

追加マウント:

"mounts": [
  "source=${localEnv:HOME}/.aws,target=/home/node/.aws,type=bind,readonly",
  "source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind"
]

macOS のマウントパフォーマンスについては docker-setup §5.2 を参照(VirtioFS 推奨)。


8. 環境変数とシークレット

静的な環境変数

"containerEnv": {
  "NODE_ENV": "development",
  "LOG_LEVEL": "debug"
}

シークレット(絶対にコミットしない)

.env ファイルをマウントするか、localEnv 経由で渡す:

"containerEnv": {
  "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}",
  "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}"
}

ホストシェルの環境変数がコンテナに注入される。.env.gitignore に追加しておくこと。


9. マルチコンテナ(docker-compose 統合)

DB / Redis などの依存関係がある場合:

.devcontainer/docker-compose.yml

services:
  app:
    image: mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm
    volumes:
      - ..:/workspaces/myapp
    command: sleep infinity
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: dev
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:

.devcontainer/devcontainer.json

{
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspaces/myapp"
}

VS Code が app コンテナにアタッチし、db は自動的に起動される。


10. CI と同じ環境 — devcontainer-cli

CI で devcontainer.json を再利用する:

npm install -g @devcontainers/cli
 
# コンテナをビルドしてコマンドを実行
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . npm test

GitHub Actions:

# .github/workflows/test.yml
- name: Test in dev container
  uses: devcontainers/ci@v0.3
  with:
    runCmd: npm test

CI がグリーンならローカルでも同じ結果が得られる。


11. 検証

# ホスト
docker --version
 
# コンテナ内(VS Code Terminal)
uname -a                # Linux ...
which node              # /usr/local/share/...
echo $NODE_ENV          # development(containerEnv が効いている)
node --version          # コンテナの Node バージョン
git --version           # features でインストール済み

5つすべてがコンテナ内で正しく応答すればセットアップ完了。


12. トラブルシューティング

"Failed to connect" / コンテナが起動しない

  • Docker ランタイムは起動している?(Docker Desktop のメニューバー、orb statuscolima status
  • docker ps でポート/名前の衝突を確認
  • VS Code → コマンドパレット → 「Dev Containers: Rebuild Without Cache」

ビルドが終わらない

  • 初回ビルドはベースイメージ(数GB)を取得するため時間がかかる — 想定内
  • 2回目以降はキャッシュが効く。postCreateCommand はキャッシュ対象外(リビルドごとに再実行)
  • onCreateCommand(初回のみ)と postCreateCommand(リビルドごと)の使い分けを意識する

ファイルに Permission denied

  • コンテナの UID とホストの UID が一致していない(Linux ホストで特に発生しやすい)
  • remoteUserupdateRemoteUserUID をセット:
    "remoteUser": "vscode",
    "updateRemoteUserUID": true

forwardPorts が動かない

  • コンテナ内のアプリが 0.0.0.0 または :: にバインドしているか(127.0.0.1 ではなく)?
  • 例:Next.js は next dev -H 0.0.0.0 が必要

ホストの Git 設定が反映されない

  • .gitconfig の自動マウントはオプション — mounts に明示的に追加する
  • または features の git が使う gitconfig のコピーに頼る

node_modules が大きくて IDE が遅い

  • VS Code のウォッチャーから node_modules を除外する(files.watcherExclude
  • または node_modules をホストマウントではなくコンテナ専用ボリュームに置く:
    "mounts": [
      "source=${localWorkspaceFolderBasename}-node-modules,target=/workspaces/${localWorkspaceFolderBasename}/node_modules,type=volume"
    ]

13. 次のステップ


参考資料

更新履歴

  • 2026-05-16:初稿。devcontainer.json の基本 · ベースイメージ選択 · features · docker-compose 統合 · CI 統合 · 6つのトラブルシューティング事例。