自動化

Dev Container(devcontainer)のメリットとは|Dockerで開発環境を統一する仕組みと設定【2026年版】

Dev Container(devcontainer)は、開発に必要なOS・ランタイム・ツール・エディタ設定を1つのDockerコンテナとして定義し、その定義ファイルをリポジトリに置いておく仕組みです。誰がクローンして開いても同じ環境が数分で立ち上がるため、「自分の環境では動く」問題や、新メンバーの環境構築に半日かかる状況を根本から解消できます。最大のメリットは、開発環境そのものをコード(.devcontainer/devcontainer.json)としてバージョン管理し、ローカルを汚さずにプロジェクト単位で再現・破棄できることです。

もともとはVS Codeの拡張機能から始まりましたが、現在はcontainers.devで公開されるオープン仕様となり、GitHub CodespacesやJetBrains系IDE、CIからも同じ定義を使い回せます。この記事では、Dev Containerのメリットと向かない場面、devcontainer.jsonの書き方(image・Dockerfile・Features)、VS Codeでの使い方、docker-composeでの複数サービス構成、コンテナ内でDockerを動かす方法、遅い・重いときの改善策、CIやCodespacesでの再利用までを2026年時点の仕様で解説します。

まとめ

  • Dev Containerは開発環境をコンテナ定義(.devcontainer/devcontainer.json)としてリポジトリに固定し、誰が開いても同じ環境を数分で再現する仕組み。
  • 核となるメリットは3つ。環境の再現性(環境差異による不具合を排除)、オンボーディングの短縮(クローンして開くだけ)、ローカルを汚さない(ツールをホストに入れない)。
  • 設定は3系統。devcontainer.json(環境全体の定義)、Dockerfile(ベースイメージ)、docker-compose.yml(複数サービス)。ツール追加は宣言的なFeaturesが基本。
  • VS Code拡張は「Dev Containers」(旧Remote – Containers)。仕様はcontainers.devで標準化され、CodespacesやCIでも同じ定義を再利用できる。
  • 向かないのは、単純な単一言語スクリプトやGUI主体・GPUをホスト直結で使う用途、macOS/Windowsでファイルアクセスが重くなるケース。遅さはボリューム設計とWSL2で大きく改善できる。

以下では、仕組みとメリットから具体的な設定・使い方・つまずきどころまでを順に見ていきます。

Dev Containerとは|Dockerコンテナを開発環境にする仕組み

Dev Containerは、アプリを配布するためのコンテナではなく、コードを書くための開発環境そのものをコンテナ化したものです。プロジェクトのルートに.devcontainerフォルダを作り、その中のdevcontainer.jsonに「どのイメージを使い、どのツールと拡張機能を入れ、起動時に何を実行するか」を記述します。エディタやCLIはこの定義を読み取り、コンテナをビルドして中に接続し、あなたはコンテナ内で開発します。

起点はVS Codeの拡張機能でしたが、現在はcontainers.devで公開されるオープン仕様(Development Containers Specification)になっています。同じdevcontainer.jsonを、VS Codeの「Dev Containers」拡張、GitHub Codespaces、JetBrains系IDE、後述のdevcontainer CLIが解釈できるため、ツールに縛られずに環境定義を共有できます。

本番用Dockerイメージとの違い

本番用のイメージは「アプリを動かすために最小限」を目指し、コンパイラやデバッガ、シェル補完などは削ぎ落とします。Dev Containerは逆に、開発に必要なツールを積極的に含めて開発体験を最大化します。同じDockerの上に成り立ちますが役割が逆であり、両者を1つのDockerfileで無理に共通化せず、共通部分をベースイメージにして開発用ツールはFeaturesや起動時コマンドで足すのが定石です。Dockerそのものの前提知識はDockerとは|仮想マシンとの違い・必要スペック・インストール手順で補完できます。

devcontainer.json・Dockerfile・docker-compose.ymlの役割分担

