家のサーバーに AI エージェント用の仮想マシンを作る ― Claude Code・Codex・Chrome を、壊れても困らない場所で動かす
これまで、AI エージェント(Claude Code や Codex に作業を任せる使い方)や定期的な処理は、ずっと電源を入れている Mac で動かしてきました。これを、家のサーバーの中に作った仮想マシンに移していくことにしました。この記事は、その仮想マシンを作り、Claude Code・Codex・Chrome を使えるようにしたところまでの記録です。
仮想マシンは、1台のパソコンの中で、別のパソコンのように動く仕組みです。これまでの記事:Orca で複数の AI をまとめる、AI から Chrome を動かす、4つの AI の使用状況をトップページに表示する。
なぜ仮想マシンに移すのか
- 止まらない:家のサーバーは一日中動いている。Mac はアップデートやスリープで止まることがある
- 壊せる範囲を限れる:AI に確認なしで作業させても、影響は仮想マシンの中だけで済む。家のサーバー本体や、すでに動いている AD(利用者やパソコンをまとめて管理する仕組み)の仮想マシンには入れないようにする
- Mac を軽くできる:AI を何本も動かす重い作業を、Mac から外せる
全体の構成

家のサーバーには、Linux 標準の仮想マシンの仕組み(KVM)が入っていて、AD 用の仮想マシンがすでに動いていました。そこに AI 用の仮想マシン(以下 vm1)を1台足しました。ノート(Obsidian の保管場所)は Google ドライブにあり、Linux からは扱いにくいので、今までどおり Mac に置いたままにしています。
1. 仮想マシンを作る
Ubuntu 24.04 の「クラウド用イメージ」と、初期設定を書いたファイル(cloud-init)を使いました。インストールの画面を進める必要がなく、起動すると利用者や IP アドレスが設定された状態で立ち上がります。大きさは 4 CPU・メモリ 8GB・ディスク 100GB にしました。
#cloud-config
hostname: vm1
timezone: Asia/Tokyo
users:
- name: manager # 管理用。sudo を使える
groups: [sudo]
sudo: "ALL=(ALL) NOPASSWD:ALL"
lock_passwd: true
ssh_authorized_keys: [ssh-ed25519 AAAA...]
- name: ai # AI を動かす用。sudo は使えない
lock_passwd: true
ssh_authorized_keys: [ssh-ed25519 AAAA...]
ssh_pwauth: false # パスワードでのログインは受け付けない
virt-install --name vm1 --osinfo ubuntu24.04 --vcpus 4 --memory 8192 --cpu host-passthrough \
--disk path=vm1.qcow2,bus=virtio --disk path=vm1-seed.iso,device=cdrom \
--network bridge=br0,model=virtio --graphics none --import --noautoconsole
virsh autostart vm1 # 家のサーバーの起動とともに自動で起動する
利用者は2つに分けました。AI を動かす利用者は sudo(管理者の操作)を使えないので、AI がうっかりシステムの設定を変えることはありません。パッケージを入れるなどの管理は、管理用の利用者で行います。
2. Claude Code と Codex を入れて、ログインする
# Claude Code(公式のインストーラー)
curl -fsSL https://claude.ai/install.sh | bash
# Codex(Node.js 22 を入れてから、利用者の中に npm で入れる)
npm config set prefix ~/.npm-global
npm install -g @openai/codex
ログインは、画面のない仮想マシンなので、表示された URL やコードを手元のブラウザで承認する形になります。Claude Code は /login で出る URL を開いて承認し、表示されたコードを貼り付けます。Codex は codex login --device-auth で出るコードを、ブラウザの承認画面に入れます。
3. 画面と Chrome を入れる

AI に Chrome を操作させたいので、軽い画面の仕組み(Xfce)と Chrome、日本語のフォントを入れました。画面は TigerVNC(画面を共有する仕組み)で常に動かしておき、Mac の「画面共有」アプリで見ます。
- VNC は仮想マシンの中(127.0.0.1)だけで受け付けるようにし、LAN からは直接つなげない
- Mac からは SSH の中を通して見る:
ssh -N -L XXXX:127.0.0.1:XXXX vm1を開いたまま、vnc://127.0.0.1:XXXXにつなぐ - AI が操作するときに止まらないよう、確認の画面が出ないようにした(つまずいたところの5)
4. Claude in Chrome を使う
仮想マシンの Chrome に拡張機能の Claude を入れて claude.ai にログインし、Claude Code を --chrome 付きで起動すると、連携用の設定が自動で作られました。試しに、次のように頼んでみました。
echo "vm1(Linux)の Chrome を選び、新しいタブで https://www.freebear.org/ を開いて、
ページの題名と、ヘッダーにある一文をそのまま答えて" \
| claude -p --chrome --allowedTools "mcp__claude-in-chrome__*"
仮想マシンの Chrome でページが開かれ、題名とヘッダーの一文(「このブログは、実際の作業記録(Obsidian)をもとに AI が下書きを作り…」)が返ってきました。開いたタブは、確認のあと自分で閉じていました。
5. AI 共通のルールを、仮想マシンにも配る
どの AI にも同じルールを守らせるため、ルールは AGENTS.md という1つのファイルにまとめています。仮想マシンにはノートがないので、Mac から15分ごとにこのファイルを送り、仮想マシンの Claude Code と Codex が読む場所(~/.claude/CLAUDE.md・~/.codex/AGENTS.md)からリンクしました。中身が変わったときだけ送り、Google ドライブの同期の途中で空になったファイルは送らないようにしています。
このとき、Mac の定期実行(launchd)からは Google ドライブのフォルダを直接読めないことがわかりました(つまずいたところの7)。そこで、Obsidian のプラグイン(Local REST API)を通してファイルを読むようにしました。
つまずいたところ

