
VS CodeのRemote - SSHとは、SSHで接続したサーバー上に「VS Code Server」を自動で配置し、手元のVS Codeの画面からサーバー上のファイル編集・ターミナル・デバッグを直接行える拡張機能です。Dev Containersは同じ仕組みをDockerコンテナに対して使い、devcontainer.jsonに書いた開発環境をチーム全員で同一に再現します。
前提は手元のVS Code(Windows / macOS / Linux)と、SSHで入れるLinuxサーバー1台です。Dev Containersの節ではDockerが動く環境(手元でもサーバー上でも可)を使います。
この記事でわかること
- UI(手元)とVS Code Server(接続先)の分離と、拡張機能がどちらで動くか(UI拡張 / ワークスペース拡張)
~/.ssh/configの書き方(ProxyJump、IdentityFile、ServerAliveInterval)と接続手順- ポートフォワーディングの仕組みと、Portsパネル /
LocalForwardの使い分け devcontainer.jsonの主要キー(image/features/postCreateCommand/customizations)- 接続できない・遅いときの切り分け(
~/.vscode-server、files.watcherExclude)
リモート開発の仕組み:VS Code Server
Remote - SSHで接続すると、VS Codeはまず通常のSSHセッションを張り、接続先の~/.vscode-server/に手元のVS Codeと同じコミットのサーバープロセスをダウンロードして起動します。以後、エディタのUIだけが手元で動き、ファイルの読み書き、ターミナル、言語サーバー(補完・定義ジャンプ)、デバッガー、Git操作はすべてサーバー側のプロセスが担当します。
この分離が拡張機能の動作場所を決めます。テーマやキーバインドのようにUIだけに関わる「UI拡張機能」は手元で、PythonやESLintのようにファイルを解析する「ワークスペース拡張機能」は接続先で動きます。接続先ごとに拡張機能を入れ直す必要があるのはこのためで、Pythonの拡張機能は接続先にPythonが入っていて初めて補完が効きます。
| 要素 | 手元(UI側) | 接続先(VS Code Server側) |
|---|---|---|
| 実行されるもの | ウィンドウ、テーマ、キーバインド | ファイルI/O、ターミナル、言語サーバー、デバッガー、Git |
| 拡張機能 | UI拡張(テーマ、アイコン) | ワークスペース拡張(Python、ESLint、Terraform) |
| 設定の保存先 | ユーザー設定(settings.json) | リモート設定(接続先ごとのsettings.json) |
| 必要なもの | VS CodeとSSHクライアント | SSHサーバー、bash、tar、curlまたはwget(執筆時点の要件は公式ドキュメント参照) |
SSH configの書き方と接続
Host homelab
HostName 192.168.10.20
User dev
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
ServerAliveCountMax 3
# 踏み台経由。VS Codeはこの設定をそのまま解釈する
Host app-prod
HostName 10.0.0.5
User dev
ProxyJump bastion.example.comRemote - SSH拡張機能は~/.ssh/config(Windowsは%UserProfile%\.ssh\config)をそのまま読むので、接続情報はVS Code側ではなくこのファイルに集約します。ServerAliveIntervalはNAT機器やWi-Fiで無通信のセッションが切られるのを防ぐ設定、ProxyJumpは踏み台経由の多段接続を1行で表す書き方です。パスワード認証でも繋がりますが、再接続のたびに入力を求められるうえサーバー側の安全性も下がるので、鍵認証にしたうえでサーバー側のパスワード認証を無効化しておきます。
接続は拡張機能「Remote - SSH」(ms-vscode-remote.remote-ssh)を入れ、コマンドパレット(F1)で「Remote-SSH: Connect to Host...」→ configのHostに書いた接続名(上の例ではhomelab)を選ぶだけです。初回は数十秒かけてVS Code Serverが配置され、左下のステータスバーに「SSH: 接続名」(例では「SSH: homelab」)と表示されれば接続済みです。
拡張機能はどこにインストールされるか
接続中に拡張機能ビューを開くと「Local - Installed」と「SSH: 接続名 - Installed」(例では「SSH: Homelab - Installed」。表記は執筆時点のVS Code 1.144.0のもの)の2つのセクションに分かれ、手元にだけ入っているワークスペース拡張機能には「Install in SSH: 接続名」ボタンが表示されます。接続先ごとに入れ直すのが面倒なら、ユーザー設定のremote.SSH.defaultExtensionsに拡張機能IDを列挙しておくと、新しい接続先へ自動で導入されます。
{
"remote.SSH.defaultExtensions": [
"ms-python.python",
"redhat.vscode-yaml",
"hashicorp.terraform"
],
"remote.SSH.remotePlatform": { "homelab": "linux" },
"files.watcherExclude": {
"**/node_modules/**": true,
"**/.venv/**": true
}
}設定は、ユーザー設定(手元)→ リモート設定(接続先ごと。~/.vscode-server/data/Machine/settings.json)→ ワークスペース設定(.vscode/settings.json)の順に上書きされます。フォーマッタやインタープリターのパスのように接続先の環境に依存する設定は、リモート設定かワークスペース設定に置くと、手元の設定と混ざりません。
ポートフォワーディング
サーバー上でpython3 -m http.server 8000のように起動したサービスは、サーバーのlocalhostにしか待ち受けていなければ手元のブラウザからは見えません。Remote - SSHはssh -L 8000:localhost:8000と同じ転送をSSHセッションの中で行い、手元のhttp://localhost:8000に届くようにします。ターミナルに「http://localhost:8000」のような出力が出ると自動でPortsパネルに追加され、手動なら「Forward a Port」で追加します。
転送されたポートは手元の127.0.0.1にしか開かないため、同じLANの別端末から見せたい場合はSSHの転送ではなく、サーバー側で0.0.0.0に待ち受けたうえでファイアウォールを開けます。恒常的に使う転送は~/.ssh/configにLocalForward 5432 localhost:5432と書いておくと、接続と同時に有効になります。
Dev Containersとは、devcontainer.jsonの書き方
Dev Containersは「開発環境そのものをコンテナにする」拡張機能で、リポジトリ直下の.devcontainer/devcontainer.jsonに書いた定義からコンテナを作り、その中にVS Code Serverを配置して接続します。Remote - SSHとの違いは接続先がサーバーかコンテナかだけで、拡張機能の分離やポート転送の考え方は同じです。手元にDockerがあればそのまま、サーバー上のDockerを使うならRemote - SSHで接続してから「Reopen in Container」する二段構えになります。WindowsではWSL2上のDockerを使うのが基本で、WSL2とDockerで作るWindows開発環境に導入手順があります。
{
"name": "infra-tools",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
"features": {
"ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
"ghcr.io/devcontainers/features/terraform:1": {},
"ghcr.io/devcontainers/features/aws-cli:1": {},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
},
"forwardPorts": [8000],
"postCreateCommand": "pipx install --include-deps ansible",
"customizations": {
"vscode": {
"extensions": ["ms-python.python", "hashicorp.terraform", "redhat.vscode-yaml"],
"settings": { "terminal.integrated.defaultProfile.linux": "bash" }
}
},
"remoteUser": "vscode",
"mounts": [
"source=${localEnv:HOME}/.aws,target=/home/vscode/.aws,type=bind,readonly"
]
}imageの代わりにbuild.dockerfileでDockerfileを指定でき、DBなど複数コンテナが必要ならdockerComposeFileとserviceでCompose定義を流用します。featuresは「ツールのインストーラーを合成する」仕組みで、Dockerfileを書かずにTerraformやAWS CLIを追加できます。postCreateCommandはコンテナ作成後に1回だけ走るので依存関係の導入に向き、customizations.vscode.extensionsに書いた拡張機能はコンテナ側へ自動で入ります。mountsで~/.awsを読み取り専用で渡しているのは、認証情報をイメージに焼き込まないためです。
コマンドパレットの「Dev Containers: Reopen in Container」でビルドと接続が始まります。ビルド済みイメージは再利用され、devcontainer.jsonを変えたときは「Rebuild Container」で作り直します。
チームで開発環境を共有する
.devcontainer/をリポジトリにコミットすると、cloneした全員が同じツールバージョン・同じ拡張機能・同じ設定で作業を始められます。READMEの「環境構築手順」がdevcontainer.jsonに置き換わる、と考えると分かりやすいです。再現性を保つにはimageのタグをubuntu-24.04のように固定し、featuresにもバージョンを指定します。latestを使うと、人によって環境が違うという元の問題に戻ります。
| 選択肢 | 向いている場面 | 向いていない場面 |
|---|---|---|
| Remote - SSH(サーバー直接) | 既存サーバーの運用作業、GPUや大容量データがサーバー側にある | 環境をチームで揃えたい(サーバーの状態が人ごとに変わる) |
| Dev Containers(手元のDocker) | チームで環境を統一、プロジェクトごとにツールのバージョンが違う | 手元のPCが非力、会社PCにDockerを入れられない |
| Remote - SSH + Dev Containers | サーバーのDockerを使いつつ環境を統一 | 設定の層が増えるため初学者には重い |
| GitHub Codespaces | 手元に何も入れたくない、外部協力者に一時的な環境を渡す | 社内ネットワークのリソースに直接触る必要がある、従量課金を避けたい |
おすすめ拡張機能(用途別に少数)
| 用途 | 拡張機能 | 使いどころ |
|---|---|---|
| リモート接続 | Remote Development(拡張機能パック。執筆時点ではRemote - SSH、Dev Containers、WSL、Remote - Tunnelsの4つを同梱) | この記事の全機能 |
| コンテナ | Microsoft製のコンテナ拡張機能(執筆時点の名称はContainer Tools。旧Docker拡張機能) | イメージ・コンテナの一覧とログ確認 |
| IaC | HashiCorp Terraform(hashicorp.terraform)、YAML(redhat.vscode-yaml) | HCLの補完・整形、Composeやワークフローのスキーマ検証 |
| 言語 | Python(ms-python.python)など対象言語1つ | 言語サーバーは接続先で動くので、接続先にランタイムが必要 |
| 整形 | EditorConfig(editorconfig.editorconfig) | 改行コードとインデントをチームで固定 |
選び方は「接続先で動くワークスペース拡張機能は必要最小限」です。拡張機能は接続先ごとにプロセスとメモリを消費し、非力なサーバーでは遅さの原因になります。Gitの履歴表示はVS Code標準のGit機能で足りることが多く、GitLensのような高機能な拡張機能は必要になってから入れます。
動作確認
接続先で動いているかどうかは、ステータスバーの表示だけでなくコマンドの出力で確かめます。下の結果は、執筆時点のVS Code 1.144.0とRemote - SSH 0.128.0で接続先に入り、続けて接続先のDockerでDev Containerを開いて記録したものです。出力のnagoyadevlab02は私たちの検証用サーバーのホスト名の例なので、自分のサーバーの名前に読み替えてください。
# 手元のターミナル:VS Codeを使う前に、素のsshで接続先に入れることを確認
$ ssh homelab hostname
nagoyadevlab02
# 手元のターミナル:VS Codeのバージョンとコミット
$ code --version
1.144.0
2c33b50b16c472efe58357d1d2a8a5414bc23fbf
x64
# Remote - SSHで接続した後、手元のターミナルから拡張機能の配置先を見る
$ ssh homelab 'ls ~/.vscode-server/extensions/'
extensions.json
hashicorp.terraform-2.40.0-linux-x64
ms-python.debugpy-2026.6.0-linux-x64
ms-python.python-2026.10.0-linux-x64
ms-python.vscode-pylance-2026.4.1
ms-python.vscode-python-envs-1.40.0-linux-x64
redhat.vscode-yaml-2.0.0
# VS Code内のターミナル(接続先で動いている)
$ hostname && echo "$TERM_PROGRAM" && ls ~/.vscode-server/cli/servers/
nagoyadevlab02
vscode
Stable-2c33b50b16c472efe58357d1d2a8a5414bc23fbf lru.json
# Dev Container内のターミナル
$ echo "$REMOTE_CONTAINERS" && whoami && terraform version | head -n 1
true
vscode
Terraform v1.17.1最初のssh homelab hostnameはVS Codeを通さない確認で、ここで失敗するならRemote - SSHも同じ理由で失敗します。接続後の拡張機能の一覧には、remote.SSH.defaultExtensionsに書いた3つのほかに、ms-python.pythonと一緒に入ったms-python.vscode-pylance、ms-python.debugpy、ms-python.vscode-python-envsが並びます。
VS Code内のターミナルでは、TERM_PROGRAMのvscodeが統合ターミナルであることを、hostnameがそのシェルが接続先で動いていることを示します。~/.vscode-server/cli/servers/のディレクトリ名はStable-の後ろが手元のcode --versionの2行目(コミットID)と一致しており、手元と同じビルドのサーバーが配置されたことが分かります。ターミナルの先頭に「This shell is running on your local machine, NOT on the connected remote machine」と出ていたら、開いたのは手元のシェルで、hostnameは手元の名前を返します。
最後のDev Container内のターミナルでREMOTE_CONTAINERSがtrue、ユーザーがremoteUserに書いたvscodeになり、terraformがfeatureで入ったバージョンを返せば、devcontainer.jsonどおりのコンテナに入っています。接続先のDockerを使ったため、ステータスバーの表示は「Dev Container: infra-tools @ homelab」でした。featureのビルドは検証環境で初回347秒かかり、同じ定義で開き直した2回目はキャッシュが効いて1秒未満で終わりました。手元がWindows・macOSの場合の動作は、この環境では試していません。
つまずきポイント
| 症状・エラーメッセージ | 原因 | 対処 |
|---|---|---|
| 「Could not establish connection to "接続名"」 | ssh自体が通っていない。Windowsでは「Permissions for '…id_ed25519' are too open」で鍵が拒否されることが多い | まずssh -v 接続名で原因を見る。鍵ファイルのアクセス権は自分だけに絞る(Windowsはプロパティ→セキュリティで継承を無効化) |
| 「Downloading VS Code Server」で止まる | 接続先からインターネットに出られない(閉域網、プロキシ) | ユーザー設定でremote.SSH.localServerDownloadをalwaysにし、手元でダウンロードしてSSH経由で転送させる |
| 接続はできるが操作が重い、サーバーのCPUが高止まり | ファイル監視がnode_modulesや.venvまで見ている、または拡張機能が多すぎる | files.watcherExcludeを設定し、不要なワークスペース拡張機能を無効化。「Remote-SSH: Kill VS Code Server on Host」で一度プロセスを落とす |
| Dev Containersが始まらず、ログに「failed to connect to the docker API at unix:///var/run/docker.sock」「connect: no such file or directory」 | Dockerを動かすマシン(Remote - SSH経由なら接続先)でDockerデーモンが起動していない。ユーザーがdockerグループに入っていない場合は、docker CLIが「permission denied while trying to connect to the docker API」を返す | 接続先でdocker psが通るか確認。デーモンが止まっていれば起動し、permission deniedならユーザーをdockerグループへ追加して再ログイン |
| Dev Containerが起動せず、ログに「invalid mount config for type "bind": bind source path does not exist: /home/ユーザー名/.aws」 | mountsのsource(この記事の例では~/.aws)が、Dockerを動かしているマシン(Remote - SSH経由なら接続先)に存在しない。featureのビルドが終わった後のdocker runで止まる | 接続先でmkdir -p ~/.awsを実行してから「Dev Containers: Reopen in Container」をやり直す。AWSを使わないならmountsの行を消す |
VS Codeを更新するたびに、接続先の~/.vscode-server/cli/servers/には新しいバージョンのサーバーがStable-コミットIDという名前で追加されます(執筆時点の配置)。ディスクの小さなVPSで「No space left on device」が出たら、接続先でdu -sh ~/.vscode-serverを確認し、使っていないバージョンを削除してください。
Dev Containerのmountsやdocker-outside-of-dockerは、コンテナの中から手元の認証情報やDockerソケットに触れられるということです。信頼できないリポジトリのdevcontainer.jsonをそのまま開くと、postCreateCommandが任意のコマンドを実行できるため、VS Codeのワークスペース信頼の確認で「信頼しない」を選び、中身を読んでから開いてください。
よくある質問
Remote SSHはサーバーに何をインストールしますか?
接続したユーザーのホームディレクトリ配下(~/.vscode-server/)にVS Code Serverの本体と拡張機能を置き、root権限やシステム領域への変更はありません。削除したいときはこのディレクトリを消します。前提として接続先にbash、tar、curlまたはwgetが必要で、対応ディストリビューションの条件は執筆時点の公式ドキュメントで確認してください。
Dev Containerは本番と同じ環境にできますか?
ランタイムのバージョンやOSは揃えられますが、完全に同一にはなりません。Dev Containerには開発ツールやvscodeユーザーが追加されるためです。近づける方法は、本番と同じベースイメージをbuild.dockerfileで指定し、マルチステージビルドのtargetで開発用ステージを分けることです。本番の挙動確認は本番用イメージで行います。
GitHub Codespacesとの違いは?
Codespacesは同じdevcontainer.jsonの仕様を使い、GitHubのクラウドでコンテナを動かすサービスです。Dockerが不要でブラウザだけで使えますが、利用時間とストレージに応じた従量課金があり(条件は公式ページ参照)、社内LANのリソースには直接届きません。社内サーバーでDev Containersを動かす方が、費用とネットワークの面で情シス用途に合います。
まとめ
Remote - SSHは「UIは手元、処理は接続先」という分離で成り立っているので、接続情報は~/.ssh/configに、環境依存の設定はリモート設定に、ワークスペース拡張機能は必要最小限に、という置き場所の整理がそのまま快適さになります。Dev Containersは同じ仕組みをコンテナに向けたもので、devcontainer.jsonをコミットすれば環境構築手順そのものをチームで共有できます。まず素のsshが通ることを確認し、遅ければfiles.watcherExcludeと拡張機能の棚卸しから手を付けてください。
次に読む
- Docker Compose入門:
dockerComposeFileで流用するCompose定義の書き方 - Claude Codeとは?ターミナルで動くAIコーディングエージェント:リモートのターミナルでそのまま動くAIエージェント