3つのファイルは競合せず、役割で分かれます。devcontainer.jsonは環境全体のマニフェストで、使用イメージ・Features・拡張機能・ポート・起動時コマンドを束ねます。Dockerfileはベースとなるコンテナイメージを定義し、OSや言語ランタイムを用意します。docker-compose.ymlはアプリとデータベースなど複数コンテナをまとめて起動する場合に使います。小さなプロジェクトならdevcontainer.jsonだけでも成立し、必要に応じてDockerfileやComposeを足していきます。

Dev Containerを導入するメリットと向かない場面

Dev Containerの価値は「環境をコード化して再現可能にする」一点に集約されます。ここから具体的なメリットが派生します。ただし万能ではなく、向かない用途もはっきりしています。

環境の再現性とオンボーディングの短縮

環境定義がリポジトリに入っているため、クローンして「コンテナで再度開く」だけで、必要なランタイム・ライブラリ・拡張機能が揃った状態から開発を始められます。手順書を見ながら各自がインストールしていく方式では、バージョンのわずかな差が「自分の環境だけで再現するバグ」を生みますが、Dev Containerでは全員が同一イメージを共有するためこの種の差異が消えます。新メンバーの初期セットアップは半日仕事から数分へ。これが実務で最も効くメリットです。

ローカルを汚さない・プロジェクトごとに切り替えられる

言語ランタイムやデータベースをホストに直接インストールしないため、ローカル環境が特定プロジェクト向けの設定で汚れません。プロジェクトAはNode.js 20、プロジェクトBはPython 3.12とPostgreSQL、といった要求が衝突せず、フォルダを開き直すだけで環境が切り替わります。不要になったらコンテナを破棄すればホストには何も残らず、環境が壊れても再ビルドで即座に復旧できます。

採用を見送るべきケース(向かない場面)

導入コストが見合わない場面もあります。依存が少ない単一言語の小さなスクリプトなら、コンテナのビルドと接続のオーバーヘッドの方が大きくなります。GPUをホスト直結で使う機械学習の重い処理や、ネイティブGUIアプリの開発も、コンテナ越しでは扱いづらくなります。さらに、macOSやWindowsではホストとコンテナ間のファイル共有が遅く、巨大なnode_modulesを抱えるプロジェクトでファイル監視やビルドが目に見えて重くなることがあります。この遅さは後述のボリューム設計で改善できますが、「まず入れれば速くなる」道具ではない点は押さえておくべきです。

devcontainer.jsonの書き方|image・Dockerfile・Featuresの基本

環境の定義には大きく3つの起点があります。既製イメージをそのまま使う(image)、Dockerfileからビルドする(build)、docker-composeを使う(dockerComposeFile)です。まずは最小構成から見ていきます。

最小のdevcontainer.json(imageを指定する)

公式が公開しているイメージを指定すれば、Dockerfileを書かずに始められます。以下はNode.js環境の最小例です。

{
  "name": "My Project",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "forwardPorts": [3000],
  "postCreateCommand": "npm install"
}

imageで使うイメージを指定し、forwardPortsで公開ポートを、postCreateCommandでコンテナ作成後に一度だけ走らせるコマンドを定義しています。タグの20のように言語のバージョンを明示しておくと、チーム全員のランタイムが揃います。

Dockerfileと組み合わせる(buildを使う)

OSパッケージの追加など細かい制御が要るときは、Dockerfileを用意してbuildで参照します。

// devcontainer.json
{
  "name": "My Project",
  "build": { "dockerfile": "Dockerfile" },
  "forwardPorts": [8000]
}

本番用と開発用でベースを共通化したい場合は、Dockerfileには本番と共有できる土台だけを書き、エディタ拡張や開発専用ツールは次のFeaturesとpostCreateCommandで足すと、1つのDockerfileに開発都合を混ぜずに済みます。

Features(features)でツールを宣言的に追加する

Node.jsやGit、AWS CLIといった追加ツールは、Dockerfileに手続き的なインストールコマンドを書く代わりに、featuresに宣言するだけで導入できます。これがモダンなDev Containerの中心的な仕組みで、検索でもdevcontainer featuresのニーズがある一方、旧来の記事では手薄な部分です。

