🎯 はじめに:この記事のスコープ
本記事はシリーズ 「脱Microsoft・OSS移行」の第 4 回 です。第 2 回 Windows 11 + Ubuntu デュアルブート構築で OS の土台、第 3 回 Ubuntu PC セットアップ で NVIDIA ドライバ・サスペンド無効化・周辺セットアップまで完了している前提です。本記事では、その上に 開発スタック を積み上げて、「数時間放置できる開発体験」 が成立するところまでを実録ベースで解説します。
| 範囲 | 含む | 含まない |
|---|---|---|
| 本記事 | Docker Engine ネイティブ / VS Code / DevContainer / Claude Code / tmux / Agent Teams / bypassPermissions 安全運用 | ハードウェア・OS インストール(第 2 回)/ NVIDIA・サスペンド・周辺セットアップ(第 3 回)/ Microsoft 365 移行(第 6 回以降) |
| 前提 | Ubuntu 26.04 LTS + KDE Plasma + NVIDIA ドライバ導入済み(第 3 回完了) | — |
💡 本記事の到達点: Ubuntu 端末を 1 つ開けば、
docker exec → tmux → claudeの 3 ステップで Agent Teams が立ち上がり、bypassPermissions の確認クリック地獄なく数時間放置できる開発環境が完成します。
🧱 全体スタック:6 層の積み上げ
開発スタックは下から 6 層に整理できます。L4(DevContainer 隔離)が安全装置として効くことが本記事の最大のポイントです。

