ヘッドレスVPSでGemini CLIを動かす方法
Ubuntu 24.04のヘッドレスVPSでGemini CLIを実行する手順です。Node.js 20以降、sudo不要のglobal npm install、API key認証、SSH切断に強いtmuxを使います。
構築するもの
自分で管理するサーバー上で常時稼働し、SSH 経由で接続できる Gemini CLI です。ノート PC を閉じた後も、長時間のエージェントタスクを実行し続けます。インストールは3つのコマンドで完了します。手間がかかるのは、デスクトップ環境を前提とする部分です。Google の CLI はログインのためにブラウザを開こうとしますが、サーバーにはブラウザがありません。そのため、このガイドの大部分では、ヘッドレス環境での手順、ディストリビューションでは提供されない新しい Node、root 権限を必要としないグローバル npm インストール、シェル履歴に残さない API key を使ったブラウザレス認証、そして SSH セッションが切断されても実行中のタスクを終了させない tmux を扱います。
Gemini CLI はオープンソース(Apache-2.0)の Node プログラム(@google/gemini-cli)です。Google の Gemini モデルと通信し、作業ディレクトリ内のファイルの読み書き、shell コマンドの実行、ツールの操作を行えます。VPS 上では、常時利用でき、実行を継続させておける小規模なエージェントとして機能します。そのため、ここで扱う個々の設定以上に、実行するアカウントとサーバー上に保存される認証情報が重要です。
前提条件と注意点
- root または sudo を使用できる、クリーンな Ubuntu 24.04 KVM VPS。KVM プランであればどれでも使用できます。CLI 自体は軽量で、停止中は数百 MB の RAM しか使用しません。
- Node.js 20 以降。これが唯一の厳格なバージョン要件です。ディストリビューションのパッケージはこれを下回るため、次のセクションで説明します。
- Google の API への外向き HTTPS(ポート 443)。受信ポートは不要です。これはサーバーではなくクライアントなので、ファイアウォールに穴を開ける必要はありません。
- サーバー上でブラウザーを必要としない認証手段。Google AI Studio で取得した Gemini API key、または自分のマシンのブラウザーへ戻る SSH トンネルのいずれかを使用します。API key を使う方法は、スクリプトや無人実行に適しています。
--sandboxの分離が必要な場合のみ Docker または Podman。これは任意で、終盤で説明します。
多くの人がつまずく注意点は、使いやすい gemini の初回ログインフローがデスクトップ向けに作られていることです。ブラウザーを開こうとするため、ヘッドレス環境では失敗するか、使用できないリンクが表示されます。開始前に認証方法を決めてください。
Node: ディストリビューションのパッケージは古すぎる
Ubuntu 24.04 は、独自のリポジトリで npm 9.2.0 と組み合わせた Node 18.19.1 を提供しています。Gemini CLI の package.json は engines: { node: ">=20" } を宣言しています。npm はデフォルトでは不一致をエラーで停止せず、そのままインストールして、差分を示す警告を出力します。
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE package: '@google/gemini-cli@0.50.0',
npm WARN EBADENGINE required: { node: '>=20' },
npm WARN EBADENGINE current: { node: 'v18.19.1', npm: '9.2.0' }
npm WARN EBADENGINE }この警告を無視すると、CLI はサポート対象外のランタイム上で実行されます。その場合、必要な Node 20 以降の API に到達した時点で、誤動作またはクラッシュする可能性があります。Node 18 は 2025 年 4 月にサポート終了となっているため、いずれにしても使用すべきではありません。CLI をインストールする前に、現行の LTS をインストールしてください。選択肢は、NodeSource(システム全体で使用する署名付き apt リポジトリ)または nvm(ユーザーごとのバージョンマネージャー)の 2 つです。どちらか 1 つを選んでください。
すべてのユーザーがサーバー上で Node を利用できるようにする場合は、NodeSource を使用します。
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
node --versionnode --version は v20.x 以上を出力する必要があります。v24.x は現在のアクティブな LTS です。現在のセットアップスクリプトは NodeSource のページで確認してください。URL 内の setup_24.x は、新しい LTS がリリースされたときに更新する箇所です。
Node を 1 ユーザーのホームディレクトリ内だけで管理し、sudo で変更しない場合は、nvm を使用します。
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install --lts
node --versionこの URL の v0.40.1 は、本文の執筆時点での最新バージョンです。実行前に nvm の README で最新リリースを確認し、URL 内のバージョンを置き換えてください。nvm には、この用途で明確な利点があります。Node とグローバルパッケージを ~/.nvm 配下にインストールするため、次のセクションで説明するグローバルインストールの権限問題が発生しません。nvm を使用する場合は、npm-prefix の手順を省略できます。
CLI を sudo npm -g なしでインストールする
実行したくなるコマンドは sudo npm install -g @google/gemini-cli です。実行しないでください。root 所有のグローバル prefix を設定すると、その後のすべてのインストールで権限エラーが発生します。また、npm cache に root 所有のファイルが残り、数か月後に問題の原因になります。システムの Node に対して sudo なしで npm install -g を実行すると、別のエラーが発生します。
npm error code EACCES
npm error syscall mkdir
npm error path /usr/lib/node_modules/@google
npm error errno -13
npm error Error: EACCES: permission denied, mkdir '/usr/lib/node_modules/@google'これは、npm が /usr/lib への書き込みを試みている状態です。このディレクトリにはユーザー権限で書き込めません。必要なのは sudo ではありません。npm のグローバル prefix をホームディレクトリに設定し、グローバルインストール先を自分が所有する場所に変更します。
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
npm install -g @google/gemini-cli
gemini --version~/.bashrc を使い、~/.profile を使わないのには理由があります。2 セクション後に CLI を実行する tmux は、ログインシェルではないシェルを起動します。このシェルは ~/.bashrc を読み込み、~/.profile を読み飛ばします。そのため、PATH の行を誤ったファイルに記述すると、必要な場所で gemini が認識されません。gemini --version でバージョン番号が表示されれば、確認は完了です。代わりに gemini: command not found が表示される場合は、PATH の export が反映されていません。「失敗しやすい例」を参照してください。nvm を使用している場合は、prefix の行をすべて省略します。nvm はグローバルパッケージをすでにホームディレクトリ内へインストールします。
以前に sudo npm を実行していて、現在 Your cache folder contains root-owned files が表示される場合は、sudo chown -R $(id -u):$(id -g) ~/.npm を 1 回実行して修正します。
ヘッドレス環境での認証問題と、その解決方法
初回は gemini を対話的に実行すると、Google アカウントでのログインを求められます。デスクトップ環境ではブラウザーのタブが開きます。ヘッドレス VPS にはブラウザーがないため、フローが開くことを想定した localhost URL を表示するか、次のようなエラーで完全に失敗します。
Failed to open browser. Please visit the following URL to authorize:
https://accounts.google.com/o/oauth2/v2/auth?...&redirect_uri=http://localhost:PORT問題は redirect_uri=http://localhost:PORT です。ノート PC でその URL を開いて承認しても、Google は http://localhost:PORT にリダイレクトします。これは サーバー上の localhost であり、ノート PC から到達できないポートです。ログインは完了しません。
この問題を解決する正攻法は 2 つあります。
1 つ目は API key です。サーバーではこれを標準の方法にしてください。Google AI Studio(aistudio.google.com)で key を作成し、環境変数として CLI に渡します。CLI は GEMINI_API_KEY を読み取り、ブラウザーのフローを完全に省略します。次に、「履歴や誰でも読めるファイルに残さない」方法です。プロンプトで export GEMINI_API_KEY=AIza... を入力しないでください。これは ~/.bash_history に平文で保存されます。また、他のユーザーが読めるファイルにも保存しないでください。シェルの起動時に読み込む mode-600 のファイルに書き込みます。
umask 077
printf 'export GEMINI_API_KEY=%s\n' 'AIzaSyYOUR_KEY_HERE' > ~/.gemini_env
chmod 600 ~/.gemini_env
echo '[ -f ~/.gemini_env ] && . ~/.gemini_env' >> ~/.bashrc
source ~/.bashrcchmod 600 は、ファイルを読み取れるのが自分のユーザーだけであることを示します。printenv GEMINI_API_KEY で key が環境変数に渡されたことを確認します。何も表示されない場合、CLI はブラウザーのフローに戻り、失敗します。別の配置にする場合、~/.gemini/ にある .env ファイルも読み取ります。適用するルールは同じなので、chmod 600 ~/.gemini/.env。
2 つ目は、個人の Google アカウントによるログインと、その無料枠を維持しながら、OAuth callback をノート PC にトンネルする方法です。ただし、CLI の loopback server は実行ごとに ランダムな port に bind します。そのため、まず OAUTH_CALLBACK_PORT 環境変数で port を固定し、その port だけを転送する必要があります。
# from your laptop, forward the callback port into the SSH session:
ssh -L 8085:localhost:8085 user@your-server
# then, on the server, pin the callback to the same port and start the CLI:
export OAUTH_CALLBACK_PORT=8085
geminiCLI はブラウザーを開けないため、認証 URL を表示します。ノート PC のブラウザーでその URL を開いて承認してください。Google が http://localhost:8085/... にリダイレクトすると、SSH forward がそのリクエストを VPS の loopback server に転送し、ログインが完了します。port を固定しない場合、実行ごとに新しいランダムな port が割り当てられます。事前に設定した ssh -L では、その port を受けられません。この方法は動作しますが、ブラウザーを操作する必要があるため、スクリプトには適しません。常時実行するものには API key を使用してください。
AI Studio ではなく Vertex AI または Google Cloud project を使用する場合は、GOOGLE_GENAI_USE_VERTEXAI=true とともに GOOGLE_API_KEY を設定します。Code Assist licence では GOOGLE_CLOUD_PROJECT を設定します。環境変数の扱いと mode-600 のファイルについては、同じルールを適用してください。
tmux 内で実行して、SSH 接続が切れてもプロセスを終了させない
SSH シェルから直接起動した gemini プロセスは、そのシェルの子プロセスです。接続が失われた場合、ノート PC を閉じた場合、Wi-Fi が切断された場合、アイドルタイムアウトが発生した場合、sshd が擬似端末を破棄し、シェルが SIGHUP を受け取り、CLI も終了します。ファイルの編集を始めてから 10 分経過したタスクも同時に終了し、再接続しても復旧できるプロセスは残りません。
tmux を使うと、sshd ではなく tmux がシェルを所有するため、この問題を解決できます。これは リモート VPS 上の tmux 内で AI コーディングエージェントを実行する場合と同じ構成で、ここでも同じように機能します。
sudo apt install -y tmux
tmux new -A -s gemini
# inside the session:
gemini
# detach with Ctrl-b then d — the task keeps running
# reconnect later from any machine:
tmux attach -t geminitmux new -A -s gemini は、gemini という名前のセッションが存在すれば接続し、存在しなければ作成します。そのため、ログインするたびに直後に実行するコマンドとして使えます。内部のシェルは SSH セッションではなく、デタッチ状態の tmux サーバーに属します。そのため接続が切れても CLI は動作を続けます。再接続してセッションに接続すれば、同じスクロールバックに戻れます。1 台のホストで複数のエージェントセッションを実行する場合は、tmux セッションを 1 つずつ割り当てます。ここではセッション間で通信する方法がありません。一方、Claude Code では 同じ VPS 上のあるセッションから別のセッションへテキストを渡せます。そのため、各 Gemini ジョブを独立させるか、ディスク上のファイルを介して連携してください。
対話を必要としないスクリプト実行では、Gemini CLI のヘッドレスモードを使用できます。gemini -p "summarise the failing tests in this repo" は回答を出力して終了し、--output-format json は他の処理へパイプできる機械可読形式の出力を生成します。API key を使うヘッドレスモードは、長時間のバッチジョブを実行する tmux セッション内や、cron エントリから起動する場合に適しています。ただし、cron ジョブはログイン時のファイルを読み込まない点に注意してください。そのため、crontab の行に専用の GEMINI_API_KEY を指定するか、コマンドから ~/.gemini_env を読み込ませてください。指定しない場合、CLI はブラウザ認証フローにフォールバックして失敗します。
本番も稼働するサーバーでのサンドボックス化と権限管理
シェルアクセスを持つエージェントは、シェルそのものです。Gemini CLI はコマンドを実行でき、デフォルトでは危険なコマンドを実行する前に確認を求めます。しかし、--yolo(すべてのツール呼び出しを自動承認)を使ってしまうことがあります。その場合、実行ユーザーの権限を完全に使って、ファイルを削除したり、git に push したり、内部サービスへアクセスしたりできます。本番も稼働するサーバーでは、これは仮想的な問題ではなく、実際の影響範囲になります。
効果の大きい順に、3 つの対策があります。
- 専用の非特権ユーザーで実行する。 root ではなく、
sudoのメンバーでもないユーザーを使います。専用のホームディレクトリを持つagentユーザーを作成し、そのユーザーの環境に Node と CLI をインストールします。指示を誤って解釈しても、そのアカウントの範囲に影響を限定できます。これは最も効果の大きい判断です。 - 本番の認証情報をサーバーに置かない。 本番の
~/.aws/credentialsや、本番からコピーした.envを置かないでください。重要な対象への書き込み権限を持つデータベースパスワードも渡しません。ステージング用、または読み取り専用の認証情報を使用します。 - 組み込みのサンドボックスを使う。 Docker または Podman がインストールされている場合、
gemini --sandbox(またはGEMINI_SANDBOX=docker)を使うと、エージェントのツール呼び出しをホストのファイルシステムとネットワークから隔離したコンテナ内で実行できます。これは非特権ユーザーの代わりにはなりませんが、同じ VPS で本番処理を行っている場合の強力な第 2 層になります。
Gemini CLI を他のセルフホスト型ツールと同じ環境で実行する場合、たとえば 同じ VPS 上でエージェントにツールを公開する MCP server を使う場合は、追加する機能ごとにエージェントが到達できる範囲が広がると考えてください。渡すトークンの権限は、正確に 1 つの用途だけに限定します。
クォータ、料金、および選択した認証経路
認証経路によって課金方法が決まります。個人の Google アカウント(OAuth 経路)では、無料の Gemini Code Assist ティアが使用されます。実際には分単位および日単位の上限があり、これを超えると、上限期間がリセットされるまでリクエストはレート制限エラーになります。AI Studio の API key は、プロジェクトに応じて無料ティアまたは従量課金になります。課金対象の key では上限が引き上げられ、トークン単位で料金が発生します。Vertex と Cloud プロジェクトの認証では、Google Cloud 経由で課金されます。
実運用上の注意点が 2 つあります。ループ内で unattended agent を実行するとクォータをすぐに消費する可能性があるため、cron job に任せる前に、最初の数回は動作を監視してください。また、サーバー側モデルを使う理由が、Google のホスト型モデルではなく、プライバシーや推論の無制限利用である場合は、別のツールが適しています。VPS 上で Ollama を使ってオープン LLM をセルフホストすることで、重みとプロンプトを自分のサーバー内に保持できます。ただし、Gemini より大幅に小さいモデルを運用することになります。
最新状態に保つ
Gemini CLI は頻繁にリリースされます。ユーザー所有の prefix にインストールしたため、更新時に sudo は必要ありません。
npm install -g @google/gemini-cli@latest
gemini --versionリリースチャネルには、安定版の @latest、週次プレビュー版の @preview、最新の変更を含む @nightly があります。依存する環境では @latest に固定してください。nvm では、グローバルパッケージはアクティブな Node バージョンの配下に保存されます。そのため、nvm use で Node を切り替えた後は、CLI の再インストールが必要になる場合があります。すべてのパッチを追いかけるのではなく、リリースノートを確認してください。
正確な文字列を確認するための失敗パターン
npm WARN EBADENGINE Unsupported engine ... required: { node: '>=20' }が発生し、その後 CLI が実行時にクラッシュする場合。 Node が古く、ディストリビューションのバージョンは 18.19.1 で、これもサポート終了を過ぎています。NodeSource または nvm から Node 20 以降をインストールし、node --versionで確認してください。Node が複数インストールされている場合は、which nodeが/usr/bin/nodeではなく、新しい Node を指していることも確認してください。
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'の場合。 root 所有の prefix にグローバルインストールしています。sudo は使用せず、npm config set prefix ~/.npm-globalを設定し、~/.npm-global/binをPATHに追加してから、通常のユーザーとして再インストールしてください。以前の sudo npmによって root 所有のキャッシュファイル(Your cache folder contains root-owned files)が残っている場合は、sudo chown -R $(id -u):$(id -g) ~/.npmを実行してください。
Failed to open browser、ログインがハングする場合、または到達できない redirect_uri=http://localhost:PORTが表示される場合。 OAuth フローは、サーバー上に存在しないブラウザーを必要とします。また、localhost のコールバック先はラップトップではなくサーバーを指します。API key を使用する方法(GEMINI_API_KEY)を使うか、OAUTH_CALLBACK_PORTを固定し、ssh -Lで SSH 経由に転送して、URL をローカルで開いてください。
SSH が切断されるとプロセスが消える場合。 SSH シェルから geminiを直接実行したため、そのシェルの子プロセスとなり、切断時に pty とともに終了しました。復旧できるものはありません。各セッションを tmux new -A -s geminiで開始し、その中で CLI を実行してください。
key を設定しても認証に失敗する場合、CLI が認証方式の選択画面に戻る場合、またはリクエストが HTTP 400の API key not validを返す場合。 key が、CLI から見える環境に存在しません。printenv GEMINI_API_KEYで確認してください。空の場合、~/.gemini_envが読み込まれていません。行が ~/.bashrcにあることを確認してください。~/.bashrcは、cronなどの非対話シェルでは読み込まれませんが、対話シェル(tmux を含む)では読み込まれます。key の値に余分な空白や引用符がある場合も、API key not validが発生します。
429 / RESOURCE_EXHAUSTED / レート制限メッセージが表示される場合。 認証で使用している tier のクォータに達しています。ウィンドウがリセットされるまで待つか、agent の処理速度を下げるか、課金対象の API key に切り替えてください。agent が再試行ループで停止している場合、同じ状態が続きます。agent を停止し、何を実行しているか確認してください。
FAQ
Gemini CLI をヘッドレスサーバーで認証するにはどうすればよいですか?
ブラウザログインではなく、API key を使用します。Google AI Studio で key を作成し、shell が読み込む mode-600 のファイル(export GEMINI_API_KEY=...)に保存すると、CLI は OAuth のブラウザフローを完全にスキップします。個人アカウントの無料 tier を使用する場合は、OAUTH_CALLBACK_PORT=8085 で loopback port を固定し、ssh -L 8085:localhost:8085 user@server で laptop に転送して、表示された URL をローカルで開きます。ただし、ブラウザを操作できる状態にしておく必要があるため、script には適しません。
npm の global install で sudo が求められるのはなぜですか?また、どうすれば避けられますか?
npm のデフォルトの global prefix が /usr/lib/node_modules であり、user が書き込めないためです。そのため、通常の npm install -g は EACCES で失敗します。sudo npm -g は誤った対処です。root 所有のファイルが残り、後続の install で問題が発生します。正しくは、prefix を home(npm config set prefix ~/.npm-global)に変更し、その bin を PATH に追加します。または nvm を使用します。nvm は global package を自動的に home 配下へインストールします。
切断後も Gemini CLI を実行し続けるにはどうすればよいですか?
tmux 内で実行します。SSH shell から起動した process は、その shell の child であるため、接続が切れると終了します。tmux は shell を detached server 配下で実行するため、切断後も process が残ります。tmux new -A -s gemini を使用し、その中で gemini を実行します。Ctrl-b d で detach し、後で tmux attach -t gemini で再接続します。
本番サーバーで Gemini CLI を実行しても安全ですか?
注意して運用する場合に限ります。shell access を持つ agent は、実行 user が可能な操作をすべて実行できるためです。sudo 権限のない専用の unprivileged user として実行し、本番の credential をそのサーバーに置かないでください。--yolo の auto-approval は避け、--sandbox(Docker または Podman)を使用して tool call を host から分離します。設定する単一の flag よりも、実行 user の account のほうが重要です。
Gemini CLI のために firewall port を開く必要はありますか?
いいえ。Gemini CLI は Google の API に対して outbound HTTPS call を行う client です。そのため、outbound port 443 は必要ですが、inbound port は必要ありません。OAuth tunnel を使用する場合も、固定した callback port(例: 8085)は localhost 上で動作し、SSH forward 経由で到達します。inbound port を開放する必要はありません。inbound は閉じたままにしてください。