{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "20" },
    "ghcr.io/devcontainers/features/aws-cli:1": {},
    "ghcr.io/devcontainers/features/git:1": {}
  }
}

各Featureは公開リポジトリで配布され、versionなどのオプションを指定できます。インストール手順を各自が書かずに済むため、Dockerfileが短くなり、ツールの入れ替えもJSONの1行で完結します。

拡張機能・設定はcustomizations.vscodeに書く(旧トップレベルは非推奨)

インストールするVS Code拡張やエディタ設定は、customizations.vscodeの下に置くのが現行仕様です。古い記事にある、トップレベルのextensionssettingsは非推奨で、現在は次の形に移行しています。

{
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
      "settings": { "editor.formatOnSave": true }
    }
  }
}

customizations配下に置くことで、VS Code固有の設定と、他ツールでも共通に使う設定(イメージやポート)が分離され、仕様に沿った移植性の高い定義になります。ESLintを含む整形環境の考え方はESLintのVS Code設定(flat config・Prettier連携)も参考になります。

VS CodeでDev Containersを使う手順

実際に動かすには、Docker Desktop(または互換のコンテナランタイム)と、VS Code拡張「Dev Containers」が必要です。この拡張は以前「Remote – Containers」という名称で、拡張IDはms-vscode-remote.remote-containersのまま引き継がれています。

拡張のインストールとコンテナで開き直す

手順はシンプルです。VS Codeに「Dev Containers」拡張を入れ、.devcontainer/devcontainer.jsonを用意したプロジェクトを開きます。コマンドパレットで「Dev Containers: Reopen in Container(コンテナーで再度開く)」を実行すると、定義に基づいてイメージがビルドされ、VS Codeがコンテナ内に接続します。初回はビルドに時間がかかりますが、2回目以降はキャッシュが効いて短時間で起動します。以降のターミナル操作やデバッグはすべてコンテナ内で行われ、ホストには影響しません。

ポートフォワード(forwardPorts)でアプリにアクセスする

コンテナ内で起動したWebアプリにブラウザからアクセスするには、ポートを転送します。devcontainer.jsonforwardPortsに番号を並べると、コンテナ起動時に自動でホストへ転送されます。

{
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "forwardPorts": [3000, 5432],
  "portsAttributes": {
    "3000": { "label": "web", "onAutoForward": "notify" }
  }
}

portsAttributesでラベルや自動転送時の挙動を指定できます。開発サーバーの3000番やデータベースの5432番など、必要なポートだけを明示しておくと、チーム内で公開ポートの認識が揃います。

ライフサイクルコマンドで初期化を自動化する

依存パッケージのインストールやマイグレーションは、コンテナのライフサイクルに紐づくコマンドで自動化します。主なものは、初回作成時に一度だけ走るonCreateCommandpostCreateCommand、起動のたびに走るpostStartCommandです。

{
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "postCreateCommand": "npm install",
  "postStartCommand": "npm run db:migrate"
}

依存の取得は作成後に一度で十分なのでpostCreateCommandに、毎回最新化したいマイグレーションはpostStartCommandに置く、といった使い分けをすると、コンテナを開いた直後から即座に開発を始められます。

docker-composeで複数サービスをまとめて起動する

アプリとデータベース、キャッシュなど複数のコンテナを同時に扱うプロジェクトでは、docker-compose.ymldevcontainer.jsonから参照します。開発用コンテナをComposeの1サービスとして定義し、そこにVS Codeが接続する形です。

// devcontainer.json
{
  "name": "App with DB",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspace",
  "forwardPorts": [3000, 3306]
}
# docker-compose.yml
services:
  app:
    image: mcr.microsoft.com/devcontainers/javascript-node:20
    volumes:
      - .:/workspace:cached
    command: sleep infinity
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: appdb