| 層 | 内容 | この記事で扱う節 |
|---|---|---|
| L0 | ハードウェア(M.2 SSD x2 物理分離 / NVIDIA GPU) | 前回 |
| L1 | Linux カーネル + Ubuntu 26.04 LTS + KDE Plasma | 前回 |
| L2 | ホスト直アプリ(VS Code / Chrome / Git / Node.js / 日本語入力) | §5-2, §5-3 |
| L3 | Docker Engine(ネイティブ) | §5-1 |
| L4 | DevContainer(隔離レイヤー) | §5-3 |
| L5 | Claude Code + tmux + Agent Teams | §5-4, §5-5 |
🐳 Phase 5-1: Docker Engine をネイティブ導入する
最初に入れるのは Docker Engine です。Windows + WSL2 + Docker Desktop の 二重仮想化(Hyper-V → WSL2 → Docker) が消えるため、メモリと I/O の両方が大幅に改善します。
Engine と Desktop の違いを再確認
| 項目 | Docker Engine(Linux) | Docker Desktop(Windows / macOS) |
|---|---|---|
| 仮想化レイヤー | カーネル直結 | Hyper-V → WSL2 → Docker(二重) |
| メモリ消費 | 軽量(ベース 200〜500 MB) | 30 GB+ ベース消費(実機計測) |
| I/O パフォーマンス | ネイティブ速度 | ファイル共有越しで遅延 |
| ライセンス | OSS(Apache 2.0)完全無料 | 大企業は有料(従業員 250 名超) |
💡 「Linux 上の Docker は Docker ではない、ただの Linux カーネル機能」 — Docker Engine は cgroups と namespaces のフロントエンドに過ぎません。Linux 上では仮想化レイヤーがゼロなので、「Docker を入れる」というより「Docker コマンドを叩けるようにする」 に近い感覚です。
インストール手順(公式 apt リポジトリ)
Ubuntu の apt install docker.io ではなく、Docker 公式 apt リポジトリを使います。最新版が即座に手に入り、docker compose v2 もまとめて入ります。
# 1. 古いバージョンの削除(クリーン導入)
sudo apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null
# 2. 公式 GPG キーと apt リポジトリを追加
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 3. Docker Engine + Compose plugin を導入
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
sudo なしで docker を叩けるようにする
毎回 sudo docker は煩雑なので、自分のユーザーを docker グループに入れます。
sudo usermod -aG docker $USER
# ログアウト → ログインし直して反映
docker run hello-world
⚠️
dockerグループは実質 root 権限です。コンテナ経由で/をマウントすればホストのファイルが何でも読み書きできるため、信頼できないユーザーには付与しないこと。個人開発機では問題なし。
systemd で自動起動
sudo systemctl enable docker
sudo systemctl start docker
sudo systemctl status docker # active (running) を確認
Ubuntu の systemd 上では、Docker Desktop のような GUI クライアントは不要です。docker コマンドと VS Code の Dev Containers 拡張だけで完結します。
💡 Docker Desktop for Linux は基本不要。 Linux 上では Docker Engine が直接動くため、Desktop の出番はほぼありません。GUI でコンテナを眺めたい場合は Lazydocker のような軽量 TUI を入れる方が筋が良いです。
🛠️ Phase 5-2: 開発常備ツールをまとめて導入
次は開発で毎日使うツール群です。Ubuntu の apt で入る最新 LTS を中心に揃えます。
Git / GitHub CLI / 基本ユーティリティ
sudo apt install -y git curl wget jq tree htop unzip build-essential
gh --version 2>/dev/null || {
# GitHub CLI(公式リポジトリ)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | \
sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] \
https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list
sudo apt update && sudo apt install -y gh
}
git config --global user.name "your-name"
git config --global user.email "your@email"
Node.js / pnpm(Volta 経由)
apt 版の Node.js はバージョンが古く、複数バージョン併用もできないため Volta で入れます。nvm より速く、Rust 製で安定。
curl https://get.volta.sh | bash
# .bashrc / .zshrc を再読み込みしてから
volta install node@lts
volta install pnpm
node -v && pnpm -v
VS Code(公式 deb)
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | \
gpg --dearmor | sudo tee /usr/share/keyrings/packages.microsoft.gpg > /dev/null
echo "deb [arch=amd64,arm64,armhf signed-by=/usr/share/keyrings/packages.microsoft.gpg] \
https://packages.microsoft.com/repos/code stable main" | \
sudo tee /etc/apt/sources.list.d/vscode.list > /dev/null
sudo apt update && sudo apt install -y code
💡 VS Code は脱 Microsoft の例外。 VS Code 自体は MIT ライセンスのオープンソース(コア部分)で、Microsoft の認証が必須なのは Marketplace 拡張のみ。
.NET MAUI公式拡張も VS Code で動くため、Visual Studio を捨てる代わりに VS Code は残すのが現実解です。気になる場合は VSCodium(テレメトリ無効版)も選択肢。
Chrome(Google 公式 deb)
Wayland セッションでも安定動作するのは Google Chrome です。Chromium 系の Edge / Brave も同様に動きます。
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install -y ./google-chrome-stable_current_amd64.deb
日本語入力(Mozc + fcitx5)
Ubuntu 26.04 + KDE Plasma で安定する組み合わせは fcitx5 + Mozc です。
sudo apt install -y fcitx5 fcitx5-mozc fcitx5-config-qt
im-config -n fcitx5
# ログアウト → ログインで有効化
KDE Plasma の System Settings → Input Method で fcitx5 を選択し、Ctrl + Space で日英切替が標準動作です。
⚠️ iBus は推奨しません。 Wayland + KDE Plasma 6 系では fcitx5 が最も実績豊富です。iBus を入れると VS Code / IntelliJ 系で日本語入力が崩れる事例が残っています。
Ctrl + Space 切替キーの確実な設定(HHKB US 配列など半角/全角キーがない環境)
開発者がよく使う HHKB US 配列 は、半角/全角キーや macOS の英かなキーが物理的に存在しません。fcitx5 のデフォルトトリガーは Ctrl+Space なので多くの場合追加設定なしで動きますが、ディストロや KDE Plasma の組み合わせで別バインド(Zenkaku_Hankaku 等、US 配列に存在しないキー)になっている場合があります。確実に Ctrl + Space で切り替えられるよう設定しておきます。
方法 A: GUI(最短)
fcitx5-configtool
- Global Options タブ
- Trigger Input Method をクリック → 既存バインドを削除 →
Ctrl+spaceを割り当て - (任意)Enumerate Input Method に
Ctrl+space/ Backward にCtrl+Shift+space - Apply
方法 B: 設定ファイル直接編集(dotfile 管理派向け)
mkdir -p ~/.config/fcitx5
cat > ~/.config/fcitx5/config << 'EOF'
[Hotkey]
TriggerKeys=Control+space
EnumerateForwardKeys=Control+space
EnumerateBackwardKeys=Control+Shift+space
EOF
# 反映
fcitx5 -r
動作確認
# キーバインド確認
grep -A 3 "\[Hotkey\]" ~/.config/fcitx5/config
# → TriggerKeys=Control+space が出ていれば設定 OK
# fcitx5 プロセス確認
pgrep -af fcitx5
ターミナル / VS Code / Chrome 等で Ctrl + Space を押して、タスクトレイの fcitx5 インジケータが「あ」⇔「A」で切り替われば成功。Mozc 自体の変換キー設定(変換 / 無変換 / Henkan モード等)は fcitx5-mozc-tool で別途調整できます(「ことえり風」「ATOK 風」「MS-IME 風」のプリセットあり)。
💡 macOS / Windows と切替キーを揃える価値 — 業務開発で複数 OS を行き来する場合、
Ctrl + Spaceで統一すると 筋肉記憶のコンフリクトがゼロになります。HHKB US ユーザーなら必須、JIS 配列ユーザーでも揃えておくのが楽。
ターミナルの選定(Ptyxis → Terminator / Konsole)
第 3 回 Ubuntu PC セットアップ でターミナル選定の指針には触れましたが、本記事の主題である docker exec → tmux → claude の起動経路で安定動作させる観点から、Terminator の最小設定だけはこの記事でも整理しておきます(VS Code 統合ターミナルから tmux 起動は公式に非対応のため、ホスト OS のターミナルアプリは独立して整える必要があります。詳細は §5-5)。
sudo apt install -y terminator
設定 → Profiles → General で 3 項目:
| 設定 | 推奨 | 効果 |
|---|---|---|
| PuTTY style paste | ON | 中クリック / 右クリックで PRIMARY からペースト |
| Copy on selection | ON | マウス選択で自動コピー |
| Background → Transparent | お好みで | Wayland でも安定動作 |
⚠️ PuTTY style paste を ON にすると、右クリックがペースト動作に変わるため、Terminator 本来の「右クリック → コンテキストメニュー(設定 / プロファイル切替 / 分割 / プロファイルの編集 …)」が呼べなくなります。 回避策は
F10キーでコンテキストメニュー表示。設定画面に入れずハマる前に覚えておくべき一手です。⚠️ tmux / vim の中では右クリックが奪われる — tmux や vim はマウスイベントを自分で処理するため、PuTTY style paste の右クリックペーストが Terminator まで届きません。
Shift+右クリック(Shift 押下中はマウスイベントが Terminator にバイパスされる)またはShift+Ctrl+Vでペーストします。Agent Teams 運用では tmux 上が常時のため、Shift+Ctrl+Vを基本動作に固定するのが筋。bash / tmux / vim / SSH 先すべてで動作が揃います。
Agent Teams 運用で使い倒す Terminator ショートカット
docker exec → tmux → claude の起動経路を複数本走らせるため、タブ追加と分割は手に馴染ませる価値があります。
| 操作 | ショートカット | 使い所 |
|---|---|---|
| 新規タブ | Shift+Ctrl+T | 別 DevContainer / 別プロジェクトに入る時に頻発 |
| 縦分割(左右) | Shift+Ctrl+E | tmux split-pane の代わりにホスト側で分けたい時 |
| 横分割(上下) | Shift+Ctrl+O | ログ tail と CLI を縦に並べる |
| 現在のターミナルを閉じる | Shift+Ctrl+W | 分割を畳む |
| タブ間移動 | Ctrl+PageUp / Ctrl+PageDown | Agent Teams 監視用と編集用を切替 |
| 分割ペイン間移動 | Shift+Ctrl+矢印 | ペインフォーカス移動 |
| 全画面切替 | F11 | プレゼン / レビュー時 |
| コンテキストメニュー(PuTTY paste ON 時) | F10 | 設定 / プロファイル切替 |
| ペースト | Shift+Ctrl+V | tmux / vim / SSH 越し全環境共通(右クリックペーストが効かない場面の基本動作) |
| 検索 | Shift+Ctrl+F | ログから特定文字列を探す |
💡
Shift+Ctrl+Tで新規タブ →docker exec → tmux a -t claudeで既存セッションにアタッチ、というフローを覚えると、Agent Teams を放置しているセッションへの再接続が一瞬で済みます。
複数テーマを切り替えたい場合は Gogh で Tokyo Night(CTS で実運用中・推奨)/ Catppuccin Mocha などを一括導入:
sudo apt install -y dconf-cli uuid-runtime
bash -c "$(wget -qO- https://git.io/vQgMr)"
# プロンプトで番号を空白区切り指定(例: 93 97 265)→ プロファイル名を分けて保存
| テーマ | 背景色 | 印象 | 推奨度 |
|---|---|---|---|
| Tokyo Night | #1A1B26 紺紫 | クール・目に優しい(彩度ひかえめ + 適度なコントラスト) | ◎ 業務開発で実運用中 |
| Catppuccin Mocha | #1E1E2E 暗紫 | 柔らかいパステル | ◎ |
| Dracula | #282A36 暗紫 | コントラスト強め・華やか | ○ |
| Gruvbox | #282828 暗茶 | 暖色レトロ | ○ |
| Campbell | #0C0C0C 黒 | Windows Terminal 標準 | ○ Windows 出身者 |
Tokyo Night を選ぶ実利は **「数時間 Agent Teams を監視し続けても目が疲れない」**こと。彩度を抑えた紺紫背景に、Claude Code の出力(緑 / シアン / 黄系)が眩しすぎないコントラストで乗ります。Dracula のような高コントラスト系は最初は見やすく感じても、放置運用で長時間眺める用途では負荷が大きくなりがちです。
ターミナル画面で 右クリック → プロファイル → [テーマ名](PuTTY style paste ON 時は F10)で即時切替できます。Agent Teams の split-pane を見続ける開発では、暗背景 + Claude のシンタックスハイライトの相性が目の疲れに直結するため、最初に固めておく価値があります。
💡 PRIMARY と CLIPBOARD の使い分け — Linux X11 / XWayland には 2 つのクリップボードがあり、マウス選択で PRIMARY、
Ctrl+Cで CLIPBOARD に入ります。Ctrl+Shift+Vは CLIPBOARD からのペーストで、bash / tmux / vim / SSH 先のすべてで共通動作。Smart copy ON で両方に同時コピーされるため、Windows 出身者は迷わずこれを設定しておくと混乱が消えます。
公式 apt 未対応ツールの回避策パターン
Ubuntu LTS のリリース直後(2026 年 4 月の 26.04 など)は、サードパーティツールの公式 apt リポジトリ対応が数週間〜数か月遅れることがあります。CTS 実機で動作確認した回避策を、優先度順に整理します。
回避策の早見表
| # | パターン | 代表例 | 利点 | 欠点 |
|---|---|---|---|---|
| 1 | 公式インストールスクリプト(curl ... | bash) | rclone / Claude Code / Volta / uv | 最新版が即手に入る、署名込み | スクリプトの安全性は自己確認 |
| 2 | Docker 公式イメージで動かす | pgAdmin4 / Portainer | 隔離・撤退容易、永続化容易 | コンテナ運用の基礎が必要 |
| 3 | AppImage + AppArmor + Gear Lever | Obsidian / Cursor / Insomnia / Postman | フル GUI、Electron 系標準 | 第 5 回の AppArmor 設定必須 |
| 4 | Snap / Flatpak(公式配信あれば) | code (snap) / gear lever (Flatpak) | 標準パッケージシステム | サンドボックス制約あり |
| 5 | ソースビルド | tmux 3.5+(§5-4 参照) | 最新版確実 | ビルド依存解決の手間 |
実例 1: pgAdmin4 を Docker で動かす(apt 26.04 未対応の代表例)
pgAdmin4 の公式 apt は 2026-05 時点で Ubuntu 26.04(resolute)に未対応です(PostgreSQL 本体の apt は対応済みだが、pgAdmin4 は別リポジトリ管理)。Docker での運用が現実解:
# .docker/pgadmin/docker-compose.yml
services:
pgadmin:
image: dpage/pgadmin4:latest
container_name: pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PW}
PGADMIN_CONFIG_SERVER_MODE: "False"
ports:
- "5050:80"
volumes:
- pgadmin-data:/var/lib/pgadmin
network_mode: host
restart: unless-stopped
volumes:
pgadmin-data:
PGADMIN_PW=<your-password> docker compose -f .docker/pgadmin/docker-compose.yml up -d
# ブラウザで http://localhost:5050
# Server を新規登録: Host=localhost / Port=5432 / User=<your-pg-user>
| 設計ポイント | 理由 |
|---|---|
network_mode: host | ホスト側 docker-compose の PostgreSQL に localhost:5432 で直接接続(§5-3 の TEST_POSTGRESQL_CONNECTION_STRING と同じ筋) |
volumes: pgadmin-data | サーバー登録情報を永続化(コンテナ再作成しても接続が消えない) |
${PGADMIN_PW} | パスワードは .env で外出し、.gitignore でリポジトリ除外 |
PGADMIN_CONFIG_SERVER_MODE: "False" | ローカル運用は False で初回ログイン簡略化 |
実例 2: 公式スクリプトで導入(rclone / Claude Code / Volta / uv)
本シリーズで扱う以下のツールは、公式 apt ではなく公式スクリプトを直接叩く運用です:
| ツール | 取得方法 | 扱う章 |
|---|---|---|
| rclone | curl https://rclone.org/install.sh | sudo bash | 第 5 回 §5-1 |
| Claude Code | curl https://claude.ai/install.sh | bash | 第 4 回 §5-4 |
| Volta(Node.js バージョン管理) | curl https://get.volta.sh | bash | 第 4 回 §5-2 |
| uv(Python パッケージ) | curl https://astral.sh/uv/install.sh | sh | 第 4 回 §5-4 |
⚠️
curl ... \| bashの安全性確認 — サードパーティ提供のスクリプトを実行する以上、信頼できるドメイン(公式)からの HTTPS 取得であることを必ず確認。組織ポリシーで禁止されている場合は、curl > install.shでダウンロード →cat install.shでレビュー →bash install.shの 3 ステップに分けます。
実例 3: AppImage + AppArmor(GUI アプリ向け)
Obsidian / Cursor / Insomnia / Postman / VS Code AppImage 配布版などの Electron 系は AppImage で配布されており、Ubuntu 24.04+ 特有の起動エラー(libfuse2t64、apparmor_restrict_unprivileged_userns)への対処が必要です。詳細は **第 5 回「Phase 5-3: Obsidian を AppArmor で安全に動かす」**で扱っています(同じパターンが他の Electron 系にも横展開可能)。
公式 apt 対応後への戻し方
新規 LTS 用の apt リポジトリが公開されたら、Docker / スクリプト導入から apt に戻すことも可能:
# 戻す例(pgAdmin4 が将来 resolute 対応した場合)
docker compose -f .docker/pgadmin/docker-compose.yml down -v
sudo apt install -y pgadmin4-desktop
💡 業務開発機では「動くから変えない」も合理的 — Docker 運用が確立している場合、apt に戻す利点は限定的です。バージョン固定・複数同居・撤退容易という Docker の強みを失う側面もあります。新規 LTS リリース直後の数か月は Docker 運用、安定期に入ってから判断、というスタンスが現実的。
Windows との ZIP ファイル交換 — 7-Zip 公式版(7zz)に統一する
Linux ホストから Windows ユーザーへ ZIP を渡す場面で、Kubuntu 標準の Ark(Dolphin の右クリック「圧縮」)が作る ZIP には 2 種類の互換性問題があります。
問題 1: 日本語ファイル名の文字化け
Linux 側は UTF-8 でファイル名を書き込むのに対し、Windows のエクスプローラ標準解凍は Shift_JIS(CP932) を期待するため、日本語ファイル名が文字化けします。Windows 10 1809 以降の解凍 UI は UTF-8 を解釈できる経路もありますが、ZIP ヘッダの UTF-8 フラグ(汎用目的ビット 11)が立っていないと CP932 にフォールバックするため、Ark が出力する ZIP では症状が再発しがちです。
問題 2: パスワード付き ZIP の互換性エラー
Ark がデフォルトで使う暗号化方式(ZipCrypto / AES の組み合わせ)が、Windows 標準解凍機能の対応範囲と噛み合わないことがあり、「ファイルが破損しています」「展開できません」といった汎用エラーで開けない事例が出ます。
解決策: 7-Zip 公式 Linux 版(7zz)を使う
p7zip 系(7za / 7zr / 7zp)は 古い移植版で開発が止まっているため、現行運用では 公式 7-Zip の Linux ビルド 7zz を使うのが正解です。
| コマンド | パッケージ | 状態 |
|---|---|---|
7za / 7zp | p7zip-full(古い移植版) | 開発終了 |
7zr | p7zip(古い移植版・最小版) | 開発終了 |
7zz | 7zip-standalone(公式の最新版) | これを使う |
インストール(Ubuntu 26.04):
sudo apt install -y 7zip-standalone
UTF-8 + パスワード付き ZIP の作成:
# -tzip: ZIP 形式、-p: パスワード入力、-scsUTF-8: ファイル名を UTF-8 で書き込む
7zz a -tzip -p -scsUTF-8 archive.zip /path/to/files
エイリアスを ~/.bashrc に登録すると日常運用が楽になります:
echo 'alias zipenc="7zz a -tzip -p -scsUTF-8"' >> ~/.bashrc
source ~/.bashrc
# 使い方
zipenc archive.zip file1.txt file2.txt
zipenc archive.zip /path/to/directory/
💡 根本回避策: 日本語ファイル名を避ける — 開発プロジェクトの成果物(ソース・ビルド出力)は 英数字ファイル名で統一するのが最も確実です。ドキュメント類でどうしても日本語ファイル名が必要な場合に
7zzを使います。
🧰 Phase 5-2.5: ホスト側カーネルチューニング(ファイル監視 / FD 上限)
WSL2 / macOS / DevContainer 内では問題にならないのに、Ubuntu ネイティブだけで踏むハマりがあります。inotify のウォッチャー数とファイルディスクリプタ上限です。中規模リポジトリ(モノレポや Astro / Next.js のフルスタック)を VS Code で開くと、SCM パネルにファイル変更が反映されない・保存しても自動再起動が動かない、という症状が出ます。
既知の症状
VS Code 起動時 / リポジトリオープン時に:
Unable to watch for file changes in this large workspace folder.
Please follow the instructions link to resolve this issue.
Ctrl+Shift+P → "Developer: Show Logs..." → “Main” で:
unable to add filesystem watcher: ENOSPC: System limit for number of file watchers reached
なぜ Mac / Windows / DevContainer では起きないのか
| 環境 | ファイル監視機構 | デフォルト上限 |
|---|---|---|
| macOS | FSEvents / kqueue | リポジトリ単位で監視(実質無制限) |
| Windows | ReadDirectoryChangesW | リポジトリ単位(実質無制限) |
| Linux ネイティブ | inotify(ファイル単位) | fs.inotify.max_user_watches=8192(古いデフォルト)/ 数万 |
| Dev Container 内 | inotify(ホスト共有) | コンテナ起動時に --ulimit nofile で別途設定済 |
Linux の inotify は ファイル単位で監視枠を消費するため、node_modules や .git 配下を含む大規模リポジトリですぐに上限に当たります。
対処 1: ファイルディスクリプタ(fd)上限の引き上げ
/etc/systemd/system.conf と /etc/systemd/user.conf:
sudo bash -c 'cat >> /etc/systemd/system.conf << EOF
DefaultLimitNOFILE=524288
EOF'
sudo bash -c 'cat >> /etc/systemd/user.conf << EOF
DefaultLimitNOFILE=524288
EOF'
シェル側 /etc/security/limits.conf:
sudo bash -c 'cat >> /etc/security/limits.conf << EOF
* soft nofile 524288
* hard nofile 524288
EOF'
確認:
grep DefaultLimitNOFILE /etc/systemd/system.conf /etc/systemd/user.conf
# → 両方が DefaultLimitNOFILE=524288
対処 2: inotify 上限の引き上げ
⚠️
/etc/sysctl.confに書いても再起動でデフォルトに戻る罠(Ubuntu 26.04)Web 上の多くの解説記事が
/etc/sysctl.confへの追記を案内していますが、Ubuntu 26.04 ではsysctl.confは後方互換のためだけに残されており、起動時の読み込み対象から外れています。手動でsudo sysctl -pした直後は値が反映されているように見えるため気づきにくく、再起動するとデフォルト値(max_user_watches=65536/max_user_instances=128)に戻り File Watcher 警告が再発します。/etc/sysctl.d/配下に専用ファイルを作成するのが確実です。
コマンド 読み込み対象 sudo sysctl -p/etc/sysctl.confのみsudo sysctl --system/etc/sysctl.conf、/etc/sysctl.d/*.conf、/run/sysctl.d/*.conf、/usr/lib/sysctl.d/*.confをすべて再読込
/etc/sysctl.d/99-inotify.conf:
# 99- プレフィックスで読み込み順を最後にする
sudo tee /etc/sysctl.d/99-inotify.conf > /dev/null << 'EOF'
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 1024
EOF
# 即時反映(/etc/sysctl.d/ も読むため --system が必須)
sudo sysctl --system
確認:
sysctl fs.inotify.max_user_watches # 524288
sysctl fs.inotify.max_user_instances # 1024
💡 watches と instances の違い: watches は「監視できるファイル / ディレクトリの総数」、instances は「監視を作成できるプロセス数」。VS Code は内部で複数の inotify インスタンスを使うため両方の上限が必要で、
Your open applications want to watch too many files for changes!の警告は instances が枯渇した時に出ます。
反映の手順
limits.conf と systemd の DefaultLimitNOFILE は ログインセッション全体に効くため、一度ログアウト → 再ログインするか、再起動するのが確実です。
# シェルの fd 上限(再ログイン後)
ulimit -n
# → 524288
# systemd ユニットの fd 上限
systemctl show --property DefaultLimitNOFILE
# → DefaultLimitNOFILE=524288
⚠️ 再起動後の inotify 値の確認は必須(
/etc/sysctl.conf罠の検知手段)上記の罠(
sysctl.confだけに書いて再起動でデフォルトに戻る)を踏んでいないかは、再起動後に以下で確認できます。1024/524288のままなら OK、128/65536に戻っていたら/etc/sysctl.d/99-inotify.confの作成に失敗しています。cat /proc/sys/fs/inotify/max_user_instances # 1024 になっているか cat /proc/sys/fs/inotify/max_user_watches # 524288 になっているか
トラブルシューティング
設定変更後も VS Code でエラーが出続ける場合:
# VS Code を完全終了してから
pkill -f "code"
# 再ログイン後に VS Code を再起動
それでも改善しなければ、code --status で起動プロセスの fd 上限を確認:
code --status | grep -i "open files"
💡 524288 は妥当か — Chromium / Electron 系のドキュメント推奨値で、Mac / Windows のデフォルト挙動とほぼ同等の余裕を確保できます。RAM 64 GB 機なら問題なし。低スペック機でメモリ消費が気になる場合でも 262144 程度までは下げて構いません。
🪟 Phase 5-3: VS Code + DevContainer 拡張
ホストに VS Code、リポジトリ側に .devcontainer/ 一式を置く構成にします。ホスト OS の認証情報・SSH 鍵をどうコンテナに持ち込むか、そして DevContainer Features で言語ランタイムを宣言的に管理するのが、Linux 移行で押さえる勘所です。
必須拡張
| 拡張 | 役割 |
|---|---|
Dev Containers(ms-vscode-remote.remote-containers) | DevContainer の起動・接続 |
Docker(ms-azuretools.vscode-docker) | コンテナ・イメージの可視化 |
| GitLens / Git Graph | Git 履歴の可視化 |
.devcontainer/ の標準構成
最小構成は次の 3 ファイルです。
.devcontainer/
├── devcontainer.json # Features・mounts・customizations
├── docker-compose.yml # サービス定義(DB やキャッシュも一緒に立てる)
└── post-create.sh # コンテナ生成後に走る初期化スクリプト
devcontainer.json の典型構成(CTS で実運用している構成をベースに汎化):
{
"name": "cts-ec",
"dockerComposeFile": "docker-compose.yml",
"service": "devcontainer",
"workspaceFolder": "/workspace/cts-ec",
// 言語ランタイムは Features で宣言的に
"features": {
"ghcr.io/devcontainers/features/dotnet:2": { "version": "10.0" },
"ghcr.io/devcontainers/features/node:1": { "version": "22" },
"ghcr.io/devcontainers/features/python:1": { "version": "3.12" }
// ⚠ docker-outside-of-docker は Ubuntu 26.04 (resolute) 未対応のため使わない(§次節)
// Docker CLI は post-create.sh で `get.docker.com` から手動導入する
},
// ホスト OS から持ち込む認証情報・設定
"mounts": [
"source=${localEnv:HOME}/.aws,target=/home/vscode/.aws,type=bind,readonly",
"source=${localEnv:HOME}/.config/gcloud,target=/home/vscode/.config/gcloud,type=bind,readonly",
"source=${localEnv:HOME}/.config/glab-cli,target=/home/vscode/.config/glab-cli,type=bind,readonly",
"source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly",
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,readonly",
"source=${localEnv:HOME}/.microsoft/usersecrets,target=/home/vscode/.microsoft/usersecrets,type=bind,readonly",
"source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind"
],
"postCreateCommand": "bash .devcontainer/post-create.sh",
"containerEnv": {
"DOTNET_CLI_TELEMETRY_OPTOUT": "1",
"ASPNETCORE_ENVIRONMENT": "Development",
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
},
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csdevkit",
"ms-python.python",
"charliermarsh.ruff",
"ms-azuretools.vscode-docker",
"googlecloudtools.cloudcode",
"gitlab.gitlab-workflow",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"settings": {
"claudeCode.env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
},
"claudeCode.teammateMode": "auto"
}
}
}
}
ポイントは 4 つ:
| 観点 | 内容 |
|---|---|
features で言語ランタイム導入 | dotnet / node / python は Microsoft 公式の DevContainer Features を使うと、Dockerfile を書かずに宣言的に揃う |
| Docker CLI は post-create.sh で導入(§次節) | docker-outside-of-docker Feature は Ubuntu 26.04 (resolute) 未対応。docker-compose.yml で /var/run/docker.sock を bind mount しつつ、CLI 本体は post-create.sh で get.docker.com から入れる |
containerEnv と claudeCode.env の 二重指定 | VS Code から起動した Claude Code は後者、ホスト端末経由(docker exec → tmux → claude)で起動した場合は前者が効く。両方に書くのが安全 |
claudeCode.teammateMode: "auto" | tmux 内なら split-pane、外なら in-process に自動切替(公式推奨) |
⚠️ DevContainer リビルド時の docker-outside-of-docker 未対応エラー(Ubuntu 26.04 固有)
Docker Engine のアップデート等でイメージキャッシュが無効化されると DevContainer のリビルドが走ります。このときに ghcr.io/devcontainers/features/docker-outside-of-docker:1 を含めていると、Ubuntu 26.04 (resolute) を未サポートとして弾かれて Dev Container 自体が起動しなくなる罠があります。
実機で踏んだエラー:
(!) Unsupported distribution version 'resolute'. To resolve, please choose a compatible OS distribution
(!) Supported distributions include: trixie bookworm buster bullseye bionic focal hirsute impish jammy noble plucky
Feature のシェルスクリプト内部で /etc/os-release の VERSION_CODENAME を見てディストリビューションをハードコードチェックしているため、"moby": false や "version": "none" などのオプション指定では回避できません。Feature 側が Ubuntu 26.04 を取り込むまで待つしかない構造です。
回避策:Feature を外し、post-create.sh で Docker CLI を手動インストール
// devcontainer.json から削除
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} // ❌ 削除
}
.devcontainer/post-create.sh に以下を追加(§5-4 で紹介する post-create.sh の冒頭付近が定位置):
# Docker CLI(docker-outside-of-docker Feature が resolute 未対応のため手動導入)
if ! command -v docker &> /dev/null; then
curl -fsSL https://get.docker.com | sh
fi
Docker CLI 本体は get.docker.com の公式スクリプトで導入し、ホストの Docker daemon は docker-compose.yml で /var/run/docker.sock を bind mount することでそのまま使えます。Feature が担っていた「コンテナ内から docker compose / docker run」は機能維持されます。
💡 早く気付くための予兆:Docker Engine の
apt upgradeが走った日や、docker system pruneを実行した日に「Reopen in Container」が落ちる場合、ほぼこの罠です。エラー画面にUnsupported distribution version 'resolute'が出ていればビンゴ。Feature を外すパッチが当たるまでこの回避策を維持してください。
ホスト → コンテナへ持ち込む認証情報・設定
DevContainer は隔離された環境ですが、開発に必要な認証情報まで作り直しになると現実的ではありません。実体はホスト側に置いたまま、コンテナには bind mount で見せるのが標準パターンです。
| ホスト側パス | コンテナ側 | mode | 用途 |
|---|---|---|---|
~/.aws | /home/vscode/.aws | bind, readonly | AWS CLI / SDK の credentials |
~/.config/gcloud | /home/vscode/.config/gcloud | bind, readonly | gcloud CLI / Application Default Credentials |
~/.config/glab-cli | /home/vscode/.config/glab-cli | bind, readonly | GitLab CLI(glab)の認証 |
~/.gitconfig | /home/vscode/.gitconfig | bind, readonly | Git の user.name / user.email / 署名鍵 |
~/.ssh | /home/vscode/.ssh | bind, readonly | GitLab / GitHub / 本番 SSH |
~/.microsoft/usersecrets | 同左 | bind, readonly | .NET User Secrets(開発用シークレット) |
~/.claude | /home/vscode/.claude | bind(書き込み可) | Claude Code 設定・OAuth トークン(ホストと共有して再認証回避) |
💡 認証系はすべて
readonly、~/.claudeだけ書き込み可。 AWS / GCP / glab / Git / SSH / .NET User Secrets は誤書き換え事故を防ぐため readonly で固定します。Claude Code はセッション情報をコンテナ内で更新するため bind mount を書き込み可にし、ホスト側~/.claudeと直接共有します(VS Code 経由とホスト端末経由でセッションが揃う副次効果あり)。
SSH agent socket forwarding(鍵をコンテナに見せたくない場合)
~/.ssh を bind mount するのが一番シンプルですが、秘密鍵をコンテナに見せたくない運用なら、ホスト OS の ssh-agent を共有する方法もあります。
"mounts": [
"source=${localEnv:SSH_AUTH_SOCK},target=/ssh-agent,type=bind"
],
"containerEnv": {
"SSH_AUTH_SOCK": "/ssh-agent"
}
これでコンテナ内の git push がホスト agent 経由で署名され、秘密鍵ファイル自体はコンテナに見えません。
docker-compose.yml の最小例
services:
devcontainer:
image: mcr.microsoft.com/devcontainers/base:ubuntu
volumes:
- ..:/workspace/cts-ec:cached
- /var/run/docker.sock:/var/run/docker.sock
command: sleep infinity
network_mode: host
| 設定 | 役割 |
|---|---|
mcr.microsoft.com/devcontainers/base:ubuntu | Microsoft 公式の Ubuntu ベースイメージ。Features が前提とする vscode ユーザーが最初から作られている |
/var/run/docker.sock の bind mount | コンテナ内から ホストの Docker daemon を叩く経路。CLI は post-create.sh で get.docker.com から入れる(§前節の resolute 回避策) |
network_mode: host | localhost:5432 などホスト側のサービスにそのままアクセスできる。Linux ホスト前提(macOS / Windows では別経路) |
実例:network_mode: host 前提のテスト接続文字列
network_mode: host を採用する DevContainer + ホスト側 docker-compose の PostgreSQL という構成では、テスト側の接続文字列を 明示的に DevContainer に注入する必要があります。Testcontainers がデフォルトでコンテナを新規起動しようとして失敗するパターンです。
実機(CTS-EC リポジトリ・8682 件のテスト)で起きた症状:
Test summary: total: 8682, failed: 979, succeeded: 7674, skipped: 29
979 件すべて Failed to connect to 127.0.0.1:5432。原因は PostgreSqlFixture.cs が TEST_POSTGRESQL_CONNECTION_STRING 未設定時に Testcontainers でコンテナ起動を試みる設計だったこと。docker-outside-of-docker Feature 経由の Docker 操作と相性が悪く失敗します。
恒久対応として devcontainer.json の remoteEnv に環境変数を注入:
"remoteEnv": {
"TEST_POSTGRESQL_CONNECTION_STRING": "Host=localhost;Port=5432;Username=ctsec;Password=devpassword;Database=cts_ec"
}
これで Testcontainers ではなく ホスト側 docker-compose で起動済みの PostgreSQL に直接接続するようになります。修正後の実機計測:
Test summary: total: 8682, failed: 1, succeeded: 8652, skipped: 29
979 → 1 に改善。残り 1 件は DI 解決テストで DB とは無関係(環境非依存の問題なので別軸で対応)。
💡
network_mode: hostを採用したら必ずremoteEnvに接続文字列を書く。DevContainer の Testcontainers 系テストフィクスチャは「コンテナ環境では Testcontainers でコンテナ起動」をデフォルト挙動にしているライブラリが多いため、明示的に「ホスト側 DB を使え」と指示する必要があります。
DevContainer 起動と動作確認
cd ~/Projects/cts-ec
code .
# VS Code 内で Cmd/Ctrl+Shift+P → "Dev Containers: Reopen in Container"
または別端末から直接:
docker compose -f .devcontainer/docker-compose.yml up -d
起動後、コンテナ内で認証情報が見えるか確認:
# DevContainer 内
ls -la ~/.aws ~/.config/gcloud ~/.gitconfig ~/.claude
git config user.email
aws sts get-caller-identity 2>/dev/null || echo "AWS 未認証"
gcloud auth list 2>/dev/null || echo "GCP 未認証"
すべて通れば bind mount は成功です。ls -la の結果が ??? ??? のように壊れていたら、ホスト側のパスが存在しないか、(Google Drive 上に置く運用などで)ホストのファイルシステムから直接読めない場所を指している可能性があります。その場合は実体ファイルをホストの通常パス(~/.aws 等)に置き直してください。
VS Code 側のカスタマイズ(Windows 出身者向け)
Windows + VS Code から移ってきた場合に最初に詰まりやすい操作感ギャップを、移行後すぐに潰しておくと体感が大きく変わります。
統合ターミナルの選択コピー / 右クリックペースト
settings.json(Ctrl+, → 右上のアイコンで JSON モード):
{
// 選択即コピー(Windows ライク)
"terminal.integrated.copyOnSelection": true,
// 右クリックでペースト(Windows ライク)
"terminal.integrated.rightClickBehavior": "paste",
// 統合ターミナルの inotify 警告抑制(§5-2.5 で根本対処済みなら不要)
"files.watcherExclude": {
"**/node_modules/**": true,
"**/.git/objects/**": true,
"**/dist/**": true
}
}
KDE Plasma + Wayland でエッジダブルクリック縦/横最大化を有効化
Agent Teams の長い出力やコードレビューを 縦に最大化して一気に読みたい場面が頻発します。KDE Plasma 6.1.3 以降は、ウィンドウのリサイズエッジ(↕ カーソルが表示される領域)をダブルクリックすると その方向にのみ最大化される機能が標準搭載されていますが、VS Code はクライアントサイドデコレーション(CSD)を使うためデフォルトでは効きません。
settings.json に 1 行追加:
{
"window.titleBarStyle": "native"
}
VS Code を再起動すると、KDE のネイティブタイトルバーを使うようになり、ウィンドウのリサイズエッジダブルクリックで縦/横方向のみ最大化が効きます。
| アプリ | エッジダブルクリック縦最大化 | 対処 |
|---|---|---|
| Terminator | ✅ デフォルトで動作(SSD) | 不要 |
| VS Code | ✅ window.titleBarStyle: native で動作 | 上記 1 行追加 |
| Chrome / Chromium 系 | ❌ Wayland + CSD の制約(KDE Bug 502885) | 現時点では解決方法なし。第 3 回で示したキーボードショートカット割り当てで代替 |
⚠️ Chrome / Chromium 系は当面「待ち」 — Wayland セッションかつ CSD という組み合わせで、現時点では確実な解決方法がありません。Chrome 側のアップデート、もしくは KWin Bug 502885 の修正待ちが現実的な状況です。Chrome を含む全アプリで縦最大化が必要なら、第 3 回で示した KDE ショートカット割り当て(System Settings → ショートカット → KWin → 「ウィンドウを縦に最大化」)が現実解です。
💡 AI 駆動開発との相性が高い設定 — Agent Teams の出力は縦に長くなりがちで、ホスト OS のターミナル(Terminator)と VS Code の両方で縦最大化が即座にできると体感が大きく変わります。脱 Microsoft 移行時に併せて設定しておく価値があります。
Settings Sync は GitHub アカウントで統一
複数 PC(自宅 / オフィス / ノート)の VS Code 設定を揃えるなら Settings Sync を有効化。Microsoft アカウントと GitHub アカウントの 2 系統がありますが、GitHub アカウントに統一するのが脱 Microsoft の方針と整合します。
Ctrl+Shift+P → "Settings Sync: Turn On..." → "GitHub" を選択
⚠️ Microsoft アカウントと GitHub アカウントを混在させないこと。両方で Settings Sync を有効にすると、別系統で同期されて設定が分裂します。一度どちらかに決めたら、もう一方では
"Settings Sync: Turn Off"で解除。
C# Dev Kit の扱い
.NET MAUI / ASP.NET Core 開発では C# Dev Kit が Visual Studio 並みの体験を提供しますが、ライセンス的に商用利用で別途判断が必要です(個人利用は無料、企業利用は有料)。脱 Microsoft の方針なら:
| 拡張 | ライセンス | 推奨 |
|---|---|---|
C# Dev Kit(ms-dotnettools.csdevkit) | 商用は別途有償 | △ 利用可否を法務で確認 |
C# Extension(ms-dotnettools.csharp) | MIT | ◎ 標準 |
| Roslyn 言語サーバー(C# Extension に内包) | MIT | ◎ |
C# Dev Kit を入れずに済ませる場合、settings.json:
{
"dotnet.preferCSharpExtension": true,
"omnisharp.useModernNet": true
}
localhost URL を外部 Chrome で開く
VS Code の Linux 版はデフォルトで localhost URL を内蔵 Simple Browser で開きます(workbench.browser.openLocalhostLinks が既定で有効)。Windows 版は外部ブラウザが起動するため、OS をまたいで Settings Sync するチームでは挙動が割れる点に注意。Web 開発で Chrome DevTools / 拡張機能を使いたい・Windows と挙動を揃えたい場合は、明示的に外部ブラウザへ振ります。
最小設定(settings.json):
{
// Ctrl+クリックを Simple Browser ではなくデフォルトブラウザに渡す
"workbench.browser.openLocalhostLinks": false
}
Settings Sync 経由で Mac / Windows 側にも同期されますが、各 OS のデフォルト挙動が違うため、全マシンで同じ挙動に揃えたい場合に明示するのが安全です。
補助的に、VS Code Remote / DevContainer で外部 URI Opener を Chrome 固定にしたい場合:
{
"remote.localPortHost": "localhost",
"workbench.externalUriOpeners": {
"localhost": "chrome"
}
}
DevContainer で ポート転送時に自動でブラウザを起動したいなら devcontainer.json:
"forwardPorts": [3000, 5432],
"portsAttributes": {
"3000": {
"label": "Web App",
"onAutoForward": "openBrowser"
}
}
openBrowser にすると ホスト OS のデフォルトブラウザで自動起動します(Wayland + Chrome の構成で動作確認済み)。
💡 デフォルトブラウザの確認: 外部ブラウザが起動しない場合、
xdg-mime query default x-scheme-handler/httpで確認。google-chrome.desktop等が返れば OK。
⚠️ DevContainer 内ターミナルで localhost:4200 が 4201 に書き換えられて CORS が落ちる罠(Linux ホスト固有)
症状: DevContainer 内のターミナルで http://localhost:4200 を Ctrl+クリックすると、VS Code が localhost:4201 に書き換えてブラウザを開きます。「ポート」タブを見るとコンテナ内 listen の 4200 が ホスト側 4201 に自動プロキシ転送されており、Ctrl+クリック時に URL もそのプロキシ先に置き換わる挙動です。
実害: Angular dev server が localhost:4200 想定で動いていても、ブラウザのアドレスが localhost:4201 になるため、API への CORS リクエストの Origin が http://localhost:4201 となり、API 側の CORS 許可リストから外れて CORS エラーで API 呼び出しが全滅します。
Microsoft.AspNetCore.Cors.Infrastructure.CorsService[6]
Request origin http://localhost:4201 does not have permission to access the resource.
Linux 固有である理由: network_mode: host は Linux 専用機能で、Windows / Mac の Docker Desktop では VS Code の自動転送ロジックが別経路で動作するため、本問題は Linux ホスト固有です。Windows + WSL2 から Ubuntu ネイティブに移ったタイミングで初めて踏む罠で、原因が見えにくい。
対処: .devcontainer/devcontainer.json に 2 設定をセットで 入れます:
{
"customizations": {
"vscode": {
"settings": {
// ターミナルで URL を Ctrl+クリックした時の URL 書き換え(4200 → 4201)を止める
"remote.forwardOnOpen": false
}
}
},
// コンテナ内ポート listen を検知して、ポートタブに自動で転送を作る挙動を止める
"otherPortsAttributes": {
"onAutoForward": "ignore"
}
}
| 設定 | 役割 |
|---|---|
customizations.vscode.settings.remote.forwardOnOpen: false | ターミナル URL の 書き換え を止める(4200 → 4201 を防ぐ) |
otherPortsAttributes.onAutoForward: "ignore" | ポートタブに 転送を作る 挙動を止める |
両方が揃って初めて完全に動作します(VS Code 1.119 系で検証)。remote.forwardOnOpen 単独だと URL 書き換えは止まるがポートタブには転送が残り、中途半端な状態になります。
⚠️ 設定変更後は Rebuild Container が必要(Reopen in Container では VS Code Server に反映されません)。なお古い記事に登場する
remote.autoForwardPorts: falseは VS Code 1.119 系で 廃止済みで効きません。forwardPorts: []も明示の必要はなく、挙動は変わりません。
Dev Container ワンクリック起動(Windows ジャンプリスト相当)
KDE Plasma のタスクバーから code のアイコンを右クリック → “新規ウィンドウ” のショートカットに、特定リポジトリを直接 DevContainer で開くコマンドを登録できます。
~/.local/share/applications/code-cts-ec.desktop:
[Desktop Entry]
Name=VS Code — cts-ec (DevContainer)
Exec=code --folder-uri vscode-remote://dev-container%2B$(printf '%s' "$HOME/Projects/cts-ec" | xxd -p | tr -d '\n')/workspace/cts-ec
Type=Application
Icon=code
Categories=Development;
update-desktop-database ~/.local/share/applications/
KDE Plasma のアプリメニューから検索可能になり、ピン留めもできます。Windows のジャンプリストに近い使い勝手。
Ubuntu ネイティブ運用の体感差
| 操作 | Windows + WSL2 + Docker Desktop | Ubuntu + Docker Engine |
|---|---|---|
docker compose up -d 初回 | 30〜60 秒 | 8〜15 秒 |
pnpm install(中規模) | 90 秒 | 40 秒 |
| ホットリロード(Vite / Next.js) | ファイル保存後 0.8〜1.5 秒 | 0.2〜0.4 秒 |
| メモリ消費(アイドル) | 30 GB ベース | 2〜4 GB ベース |
| Agent Teams 並列稼働のハード上限(64 GB ホスト・実機) | 約 3 並列が限界(.wslconfig で 48 GB 拡張済み) | 約 9 並列まで余裕 |
💡 数値は実機計測の概算ですが、「明らかに体感が違う」レベルの差になります。WSL2 のオーバーヘッドは「気にならない」と言われがちですが、それは比較対象がないからで、ネイティブに移ると違いがはっきりわかります。Linux 上では bind mount のネイティブ速度になり、Windows + WSL2 構成で発生していた
node_modulesの I/O 遅延もほぼ消えます。⚠️ 「ハードで動かせる並列数」と「実運用での並列数」は別軸: Ubuntu ネイティブでハード上は 9 並列が動いても、Claude Code(Max 20× プラン)の 5 時間制限と週間制限が先に枯渇します。実機では 3 ステアリング × 3 Agent = 9 並列を回したら 5 時間制限が即アウト、週間制限も 1 日で 2 日分消費して運用断念した実例があります。現実的な運用上限は 3〜4 並列で、これは「Linux 側の前提」とは別軸(サブスク側の制限)です。プラン選定とレート最適化の詳細は ステアリング駆動開発 シリーズの管轄。
つまり Windows はハードで詰まる / Ubuntu はサブスクで詰まる という二段の壁が見えます。ハード側のボトルネックを Linux ネイティブで解消した上で、サブスク側の制限と向き合うのが正しい順序です。
Docker Desktop のメモリ上限引き上げ(Windows ホストの対症療法)
Windows + Docker Desktop でメモリ不足にぶつかった場合、%USERPROFILE%\.wslconfig(Windows ホーム直下)に以下を書くと WSL2 側の上限を引き上げられます:
[wsl2]
memory=48GB
swap=8GB
processors=16
設定後は PowerShell で:
wsl --shutdown
# その後 Docker Desktop を再起動
| 観点 | 値 |
|---|---|
| 64 GB ホストでの安全圏 | memory=48GB(Windows 側に 8〜12 GB を残す必要があるため) |
| Docker Desktop での認識値 | 48 GB 割当 ≒ 約 46 GB(OS オーバーヘッドで 4〜5% 引かれる) |
| 上限まで攻める場合 | memory=56GB まで上げられるが、Windows 側(VS Code / ブラウザ / Docker Desktop ホストプロセスで合計 8 GB 前後)が苦しくなる |
⚠️ メモリ拡張は対症療法。 ホスト側からの割り当ては増やせますが、二重仮想化のオーバーヘッドはそのまま残るため、根本解決にはなりません。同一 64 GB ハード上で Windows + Docker Desktop(48 GB 拡張)の 3 並列 ≒ Ubuntu ネイティブの 9 並列と、3 倍以上の差が実機で出ます。Linux ネイティブへの移行が本筋です。
🤖 Phase 5-4: Claude Code 導入と post-create.sh
本記事の方針では Claude Code は DevContainer 内で動かす のが前提です(理由は §5-5)。Claude Code 本体・tmux・必要な CLI 群(gcloud / aws / glab / uv 等)は post-create.sh で都度セットアップするのが、CTS で標準化した運用です。Dockerfile に詰め込むとベースイメージの更新と各ツールのバージョン更新のたびに再ビルド待ちになるため避けます。
Claude Code は 公式ネイティブインストーラー で入れる
公式の install.sh を使うと、npm グローバル経由のインストール時に発生しがちな権限・PATH まわりのハマりを回避できます。
curl -fsSL https://claude.ai/install.sh | bash
export PATH="$HOME/.local/bin:$PATH"
claude --version
💡
npm install -g @anthropic-ai/claude-codeよりもinstall.shを推奨。 公式インストーラーは~/.local/binにバイナリを配置するため、コンテナ内vscodeユーザーで sudo 不要。PATHを.bashrcに通しておけば、ホスト端末経由docker exec → tmux → claudeのフローでも素直に呼べます。
tmux は ソースビルドで最新版
Ubuntu の apt install tmux で入るのは古めのバージョンで、Agent Teams(split-pane モード)の挙動が安定しないことがあります。tmux 3.5 以降をソースビルドするのが確実です。
TMUX_VERSION="3.6a" # 公式リリースから最新を取得して固定
sudo apt-get install -y libevent-dev ncurses-dev build-essential bison pkg-config
curl -sL "https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/tmux-${TMUX_VERSION}.tar.gz" \
| tar xz -C /tmp
cd /tmp/tmux-${TMUX_VERSION} && ./configure -q && make -j"$(nproc)" -s && sudo make install
~/.tmux.conf の典型設定(マウススクロール有効化・履歴拡張・Agent Teams のレイアウト整え):
set -g mouse on
set -g history-limit 50000
set -g status on
set -g default-terminal "tmux-256color"
setw -g mode-keys vi
# Agent Teams レイアウト自動調整
set -g main-pane-width '50%'
set-hook -g after-split-window "select-layout main-vertical"
post-create.sh の典型構成
#!/bin/bash
set -e
# Docker CLI(docker-outside-of-docker Feature が Ubuntu 26.04 (resolute) 未対応のため手動導入)
# /var/run/docker.sock は docker-compose.yml で bind mount 済みなので、CLI 本体だけここで入れる
if ! command -v docker &> /dev/null; then
curl -fsSL https://get.docker.com | sh
fi
# Google Cloud SDK
if ! command -v gcloud &> /dev/null; then
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
| sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install -y google-cloud-cli
fi
# AWS CLI
if ! command -v aws &> /dev/null; then
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip
unzip -q /tmp/awscliv2.zip -d /tmp && sudo /tmp/aws/install
rm -rf /tmp/awscliv2.zip /tmp/aws
fi
# GitLab CLI(glab)
# uv(Python パッケージマネージャー)
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
# Claude Code(公式ネイティブインストーラー)
if ! command -v claude &> /dev/null; then
curl -fsSL https://claude.ai/install.sh | bash
fi
# tmux(最新版をソースビルド)
# … 上記の TMUX_VERSION ブロック …
# ~/.tmux.conf 配置
# … 上記の ~/.tmux.conf を配置 ~
# Claude Code 設定
mkdir -p ~/.claude
cat > ~/.claude/settings.json << 'EOF'
{
"skipDangerousModePermissionPrompt": true,
"teammateMode": "auto",
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
}
}
EOF
# .bashrc に PATH と alias
ALIAS_MARKER="# Claude Code alias"
if ! grep -q "$ALIAS_MARKER" "$HOME/.bashrc" 2>/dev/null; then
cat >> "$HOME/.bashrc" << 'EOF'
# Claude Code alias
export PATH="$HOME/.local/bin:$PATH"
export LANG=C.UTF-8
alias cc="claude --dangerously-skip-permissions"
EOF
fi
echo "post-create.sh: 完了"
💡
alias cc="claude --dangerously-skip-permissions"を入れておくと、DevContainer 内でccの 2 文字で bypassPermissions 付きの起動になります。DevContainer 内でしか効かないため、ホスト OS で誤って打つ事故を構造的に防げます。
Agent Teams 環境変数の二重指定
devcontainer.json の containerEnv と customizations.vscode.settings.claudeCode.env の 両方に CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 を書きます(§5-3 の例参照)。
| 起動経路 | 効くのは | 必要な設定 |
|---|---|---|
| VS Code 内蔵の Claude Code 拡張から起動 | customizations.vscode.settings.claudeCode.env | こちらだけだと CLI 直叩きで効かない |
ホスト端末 → docker exec → tmux → claude(本記事推奨) | containerEnv | こちらだけだと VS Code 経由で効かない |
teammateMode は "auto"(tmux 内なら split-pane、外なら in-process に自動切替)が公式推奨です。
認証
DevContainer に最初に入ったときに claude を実行すると、ブラウザで OAuth ログインを促されます。Ubuntu ホストの Chrome で完了すれば、認証情報は ~/.claude に保存されます。
§5-3 で ~/.claude をホストと bind mount で共有しているため、コンテナを作り直しても再ログイン不要、かつ VS Code 経由とホスト端末経由でセッションが揃うメリットもあります。
🛡️ Phase 5-5: Linux 固有の起動経路と DevContainer 隔離
本記事の主題は Linux 固有のハマりどころです。Claude Code 本体の使い方や Agent Teams の運用ノウハウは AI 駆動開発シリーズ側にすべて書かれているため、ここでは Linux ホスト + DevContainer + tmux の起動経路だけを正確に押さえます。
なぜ DevContainer 隔離が必須か(bypassPermissions の安全装置)
bypassPermissions は破壊的操作(rm -rf, git push --force, git reset --hard 等)を 無確認実行する危険機能です。生産性は劇的に上がる代わりに、ホスト OS で動かすと事故時の被害が制御不能になります。
| 環境 | bypassPermissions の効果 | 事故時の被害範囲 |
|---|---|---|
ホスト OS で claude --dangerously-skip-permissions | 確認なし高速実行 | ホスト全体(最悪 OS 再インストール) |
| DevContainer 内で bypassPermissions(採用) | 確認なし高速実行 | コンテナ内のみ(docker compose down -v で復旧) |
⚠️ bypassPermissions をホスト OS で動かすのは絶対 NG。 一度
rm -rf ~系の事故を起こすと丸 1 日復旧不可になります。DevContainer に閉じ込めると コンテナを破棄して再作成(5 分) で済むため、安全装置として桁違いに効きます。これは AI 駆動開発の生産性を支える Linux 側の前提条件です。
なぜ VS Code 統合ターミナルではなく Konsole から起動するのか
VS Code の統合ターミナルから tmux を起動すると、tmux pane と VS Code のターミナル制御コードが競合して表示が崩れます。これは Claude Code 公式ドキュメントでも 環境制限として明記されています。
| 起動経路 | 安定性 |
|---|---|
VS Code 統合ターミナル → docker exec → tmux | ❌ 公式に非対応(表示崩壊・色化け・カーソル位置ずれ) |
| VS Code Dev Containers の “Open in Terminal” | ❌ 同上 |
Ubuntu ホスト端末(Konsole / GNOME Terminal)→ docker exec → tmux | ✅ 安定(Linux + tmux は公式対応) |
💡 VS Code はコード編集専用、tmux + Claude Code は別端末 が安定運用の鉄則。Ubuntu に移ったことで PowerShell + WSL の 2 段経由は不要になり、KDE Plasma の Konsole から直接
docker execできるようになります(後述の比較表参照)。
起動フロー

起動コマンド(Linux 固有部分のみ)
# ① Konsole / GNOME Terminal を開く(VS Code 統合ターミナルではない)
# ② プロジェクトに移動
cd ~/Projects/cts-ec
# ③ DevContainer を起動(VS Code から開いていれば既に起動済み)
docker compose -f .devcontainer/docker-compose.yml up -d
# ④ DevContainer に入る
docker exec -it -u vscode cts-ec_devcontainer-devcontainer-1 bash
# === ここから DevContainer 内 ===
# ⑤ ロケール(コンテナの environment で設定済みなら不要)
export LANG=ja_JP.UTF-8
# ⑥ tmux セッション(start-directory を明示してプロジェクト直下で起動)
tmux new -s claude -c /workspace/cts-ec
# ⑦ Claude Code を起動
claude
⚠️
tmux new -c <directory>は必須 — Claude Code は 起動時の CWD(カレントディレクトリ) からプロジェクト固有の slash commands / skills(.claude/commands//.claude/skills/)を解決します。公式ドキュメントに「Claude Code operates within the directory where you launch it」と明記されています。
-cを省略すると、tmux 新規セッションの CWD は 呼び出し元シェルの状態に依存します(特に Tailscale + SSH 越しのdocker exec → tmux → claude経路では$HOME(/home/vscodeなど)になりがち)。この場合 プロジェクトの slash commands が認識されない症状が出ます。tmux man page のnew-session [-c start-directory]で公式保証された挙動なので、常に明示するのが安全です。
なお、既存セッションへの再アタッチは tmux a -t claude でカレントディレクトリは保持されるので、-c の指定は 新規作成時のみで OK。
📌 Windows 時代との差分: WSL 環境では
PowerShell → wsl → docker exec → tmux → claudeの 5 段経由でしたが、Ubuntu ホストではKonsole → docker exec → tmux → claudeの 3 段に短縮されます。日々の起動コストが地味に効きます。
Claude Code の使い方は AI 駆動開発シリーズへ
ここから先の以下のトピックは、本記事の主題(Linux 固有のセットアップ)から外れるため、「ステアリング駆動開発」シリーズで詳述しています。
| トピック | 参照先 |
|---|---|
| 12 体のサブエージェント構成 / Delegate Mode / コスト戦略 / Agent Teams 運用 | Agent Teams 編:12 体のサブエージェント構成とマルチエージェント並列協調 |
bypassPermissions + Remote Control による「スタバから監視・承認」運用 | Claude Code リモート操作(Tailscale + SSH) |
| 7 層ハーネスエンジニアリングの設計指針 | Claude Code 7 層ハーネスエンジニアリング |
💡 本記事は「Claude Code を Linux で動くようにする」までが範囲。 「Claude Code をどう使い倒すか」は AI 駆動開発シリーズの管轄です。重複させないことで、双方の記事をシンプルに保てます。
🛡️ リスクと対策表(Ubuntu 開発環境セットアップ 編)
| リスク | 影響度 | 対策 |
|---|---|---|
| ホスト OS で bypassPermissions を有効化 | 致命的 | 必ず DevContainer 内のみで有効化。ホストで --dangerously-skip-permissions を打たない |
| VS Code 統合ターミナルから tmux 起動 | 中 | Konsole / Terminator / GNOME Terminal から docker exec で起動(公式に非対応) |
~/.aws / ~/.ssh をコンテナ内で誤書き換え | 中 | bind mount を readonly で持ち込む(~/.claude 以外はすべて readonly) |
docker グループ付与の権限過大 | 中 | 個人開発機のみ。共有サーバーでは付与しない |
apt install tmux が古く Agent Teams が不安定 | 中 | tmux 3.5 以降を post-create.sh でソースビルド |
| Agent Teams 環境変数が起動経路によって効かない | 中 | containerEnv と customizations.vscode.settings.claudeCode.env の両方に書く |
| DevContainer 内の Claude 認証情報が消える | 低 | ~/.claude をホスト側 ~/.claude と bind mount で共有 |
node_modules の I/O が遅い | 低(解消済み) | Ubuntu ネイティブで bind mount が高速化されているため WSL2 比で気にならない |
| VS Code SCM パネルがファイル変更を取りこぼす | 中(Linux ネイティブ固有) | fs.inotify.max_user_watches=524288 + DefaultLimitNOFILE=524288(§5-2.5 参照) |
inotify 上限が再起動でデフォルトに戻る(/etc/sysctl.conf 罠) | 中(Ubuntu 26.04 固有) | /etc/sysctl.conf は起動時に読み込まれないため、必ず /etc/sysctl.d/99-inotify.conf に置き sudo sysctl --system で反映。再起動後 cat /proc/sys/fs/inotify/max_user_instances が 1024 のままか確認(§5-2.5 参照) |
| DevContainer 内ターミナル URL の自動書き換え(4200 → 4201)→ CORS エラー | 中(Linux ホスト固有) | devcontainer.json に remote.forwardOnOpen: false と otherPortsAttributes.onAutoForward: "ignore" の 2 設定を併記し Rebuild Container(§5-3 「VS Code 側のカスタマイズ」参照) |
network_mode: host で Testcontainers が衝突 | 中 | devcontainer.json の remoteEnv に TEST_POSTGRESQL_CONNECTION_STRING を明示 |
docker-outside-of-docker Feature が Ubuntu 26.04 (resolute) 未対応 → リビルドで Dev Container 起動不能 | 中(Ubuntu 26.04 固有) | Feature を削除し、.devcontainer/post-create.sh で curl -fsSL https://get.docker.com | sh により Docker CLI を手動導入。/var/run/docker.sock は docker-compose.yml で bind mount 維持(§5-3 参照) |
localhost URL が VS Code 内蔵 Simple Browser で開かれる | 低(Linux デフォルト挙動) | settings.json に workbench.browser.openLocalhostLinks: false。Settings Sync で OS 間の挙動を統一 |
| Settings Sync が Microsoft / GitHub の両系統で分裂 | 低 | GitHub アカウントに統一(脱 Microsoft 方針と整合) |
| Ark(Dolphin の圧縮)で作成した ZIP が Windows で文字化け / 解凍エラー | 中(Windows 連携時) | 7-Zip 公式版 7zz(7zip-standalone)を使う。7zz a -tzip -p -scsUTF-8 で UTF-8 フラグ付き ZIP を生成、zipenc エイリアス推奨(§5-2 参照) |
| C# Dev Kit の商用ライセンス | 低 | 標準 C# Extension(MIT)で代替、または法務確認 |
tmux new -s <name> で -c 省略 → SSH 越しに project commands が認識されない | 中 | tmux new -s claude -c /workspace/<repo> で必ず start-directory を明示。Claude Code は起動時 CWD から .claude/commands / .claude/skills を解決する(公式仕様) |
✅ 第 4 回まとめ
- Docker Engine をネイティブで導入することで、WSL2 + Docker Desktop の二重仮想化が消え、メモリ・I/O が大幅に改善
- VS Code は脱 Microsoft の例外として残すのが現実解。Dev Containers 拡張で WSL 時代と同じ DevContainer 構成がそのまま動く
- ホスト側カーネルチューニング(
fs.inotify.max_user_watches=524288+DefaultLimitNOFILE=524288) が Linux ネイティブ固有の必須前提。WSL2 / macOS では発生しないが、Linux ネイティブで VS Code の SCM パネルがファイル変更を取りこぼす症状が出る - ターミナルは Terminator + PuTTY style paste + Copy on selection が Windows 出身者の操作感ギャップを最小化。Gogh で Tokyo Night など暗背景テーマを揃えると Agent Teams 監視が楽
- 言語ランタイムは DevContainer Features で宣言的に(dotnet / node / python)。Dockerfile を書かずに済む。ただし
docker-outside-of-dockerFeature は Ubuntu 26.04 (resolute) 未対応のため使わず、Docker CLI はpost-create.shでget.docker.comから導入し、/var/run/docker.sockはdocker-compose.ymlで bind mount - 認証情報は ホスト側に置いたまま
mountsで readonly bind mount(~/.aws~/.config/gcloud~/.config/glab-cli~/.gitconfig~/.ssh~/.microsoft/usersecrets)。~/.claudeだけ書き込み可で bind、ホスト ↔ コンテナでセッション共有 network_mode: hostを採用したらremoteEnvにTEST_POSTGRESQL_CONNECTION_STRINGを明示。Testcontainers のデフォルト挙動(コンテナ起動)と衝突する- Claude Code は公式ネイティブインストーラー(
curl https://claude.ai/install.sh | bash)で導入。tmuxは 3.5 以降をソースビルドして Agent Teams を安定動作させる - Agent Teams 環境変数は
containerEnvとclaudeCode.envの両方に書く。VS Code 経由とホスト端末経由のどちらの起動でも効くように - bypassPermissions は必ず DevContainer 内のみで運用。ホスト OS で動かすと事故時に丸 1 日復旧不可。
alias cc="claude --dangerously-skip-permissions"を.bashrcに置けば、コンテナ内でだけ短縮起動できる - VS Code 統合ターミナルからは tmux NG(公式に非対応)。Konsole / Terminator / GNOME Terminal から
docker exec → tmux → claudeの 3 ステップが鉄則 tmux new -s claude -c /workspace/<repo>で start-directory を必ず明示。Claude Code は起動時 CWD からプロジェクト固有の.claude/commands//.claude/skills/を解決する仕様(公式ドキュメントで明記)。-c省略すると SSH 越しでプロジェクトコマンドが認識されない- VS Code Settings Sync は GitHub アカウントに統一。Microsoft アカウントとの混在は分裂の原因
- 本記事は Linux 固有のセットアップ に絞り、Agent Teams の使い方や Remote Control によるリモート運用などの AI 駆動開発ノウハウは「ステアリング駆動開発」シリーズで詳述(重複させない)
- Ubuntu 26.04 LTS + KDE Plasma + Docker Engine + DevContainer + Claude Code のスタックは 2031 年まで OS サポートありで、長期安定運用が可能
これで OS の土台 + 業務 PC 化 + 開発スタック までが完成しました。次回からはデータ層・コラボレーション・本番移行のフェーズに入ります。
次回「Ubuntu データ共有セットアップ — rclone Drive + QNAP NAS + Obsidian Vault」では、rclone + systemd ユーザーサービスによる Google Drive 自動マウント、QNAP NAS への NFS / SMB ハイブリッド接続、AppArmor プロファイルで Obsidian など Electron AppImage を安全に動かす手順までを扱います。
📚 シリーズ記事
| # | タイトル | 内容 |
|---|---|---|
| 1 | 脱Microsoft 全体戦略・総論 | 値上げ背景・ロードマップ・コスト・リスク |
| 2 | Windows 11 + Ubuntu デュアルブート構築 | M.2 物理分離・KDE Plasma・GRUB |
| 3 | Ubuntu PC セットアップ | NVIDIA ドライバ事後導入・サスペンド無効化・Bluetooth・ターミナル選定 |
| 4 | Ubuntu 開発環境セットアップ(本記事) | Docker Engine ネイティブ・DevContainer 機密情報マウント・Claude Code |
| 5 | Ubuntu データ共有セットアップ | rclone Drive + systemd・QNAP NFS/SMB・Obsidian Vault・AppArmor |
| 6 | Microsoft 365 → Google Workspace 全面移行 | メール・SSO・MFA・MX 切替 + OneDrive → Google Drive ファイル移行・共有リンク・コスト最適化 |
| 7 | Ubuntu リモート開発セットアップ | Tailscale + SSH + VS Code Remote / DevContainer・Wayland ホスト |
| 8 | ActiveReports → Playwright + Scriban 移行(公開予定) | 帳票エンジン置換・バーコード・印刷品質 PDF |
| 9 | コスト削減効果と 1 年運用レビュー(公開予定) | 実績ベースのコスト・運用上のハマりどころ |
🔗 関連リソース
CTS-KB 内の関連記事
- 脱Microsoft 全体戦略・総論 — シリーズ第 1 回
- Windows 11 + Ubuntu デュアルブート構築 — シリーズ第 2 回(前提となる OS 構築)
- Claude Code 7 層ハーネスエンジニアリング — Claude Code を多層構造で扱う設計指針
- Agent Teams 編:12 体のサブエージェント構成とマルチエージェント並列協調 — 本記事の Agent Teams の中身
- Claude Code リモート操作(Tailscale + SSH) — 放置運用と相性の良いリモート接続
関連用語
- Docker — Linux カーネル機能を活用する OSS の仮想化プラットフォーム
- DevContainer — リポジトリに同梱する開発環境コンテナ仕様(本記事の主題)
- Claude Code — Anthropic 公式の CLI コーディングエージェント
- Agent Teams — Claude Code の実験的並列実行機能(運用詳細は AI 駆動開発シリーズ)
- Ubuntu — 開発デファクトの Linux ディストロ
- tmux — Agent Teams のバックエンドとなる端末多重化ツール
- WSL2 — Windows での代替実行環境(移行元)
- Wayland / X11 — Linux のディスプレイサーバー
外部リソース
- Docker Engine 公式ドキュメント — Ubuntu 向けインストール手順
- Volta — Rust 製の Node.js バージョン管理ツール
- Visual Studio Code — Microsoft 公式の deb パッケージ
- VSCodium — VS Code のテレメトリ無効ビルド
- Anthropic Claude Code — 公式ドキュメント