はじめに
Mac mini をリモートサーバーとして運用し、MacBook Air の VS Code から、標準機能の Remote-SSH を使って接続しようとしたところ、ターミナルからは問題なく SSH 接続できるのに、VS Code Remote-SSH から接続した場合に限ってエラーが発生した。
解決してから分かったのだが、原因は Mac mini(リモートサーバー)側で fish シェルを使っていたことだった。
この問題は Codex(GPT-5.5)の助けを借りて解決した。
TL;DR
-
症状: ターミナルからの
ssh接続は即座に成功する一方、VS Code Remote-SSH だけが認証成功から約17秒後にConnecting with SSH timed outとなる。 -
本当の原因: リモートサーバーのアカウントで、ログインシェルに
fishを使用していたこと。VS Code が接続直後にリモートサーバーをインストールするためのブートストラップスクリプトは POSIX(sh/bash)構文で書かれており、fish では実行できない。そのため、サーバーのインストール処理全体が失敗していた。 -
解決策(ログインシェルは変更しない): VS Code 専用の SSH エイリアスを作成し、接続時のブートストラップ処理だけを
bashで実行する。使用するのはRemoteCommandとremote.SSH.enableRemoteCommand。
症状
Authenticated to <host> using "publickey" ← 認証は成功
(node) DeprecationWarning ...
(約17秒間、応答なし)
Terminating local server → Connecting with SSH timed out
ssh <host> では問題なく接続できるのに、VS Code の Remote-SSH だけが何度試してもタイムアウトした。
試したこと(疑ったものの、原因ではなかった)
確認したところ、以下はすべて正常だった。
- SSH、ネットワーク、公開鍵認証
- VS Code Server の tarball ダウンロード(手動でダウンロードし、
nodeの起動まで成功) - ディスク容量
- Gatekeeper による隔離属性(quarantine)が付いていないこと
- シェル初期化時の出力による汚染(
ssh -T <host> < /dev/nullの stdout は 0 バイトで、余計な出力なし)
よく原因として挙げられる ~/.vscode-server のキャッシュ破損や、シェルの rc ファイルによる出力汚染も疑い、削除や修正を試したが、いずれも関係なかった。
fish で失敗する理由
最近の Remote-SSH は接続後、リモート側のログインシェルをそのまま exec チャネルとして使用し、サーバーインストール用のスクリプト(POSIX 構文)を流し込む。
ログインシェルが bash や zsh なら問題ないが、fish は構文が異なるため、このスクリプトを実行できない。
その結果、次の状態になる。
- リモート側の
~/.vscode-serverにサーバーがインストールされない - VS Code は応答(handshake)を受け取れず、約17秒後にタイムアウトする
~/.vscode-serverがいつまでも空のままなら、「サーバーのダウンロードに失敗した」のではなく、ブートストラップ処理自体が実行されていない可能性を疑うべきだ。これはシェルの問題を示す重要な手掛かりになる。
VS Code Remote-SSH は、fish のような 非 POSIX ログインシェルを公式にはサポートしていない。
参考: microsoft/vscode-remote-release #10967
解決策 — fish を維持したまま接続する
ログインシェルを bash または zsh に変更するのが最も簡単だが、fish を使い続けたい場合は、VS Code 接続時のブートストラップ処理だけを bash で実行すればよい。
1. ~/.ssh/config に VS Code 専用エイリアスを追加する
既存のホスト設定はそのまま残し、VS Code 用の設定を別名で追加する。
# 通常の接続用 — fish のまま
Host myserver
HostName myserver.example.com
User me
# VS Code Remote-SSH 専用 — 接続時のブートストラップだけ bash を使用
Host myserver-vscode
HostName myserver.example.com
User me
RequestTTY force
RemoteCommand /bin/bash -l
2. VS Code の settings.json を設定する
"remote.SSH.enableRemoteCommand": true
3. Remote-SSH から myserver-vscode に接続する
これで接続できるようになった。
通常どおり ssh myserver で接続した場合は、引き続き fish が起動する。また、VS Code のリモート統合ターミナルもログインシェルの設定に従うため、fish のまま利用できる。
RemoteCommand が影響するのは、接続時のブートストラップ処理だけである。
代替案
-
最も手軽:
settings.jsonに"remote.SSH.useLocalServer": falseを設定する。exec-server モデルを無効化することで回避できる場合がある。 -
最も確実:
chsh -s /bin/bash(または zsh)でログインシェルを変更する。インタラクティブシェルだけ fish にしたい場合は、~/.bash_profileに[[ $- == *i* ]] && exec fishを記述すればよい。 -
非推奨:
config.fishでTERM_PROGRAM=vscodeの場合にexec bashする。ブートストラップ実行時点ではこの環境変数が設定されていないため、信頼できない。
学んだこと
- 通常の SSH 接続は成功するのに Remote-SSH だけが失敗する場合、ネットワークではなく、リモートシェルやブートストラップ処理を疑う。
- 調査時に何でも
bash -lcで囲んでしまうと、ログインシェル自体の問題を見落とす。少なくとも一度はシェルを明示せずにリモートコマンドを実行するとよい。fish の構文エラーがそのまま表示される。 -
~/.vscode-serverが空だからといって、必ずしもダウンロードに失敗したとは限らない。ブートストラップ処理が実行されていないサインである可能性がある。
おわりに
まさか fish が原因だとは考えていなかったため、解決までにかなり遠回りしてしまった。
やはり Bash か Zsh が最強なのかもしれない……。