dockerComposeFileで構成ファイルを、serviceで接続先のサービスを指定します。同じComposeファイル内のサービスは自動的に同一ネットワークに置かれるため、アプリからはlocalhostではなくサービス名(例ではdb)をホスト名にして接続します。この方式は、AWSサービスをローカルで模擬するLocalStackや、Redisなどを足す場合も同じで、サービスを1つ追加するだけで全員の環境にそのミドルウェアが行き渡ります。データベースイメージのタグ(mysql:8.0など)は保守中のバージョンを選んでおくと、サポート切れによる差し替えを避けられます。

コンテナ内でDockerを使う|docker-in-dockerとソケット共有

Dev Container内からさらにDockerを操作したい場面(コンテナのビルド、Testcontainers、docker-composeの実行など)では、方式が2つあります。検索でもdevcontainer privilegedmountsのニーズがあり、古い記事のdocker-from-dockerという呼び方は、現在はFeaturesとして整理されています。

docker-in-docker(コンテナ内に独立したDockerを持つ)

コンテナの中で独立したDockerデーモンを動かし、ホストから独立した「子」コンテナを生成する方式です。ホストのDockerと分離される代わりに、コンテナを特権付き(--privileged)で動かす必要があります。Featureを1行足すだけで有効になります。

{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  }
}

docker-outside-of-docker(ホストのDockerを共有する)

ホストのDockerソケットをコンテナにマウントし、ホスト側のDockerを操作する方式です。socatでソケットを転送する仕組みのため特権は不要で、生成されるのはホストと並ぶ「兄弟」コンテナになります。ホストとイメージのキャッシュを共有できるのも利点です。mountsでソケットを渡します。

{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
  },
  "mounts": [
    "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
  ]
}

イメージのキャッシュ共有や特権の回避を重視するならdocker-outside-of-docker、ホストと完全に分離したいなら特権付きのdocker-in-docker、という選び分けになります。いずれもDocker自体の権限モデルが前提になるので、コンテナの隔離の仕組みはDockerの仕組みを原理から理解するで押さえておくと判断しやすくなります。

Dev Containerが遅い・重いときの改善策

「Dev Containerは遅い・重い」という声の大半は、macOSとWindowsでのホスト⇔コンテナ間のファイル共有が原因です。ソースコードをホスト側に置いてバインドマウントすると、OSをまたぐファイルアクセスがボトルネックになり、大量の小さなファイルを読み書きするnode_modulesやビルド出力で顕著に遅くなります。対策は「重いパスを共有マウントから外す」ことに尽きます。

node_modulesなどを名前付きボリュームに退避する

書き込みが集中するフォルダを、ホストとの共有ではなくDockerの名前付きボリュームに載せると、コンテナ内の高速なファイルシステムで動くため体感が大きく変わります。mountsで対象サブフォルダを名前付きボリュームに割り当てます。

{
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "workspaceFolder": "/workspace",
  "mounts": [
    "source=node_modules,target=/workspace/node_modules,type=volume"
  ]
}

ソースコード全体はホストと共有して編集の即時反映を保ちつつ、node_modulesだけをボリュームに逃がすのがよく使われる型です。ビルド出力やキャッシュのフォルダも同様に扱えます。

WindowsはWSL2のファイルシステム上にリポジトリを置く

Windowsでは、リポジトリを/mnt/c配下(Windowsドライブ)ではなく、WSL2のLinuxファイルシステム内(~/repos/...など)に置くことが最も効果的です。Docker DesktopのバックエンドがWSL2の場合、Linux側にあるファイルはネイティブに近い速度で扱えますが、Windowsドライブ上のファイルはOS越しのアクセスになって大きく遅くなります。WSL2の仕組みと導入はWSL2とは|WSL1との違い・仕組み・インストール方法で確認できます。加えて、ベースイメージを必要最小限にし、Featuresで使うツールだけを入れるとビルドと起動がさらに軽くなります。

CI・GitHub Codespacesで同じ定義を再利用する

Dev Containerの定義は、ローカルだけでなくクラウドやCIでも同じものを使い回せます。1つのdevcontainer.jsonを「開発環境の正」とし、どこで開いても同じ環境になる状態を作れるのが、コンテナ化した開発環境の到達点です。

GitHub Codespacesでそのまま開く

