TL;DR(忙しい人向け)
┌─────────────────────────────────────────────────────────────────┐
│ Mutagen + Neovim = 最強のリモート開発環境 │
├─────────────────────────────────────────────────────────────────┤
│ ✅ 定義ジャンプ: 1.2秒 → 0.05秒(24倍高速) │
│ ✅ ファイル検索: 4.5秒 → 0.3秒(15倍高速) │
│ ✅ リモートCPU負荷: 30% → 1%以下 │
│ ✅ ネット切断時: 作業継続可能 │
└─────────────────────────────────────────────────────────────────┘
| 項目 | VSCode Remote SSH | Mutagen + Neovim |
|---|---|---|
| ファイルを開く | 0.5〜2秒 | 即座 |
| 定義ジャンプ | 1〜3秒 | 0.05秒 |
| 補完候補表示 | 0.3〜1秒 | 即座 |
| ファイル検索 | 数秒〜十数秒 | 0.3秒 |
| ネットワーク切断時 | 作業不能 | 継続可能 |
はじめに:なぜVSCode Remote SSHは遅いのか
graph LR
subgraph VSCode["VSCode Remote SSH"]
V1[キー入力] --> V2[SSH転送]
V2 --> V3[リモートVSCode Server]
V3 --> V4[リモートLSP]
V4 --> V5[SSH転送]
V5 --> V6[画面描画]
end
subgraph Mutagen["Mutagen + Neovim"]
M1[キー入力] --> M2[ローカルNeovim]
M2 --> M3[ローカルLSP]
M3 --> M4[画面描画]
end
style V2 fill:#ff6b6b,stroke:#333,color:#fff
style V5 fill:#ff6b6b,stroke:#333,color:#fff
style M2 fill:#4caf50,stroke:#333,color:#fff
style M3 fill:#4caf50,stroke:#333,color:#fff
VSCode Remote SSHは「リモートで全部やる」設計。
キー入力のたびにSSH往復が発生し、LSPもリモートで動く。ネットワークレイテンシがそのまま操作遅延になる。
Mutagen + Neovimは「ローカルでやって同期する」設計。
エディタもLSPもローカルで動く。ファイルはバックグラウンドで同期。操作遅延はゼロ。
全体アーキテクチャ
graph TB
subgraph Local["ローカルマシン(Mac/Linux)"]
NV[Neovim]
LSP[LSP Server<br/>phpactor / tsserver]
TS[Treesitter]
TEL[Telescope / fzf]
LF[(ローカルファイル<br/>~/dev/project/)]
NV --> LSP
NV --> TS
NV --> TEL
NV <--> LF
LSP --> LF
end
subgraph Sync["Mutagen 同期エンジン"]
MD[Mutagen Daemon]
FS[ファイル監視<br/>fsnotify / FSEvents]
DIFF[差分検出]
TRANS[SSH転送]
MD --> FS
FS --> DIFF
DIFF --> TRANS
end
subgraph Remote["リモートサーバー(Linux)"]
RF[(リモートファイル<br/>/home/user/project/)]
APP[アプリケーション]
DB[(MySQL / PostgreSQL)]
DOCKER[Docker コンテナ群]
RF --> APP
APP --> DB
APP --> DOCKER
end
LF <-->|双方向同期| MD
TRANS <-->|SSH| RF
style NV fill:#4caf50,stroke:#333,color:#fff
style LSP fill:#4caf50,stroke:#333,color:#fff
style MD fill:#2196f3,stroke:#333,color:#fff
style RF fill:#ff9800,stroke:#333,color:#fff
レイテンシ発生ポイントの比較
graph TD
subgraph VSCode["❌ VSCode Remote SSH"]
direction LR
VK[キー入力] -->|レイテンシ| VS[SSH]
VS -->|レイテンシ| VR[リモート処理]
VR -->|レイテンシ| VS2[SSH]
VS2 -->|レイテンシ| VD[画面描画]
VNote["往復レイテンシ × 2 = 遅い"]
end
subgraph Mutagen["✅ Mutagen + Neovim"]
direction LR
MK[キー入力] --> ML[ローカル処理]
ML --> MD[画面描画]
MNote["レイテンシ = 0"]
end
style VS fill:#ff6b6b,stroke:#333,color:#fff
style VS2 fill:#ff6b6b,stroke:#333,color:#fff
style ML fill:#4caf50,stroke:#333,color:#fff
Mutagenの同期メカニズム
ファイル同期フロー
sequenceDiagram
participant E as Neovim
participant L as ローカルFS
participant M as Mutagen
participant R as リモートFS
Note over E,R: ファイル編集 → 保存
E->>L: ファイル保存
L-->>E: 保存完了(即座)
Note over E: ユーザーは既に次の作業へ
L->>M: fsnotify: 変更検知
M->>M: 差分計算
M->>R: SSH: 差分転送
R-->>M: 転送完了
M->>M: メタデータ更新
Note over M,R: バックグラウンド処理<br/>(ユーザーは待たない)
双方向同期の仕組み
graph TD
subgraph Alpha["Alpha(ローカル)"]
LA[(~/dev/project/)]
LW[ファイル監視]
end
subgraph Beta["Beta(リモート)"]
RA[(/home/user/project/)]
RW[ファイル監視]
end
subgraph Engine["Mutagen エンジン"]
CACHE[(メタデータ<br/>キャッシュ)]
HASH[ハッシュ比較]
RESOLVE[コンフリクト解決]
TRANSFER[差分転送]
end
LA --> LW
RA --> RW
LW --> HASH
RW --> HASH
HASH --> CACHE
HASH --> RESOLVE
RESOLVE --> TRANSFER
TRANSFER --> LA
TRANSFER --> RA
style CACHE fill:#e3f2fd,stroke:#1976d2
style TRANSFER fill:#4caf50,stroke:#333,color:#fff
SSHFSとの決定的な違い
graph LR
subgraph SSHFS["❌ SSHFS"]
S1[ファイル読み込み] -->|毎回SSH| S2[リモート]
S2 -->|毎回SSH| S3[返却]
S4["ls, stat, read<br/>全てネットワーク経由"]
end
subgraph Mutagen["✅ Mutagen"]
M1[ファイル読み込み] --> M2[ローカルFS]
M3["高速ローカルアクセス<br/>同期はバックグラウンド"]
end
style S1 fill:#ff6b6b,stroke:#333,color:#fff
style M1 fill:#4caf50,stroke:#333,color:#fff
| 項目 | SSHFS | Mutagen |
|---|---|---|
| 動作原理 | FUSEでリモートをマウント | 双方向ファイル同期 |
| ファイル読み込み | 毎回SSH経由 | ローカルから直接 |
| メタデータ取得 | 毎回SSH経由 | ローカルにキャッシュ |
| LSP動作場所 | リモート必須 | ローカルで動作 |
| ネットワーク切断時 | 即死 | ローカルで作業継続 |
SSHFSは「リモートをローカルに見せかける」だけ。Mutagenは「実際にファイルをコピーして同期する」。
実装手順(コピペで完成)
セットアップフロー
flowchart TD
Start([開始]) --> SSH[1. SSH設定]
SSH --> Install[2. Mutagenインストール]
Install --> Session[3. 同期セッション作成]
Session --> Ignore[4. 除外設定]
Ignore --> LSP[5. Neovim LSP設定]
LSP --> Test{動作確認}
Test -->|成功| Done([完成])
Test -->|失敗| Debug[トラブルシューティング]
Debug --> Test
style Start fill:#e3f2fd,stroke:#1976d2
style Done fill:#4caf50,stroke:#333,color:#fff
style Debug fill:#ff9800,stroke:#333,color:#fff
1. SSH設定
~/.ssh/config に以下を追加:
Host devserver
HostName 192.168.1.100 # リモートサーバーのIP
User developer
IdentityFile ~/.ssh/id_ed25519
# 接続の高速化(重要)
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Keep Alive
ServerAliveInterval 60
ServerAliveCountMax 3
ソケットディレクトリを作成:
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets
2. Mutagenインストール
macOS(Homebrew):
brew install mutagen-io/mutagen/mutagen
Linux:
# 最新バージョンをダウンロード
curl -LO https://github.com/mutagen-io/mutagen/releases/download/v0.18.1/mutagen_linux_amd64_v0.18.1.tar.gz
tar xzf mutagen_linux_amd64_v0.18.1.tar.gz
sudo mv mutagen /usr/local/bin/
デーモン起動:
mutagen daemon start
3. 同期セッション作成
# 基本的な同期セッション作成
mutagen sync create \
--name=myproject \
~/dev/myproject \
devserver:/home/developer/myproject
4. 除外設定(超重要)
node_modulesやvendorを同期すると地獄を見る。必ず除外設定を行うこと。
プロジェクトルートに .mutagen.yml を作成:
sync:
defaults:
ignore:
vcs: true # .git を除外
paths:
# Node.js
- "node_modules/"
- ".npm/"
- ".pnpm-store/"
# PHP
- "vendor/"
# ビルド成果物
- "dist/"
- "build/"
- "out/"
- ".next/"
- ".nuxt/"
# ログ・キャッシュ
- "logs/"
- "*.log"
- ".cache/"
- "storage/logs/"
- "storage/framework/cache/"
# IDE
- ".idea/"
- ".vscode/"
# OS
- ".DS_Store"
- "Thumbs.db"
設定ファイルを使って同期セッションを作成:
mutagen sync create \
--name=myproject \
--configuration-file=~/dev/myproject/.mutagen.yml \
~/dev/myproject \
devserver:/home/developer/myproject
5. 同期状態の確認
mutagen sync list
成功時の出力例:
--------------------------------------------------------------------------------
Name: myproject
Identifier: sync_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Alpha:
URL: /Users/developer/dev/myproject
Connection state: Connected
Beta:
URL: devserver:/home/developer/myproject
Connection state: Connected
Status: Watching for changes
--------------------------------------------------------------------------------
「Watching for changes」と表示されていれば成功!
Neovim LSP設定
LSPが爆速になる理由
graph TD
subgraph Remote["VSCode Remote: LSPがリモート"]
R1[コード補完要求] -->|SSH| R2[リモートLSP]
R2 -->|SSH| R3[補完候補]
RNote["往復レイテンシで遅い"]
end
subgraph Local["Mutagen: LSPがローカル"]
L1[コード補完要求] --> L2[ローカルLSP]
L2 --> L3[補完候補]
LNote["ローカル処理で爆速"]
end
style R1 fill:#ff6b6b,stroke:#333,color:#fff
style L1 fill:#4caf50,stroke:#333,color:#fff
設定例(PHP + TypeScript)
~/.config/nvim/lua/plugins/lsp.lua:
local lspconfig = require('lspconfig')
-- PHP(intelephense)
lspconfig.intelephense.setup({
settings = {
intelephense = {
stubs = {
"apache", "bcmath", "bz2", "calendar", "Core",
"curl", "date", "dom", "fileinfo", "filter",
"gd", "hash", "iconv", "intl", "json", "libxml",
"mbstring", "mysqli", "openssl", "pcre", "PDO",
"pdo_mysql", "pdo_sqlite", "Phar", "posix",
"readline", "Reflection", "session", "SimpleXML",
"SPL", "standard", "tokenizer", "xml", "xmlreader",
"xmlwriter", "zip", "zlib", "wordpress", "laravel"
},
},
},
})
-- TypeScript
lspconfig.ts_ls.setup({})
-- LSPキーマッピング
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local opts = { buffer = args.buf }
-- 定義ジャンプ(最重要)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
-- 型定義ジャンプ
vim.keymap.set('n', 'gy', vim.lsp.buf.type_definition, opts)
-- 参照一覧
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
-- ホバー情報
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
-- リネーム
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
end,
})
実測ベンチマーク
テスト環境
- ローカル: MacBook Pro M2, 16GB RAM
- リモート: Ubuntu 22.04, 4 vCPU, 8GB RAM
- ネットワーク: 同一LAN内(レイテンシ約1ms)
- プロジェクト: Laravel + Vue.js(PHPファイル約500、JSファイル約200)
結果
graph LR
subgraph Result["ベンチマーク結果"]
direction TB
subgraph Jump["定義ジャンプ"]
J1["VSCode: 1.2秒"]
J2["SSHFS: 0.8秒"]
J3["Mutagen: 0.05秒"]
end
subgraph Search["ファイル検索"]
S1["VSCode: 4.5秒"]
S2["SSHFS: 3.2秒"]
S3["Mutagen: 0.3秒"]
end
subgraph CPU["リモートCPU"]
C1["VSCode: 15-30%"]
C2["SSHFS: 5-10%"]
C3["Mutagen: 1%以下"]
end
end
style J3 fill:#4caf50,stroke:#333,color:#fff
style S3 fill:#4caf50,stroke:#333,color:#fff
style C3 fill:#4caf50,stroke:#333,color:#fff
| 操作 | VSCode Remote | SSHFS + Neovim | Mutagen + Neovim |
|---|---|---|---|
| Neovim/VSCode起動 | 3.2秒 | 1.8秒 | 0.4秒 |
| 定義ジャンプ(PHP) | 1.2秒 | 0.8秒 | 0.05秒 |
| 定義ジャンプ(TS) | 0.9秒 | 0.6秒 | 0.03秒 |
| プロジェクト全体検索 | 4.5秒 | 3.2秒 | 0.3秒 |
| ファイル保存→反映 | 即時 | 即時 | 0.2秒(非同期) |
| リモートCPU使用率 | 15-30% | 5-10% | 1%以下 |
WAN経由(VPN等)では差がさらに顕著になる。
トラブルシューティング
問題解決フローチャート
flowchart TD
Start([同期が動かない]) --> Q1{SSH接続できる?}
Q1 -->|No| A1[SSH設定を確認<br/>ssh devserver で直接接続]
Q1 -->|Yes| Q2{Mutagenデーモン起動?}
A1 --> Q1
Q2 -->|No| A2[mutagen daemon start]
Q2 -->|Yes| Q3{Status: Watching?}
A2 --> Q3
Q3 -->|No| Q4{Conflictsあり?}
Q3 -->|Yes| Done([正常動作])
Q4 -->|Yes| A4[mutagen sync reset<br/>--mode=alpha-wins]
Q4 -->|No| A5[mutagen sync monitor<br/>で詳細確認]
A4 --> Q3
A5 --> Q6{除外設定OK?}
Q6 -->|No| A6[.mutagen.yml で<br/>node_modules等を除外]
Q6 -->|Yes| A7[mutagen daemon stop<br/>mutagen daemon start]
A6 --> Q3
A7 --> Q3
style Start fill:#ff6b6b,stroke:#333,color:#fff
style Done fill:#4caf50,stroke:#333,color:#fff
よくある問題と解決策
「Connecting…」のまま進まない
# SSHで直接接続できるか確認
ssh devserver
# できない場合はSSH設定を見直す
# できる場合はMutagenデーモンを再起動
mutagen daemon stop
mutagen daemon start
「Conflicts detected」が出る
両方で同じファイルを編集した場合に発生。
# ローカルを優先する場合
mutagen sync reset myproject --mode=alpha-wins
# リモートを優先する場合
mutagen sync reset myproject --mode=beta-wins
同期が遅い・止まる
# 同期状況の詳細確認
mutagen sync monitor myproject
# 除外設定を確認(node_modules等が含まれていないか)
cat .mutagen.yml
操作リファレンス
Neovim キーマップ
| キー | 動作 |
|---|---|
gd |
定義ジャンプ |
Ctrl-o |
前の位置に戻る |
Ctrl-i |
次の位置に進む |
gr |
参照一覧 |
K |
ホバー情報表示 |
<leader>rn |
リネーム |
Mutagen コマンド
# 同期状態確認
mutagen sync list
# 同期を一時停止
mutagen sync pause myproject
# 同期を再開
mutagen sync resume myproject
# 強制的に再同期
mutagen sync reset myproject
# セッション削除
mutagen sync terminate myproject
# リアルタイム監視
mutagen sync monitor myproject
便利エイリアス
~/.zshrc または ~/.bashrc に追加:
# Mutagen同期管理
alias devsync='mutagen sync list'
alias devstart='mutagen sync resume --all'
alias devstop='mutagen sync pause --all'
alias devmon='mutagen sync monitor'
セキュリティ考慮事項
リスクと対策
graph TD
subgraph Risks["リスク"]
R1[ソースコード漏洩]
R2[SSH鍵の漏洩]
R3[ローカルPC紛失]
end
subgraph Mitigations["対策"]
M1[ディスク暗号化<br/>FileVault / BitLocker]
M2[SSH鍵パスフレーズ<br/>ed25519使用]
M3[リモートワイプ設定]
end
R1 --> M1
R2 --> M2
R3 --> M3
style R1 fill:#ff6b6b,stroke:#333,color:#fff
style R2 fill:#ff6b6b,stroke:#333,color:#fff
style R3 fill:#ff6b6b,stroke:#333,color:#fff
style M1 fill:#4caf50,stroke:#333,color:#fff
style M2 fill:#4caf50,stroke:#333,color:#fff
style M3 fill:#4caf50,stroke:#333,color:#fff
会社に確認すべきこと
- ソースコードをローカルにコピーして良いか
- 私物PC使用の場合、セキュリティ要件を満たしているか
- 退職時のデータ削除手順
許可を得ずに勝手にやると懲戒対象になりうる。必ず確認すること。
まとめ
この構成の価値
graph LR
subgraph Benefits["Mutagen + Neovim のメリット"]
B1[ローカルの快適さ]
B2[リモートの本番環境]
B3[ネット切断耐性]
B4[サーバー負荷軽減]
end
B1 --> Result[最強の<br/>開発体験]
B2 --> Result
B3 --> Result
B4 --> Result
style Result fill:#4caf50,stroke:#333,color:#fff
- ローカルの快適さとリモートの本番同等環境を両立
- VSCode Remote SSHの「もっさり感」から完全に解放
- ネットワークが不安定でもローカルで作業継続可能
- リモートサーバーの負荷を大幅に軽減
最後に
VSCode Remote SSHは「誰でも使える」という点では優れている。だが、速度を求めるなら選択肢にならない。
Mutagenの初期設定は少し手間がかかる。だが一度設定すれば、その後の開発体験は別次元になる。
Mutagenで開発速度を爆上げして、人生の時間を取り戻そう。