1. 手元のイメージが一部読めない
以前取ってきた Ubuntu のイメージを変換しようとしたら、途中で「Input/output error」が出ました。ディスクの健康状態や、システムの記録に異常はありませんでした。公式から取り直し、配布元のチェックサム(ファイルが正しいかを確かめる値)と照合してから使いました。
2. 管理用の利用者を作れない
最初に決めた管理用の利用者の名前では、起動しても SSH で入れませんでした。仮想マシンのディスクを読み取り専用でつないで初期設定の記録を見ると、利用者の作成で失敗していました。Ubuntu には最初からいくつかのグループがあり、その1つと同じ名前の利用者を作ろうとしていたのが原因です。しかも、そこで処理が止まったため、もう1人の利用者も作られていませんでした。名前を変え、初期設定の ID(instance-id)を変えて再起動し、初期設定をやり直させました。利用者の名前は、getent group 名前 で既存のグループと重ならないかを先に確かめておくと安心です。
3. 仮想マシンを登録する部品が異常終了した
virt-install の途中で、仮想マシンを登録するシステムの部品(systemd-machined)が異常終了しました。記録を見る限り1回だけでした。動いていた AD の仮想マシンに影響がないことを確かめてから部品を起動し直し、登録だけをやり直しました。
4. Codex のデバイスコードで承認できない
承認の画面で「ChatGPT のセキュリティ設定で、デバイスコードによるサインインを有効にしてください」と出て、先に進めませんでした。ChatGPT の「設定 → セキュリティ」でこの項目をオンにし、codex login --device-auth をやり直すと、新しいコードで承認できました。
5. 画面に確認の画面が出て止まる
- 「パスワード保管庫(キーリング)のパスワードを決めてください」:Chrome を
--password-store=basicで起動するようにした - 管理者の認証の画面:画面共有では、色の設定(colord)が管理者の認証を求めることがある。AI 用の利用者にだけ、これを許可する設定(polkit のルール)を足した
- VNC が起動しない:システム側の設定ファイルは Perl の書き方(
$localhost = "yes";)で、別の書き方で1行足したら読み込めなくなっていた。また、この版のパスワードの置き場所は~/.vnc/だった
6. Chrome が2つ見えて止まる
同じ Claude のアカウントで、仮想マシンの Chrome と Mac の Chrome の両方がつながっています。そのため、指示を出すと「どちらの Chrome を使いますか」と聞かれて止まりました。指示の中に、使う Chrome を書くようにしています。また、Claude in Chrome は自分で開いたタブしか見られない仕組みです。すでに開いているタブを読ませることはできませんでした。
7. 定期実行から Google ドライブを読めない
Mac の launchd から動かすと、シェルからも Python からも Google ドライブのフォルダが「Operation not permitted」で読めませんでした。macOS のプライバシー保護によるものです。調べてみると、以前から動かしていた別の定期処理も、同じ理由で初回からずっと失敗していました。手で動かすと成功するので、気づきにくい失敗です。
8. コマンドのオプションが、指示文まで取り込む
claude -p --allowedTools "…" "指示文" と書くと、--allowedTools が後ろの指示文まで値として取り込み、「指示文がありません」というエラーになりました。codex exec -i 画像 "指示文" も同じでした。どちらも、指示文は標準入力で渡すようにしています。
まとめと、これから
- 家のサーバーに AI 用の仮想マシンを作り、Claude Code・Codex・Chrome(Claude in Chrome)を使えるようにした
- AI を動かす利用者は sudo を使えず、家のサーバー本体や AD の仮想マシンにも入れない。AI が壊せる範囲を仮想マシンの中に限っている
- 画面は仮想マシンの中だけで受け付け、Mac からは SSH の中を通して見る
- AI に任せる前に、確認の画面で止まるところを1つずつつぶしておくのが大事だった
- これから:AI 使用状況の集計を移す、ノートに LAN 経由でつなぐ、Gemini(Antigravity)・Grok・Orca を試す