GitHub Codespacesは、リポジトリの.devcontainerを読み取ってクラウド上に開発環境を立ち上げるサービスです。ローカルにDockerを用意しなくても、ブラウザやVS Codeから同じ環境に接続でき、低スペックの端末でも重い環境を扱えます。ローカルで使っている定義がそのまま動くため、Codespaces用に別の設定を書く必要はありません。

CIでビルド・テストする(devcontainers/ciとCLI)

CIでも開発と同じコンテナでテストを走らせれば、「ローカルでは通るのにCIで落ちる」を防げます。GitHub Actionsならdevcontainers/ciアクションで、任意のCIならdevcontainer CLI(@devcontainers/cli)のdevcontainer updevcontainer buildで、devcontainer.jsonから環境を構築して実行できます。

# GitHub Actions の例
- uses: devcontainers/[email protected]
  with:
    runCmd: npm test

ビルド済みイメージをGitHub Container Registry(ghcr.io)などにプレビルドして公開しておくと、各開発者やCIはビルドを待たずにイメージを取得でき、起動が大幅に速くなります。CIの基本的な組み方はGitHub Actionsで自動テストとビルドを設定する方法が参考になります。

devcontainer-lock.jsonでFeaturesのバージョンを固定する

Featuresは参照するバージョンが更新されると内容が変わる可能性があります。devcontainer-lock.jsonを使うと、解決した各Featureのバージョンを固定でき、時間が経ってもCIとローカルで同じFeatureのバージョンでビルドされる状態を保てます。lockファイルもリポジトリにコミットして共有し、再現性を担保します。

Dev Containerに関するよくある質問(FAQ)

Dev ContainerとDockerの違いは何ですか?

Dockerはコンテナを作って動かす基盤技術そのものです。Dev Containerはそのdockerを土台に、「開発環境をコンテナとして定義し、エディタから接続して開発する」ための仕組みと仕様(devcontainer.json)を指します。つまりDev Containerを使うにはDockerが前提であり、Dockerに開発体験のための標準化されたレイヤーを載せたものがDev Containerだと考えると整理しやすくなります。

devcontainer.jsonとDockerfileはどちらが必要ですか?

必須なのはdevcontainer.jsonだけです。既製イメージをimageで指定すればDockerfileは不要で始められます。OSパッケージの追加など細かい制御が必要になった時点でbuildからDockerfileを参照する、という段階的な足し方で問題ありません。追加ツールの多くはDockerfileを書かずFeaturesで賄えます。

Dev Containerが遅いのはなぜですか?どう改善しますか?

主な原因は、macOSやWindowsでホストとコンテナ間のファイル共有が遅いことです。node_modulesのように大量の小さなファイルを扱うフォルダを名前付きボリュームに逃がす、WindowsではリポジトリをWSL2のLinuxファイルシステム上に置く、ベースイメージを軽量にする、の3点で体感は大きく改善します。逆に言えば、これらを行わずソースを丸ごとホスト共有すると遅くなりやすいということです。

Dev Containersを使うのにDocker Desktopは必須ですか?

Dockerが動く環境は必要ですが、Docker Desktopそのものが唯一の選択肢ではありません。Docker Desktopは個人利用や小規模事業者(従業員250人未満かつ年間売上1,000万ドル未満が目安)では無料ですが、これを超える組織では有償サブスクリプションが必要になります。ライセンス条件が合わない場合は、Podmanやその他のOCI互換ランタイムを使う構成も選べます。VS Codeの「Dev Containers」拡張はDockerのAPIに対応したランタイムがあれば動作するため、ライセンス要件に応じて基盤を選べます。

Dev ContainerとGitHub Codespacesはどう違いますか?

Dev Containerは環境定義の仕組み(仕様とdevcontainer.json)で、GitHub CodespacesはそのDev Container定義をクラウド上で動かすホスティングサービスです。ローカルで動かせばDev Container、GitHubのクラウドで動かせばCodespacesという関係で、どちらも同じ.devcontainerを読み取るため、定義を書き分ける必要はありません。

関連記事

資料請求

RELATED POSTS 関連記事