如何在无头VPS上运行Gemini CLI
在无头VPS运行Gemini CLI:使用Node 20+、无需sudo的全局安装、无浏览器API密钥认证和tmux,SSH断开后长任务仍可继续。
构建内容
在您拥有的服务器上运行始终在线的 Gemini CLI。通过 SSH 访问它,并运行长时间的代理任务。即使关闭笔记本电脑,任务也会继续运行。安装只需执行 3 条命令。真正需要处理的是所有依赖桌面环境的部分:Google 的 CLI 希望打开浏览器完成登录,但服务器没有浏览器。因此,本指南的大部分内容都介绍无头运行方式、发行版不会提供的当前 Node、无需 root 的全局 npm 安装、不会将 API 密钥写入 shell 历史记录的无浏览器身份验证,以及 tmux。这样,即使 SSH 会话断开,正在运行的任务也不会随之终止。
Gemini CLI 是一个开源的(Apache-2.0)Node 程序(@google/gemini-cli)。它与 Google 的 Gemini 模型通信,可以读取和写入文件、运行 shell 命令,并驱动工作目录中的工具。在 VPS 上,它是一个小型、始终可用的代理。您可以让它持续工作。因此,它运行所使用的账户以及保存在服务器上的凭据,比本指南中的任何单项设置都更重要。
前置条件和需要注意的问题
- 一台全新的 Ubuntu 24.04 KVM VPS,使用 root 或 sudo。任何 KVM 方案都可以;CLI 本身占用资源很少,空闲时只需几百 MB RAM。
- Node.js 20 或更高版本。这是唯一严格的版本要求,而发行版软件包低于该版本,详见下一节。
- 可通过 HTTPS(端口 443)访问 Google API。不需要入站端口;这是客户端而不是服务器,因此无需为它开放防火墙端口。
- 一种无需在服务器上使用浏览器的身份验证方式:使用 Google AI Studio 提供的 Gemini API key,或通过 SSH 隧道连接到您自己计算机上的浏览器。API key 方式适合脚本和无人值守运行。
- 仅当您需要
--sandbox隔离时才使用 Docker 或 Podman。此项可选,文末附近会介绍。
最容易被忽略的问题是:友好的 gemini 首次运行登录流程是为桌面环境设计的。它会尝试打开浏览器;在无头服务器上,这通常会失败,或者提供一个无法使用的链接。开始前请先确定身份验证方式。
Node:发行版软件包版本过旧
Ubuntu 24.04 在自有软件源中提供 Node 18.19.1,并搭配 npm 9.2.0。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(按用户管理版本)。请选择其中一个。
如果希望 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 保留在某个用户的主目录中,并且不使用 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,获取最新版本,并在运行命令前替换其中的版本号。nvm 对此任务有一个实际优势:它会将 Node 及其全局软件包安装到 ~/.nvm 下,因此下一节中的全局安装权限问题不会出现。如果选择 nvm,可以跳过 npm prefix 步骤。
安装 CLI,但不使用 sudo npm -g
看似合理的命令是 sudo npm install -g @google/gemini-cli。不要这样做。由 root 拥有的全局 prefix 会导致之后每次安装都出现权限错误,并在 npm 缓存中留下由 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 是有意的:两节之后,您将在 tmux 中运行 CLI。tmux 启动的是非登录 shell,该 shell 会读取 ~/.bashrc,但跳过 ~/.profile。因此,如果将 PATH 行写入错误的文件,gemini 就会在您实际需要的位置不可用。gemini --version 能够输出版本号,就说明配置正确。若输出 gemini: command not found,说明您的 PATH 导出未生效,请参阅故障模式。使用 nvm 时,完全跳过这些 prefix 行:nvm 已经将全局安装放在您的主目录下。
如果您之前执行过 sudo npm,现在看到 Your cache folder contains root-owned files,请使用 sudo chown -R $(id -u):$(id -g) ~/.npm 修复一次。
无头环境下的身份验证问题及解决方法
首次以交互方式运行 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。即使您在笔记本电脑上打开该 URL 并完成授权,Google 也会重定向到 http://localhost:PORT,也就是服务器上的 localhost。笔记本电脑无法访问服务器上的这个端口,因此登录无法完成。
有两种可靠的解决方法。
第一种方法是使用 API key,这也是服务器上的默认选择。在 Google AI Studio(aistudio.google.com)中创建密钥,然后通过环境变量提供给 CLI;CLI 会读取 GEMINI_API_KEY,从而完全跳过浏览器流程。接下来需要避免密钥进入历史记录或其他用户可读的文件。不要在提示符中输入 export GEMINI_API_KEY=AIza...,因为它会以明文写入 ~/.bash_history;也不要将它放入其他用户可以读取的文件中。请将密钥写入一个由 shell 启动时加载的 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 确认密钥已进入环境变量;如果没有任何输出,CLI 就会回退到浏览器流程并失败。如果您更喜欢这种布局,它还会读取 .env 中的 ~/.gemini/ 文件,规则相同,即 chmod 600 ~/.gemini/.env。
第二种方法保留个人 Google 账号登录方式及其免费层级,通过隧道将 OAuth 回调转发回您的笔记本电脑。问题是,CLI 的回环服务器每次运行都会绑定一个随机端口。如果不先使用 OAUTH_CALLBACK_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。请在笔记本电脑的浏览器中打开该 URL 并完成授权。当 Google 重定向到 http://localhost:8085/... 时,SSH 转发会将请求传递到 VPS 上的回环服务器,登录即可完成。如果不固定端口,每次运行都会使用新的随机端口,预先设置的任何 ssh -L 都无法捕获该端口。该方法可以使用,但需要您在浏览器前完成操作,因此不适合脚本。对于需要持续运行的任务,请使用 API key。
如果要使用 Vertex AI 或 Google Cloud 项目,而不是 AI Studio,请同时设置 GOOGLE_API_KEY 和 GOOGLE_GENAI_USE_VERTEXAI=true;如果使用 Code Assist 许可,则设置 GOOGLE_CLOUD_PROJECT。环境变量管理方式相同,也应使用 mode-600 文件。
在 tmux 中运行,避免 SSH 会话断开导致进程终止
直接从 SSH shell 启动的 gemini 进程是该 shell 的子进程。连接丢失、笔记本电脑关闭、Wi-Fi 断开或空闲超时都会导致 sshd 拆除伪终端,shell 收到 SIGHUP 后也会向 CLI 发送挂断信号。正在编辑文件的任务即使已经运行了十分钟,也会随之终止。重新连接后,已没有可恢复的进程。
tmux 通过接管 shell 解决此问题,而不是让 sshd 接管 shell。这与在远程 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 gemini如果名为 gemini 的会话存在,tmux new -A -s gemini 会连接到该会话;如果不存在,则创建该会话。因此,每次登录后都可以直接运行这条命令。会话中的 shell 属于已分离的 tmux 服务器,而不是 SSH 会话。即使连接断开,CLI 仍会继续运行。重新连接并附加会话后,您可以回到同一份滚动缓冲区。如果您在同一台主机上运行多个代理会话,请为每个会话使用一个 tmux 会话。这里的会话无法相互通信;这不同于 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 会回退到浏览器流程并失败。
在同时运行生产服务的服务器上进行沙箱隔离和权限控制
具有 shell 访问权限的代理本质上等同于一个 shell。Gemini CLI 可以运行命令。默认情况下,它会在执行每个有风险的命令前请求确认,但用户可能会启用 --yolo(自动批准每次工具调用)。这样一来,它就可以使用运行该代理的用户的全部权限删除文件、推送到 git,或访问内部服务。在同时运行生产服务的服务器上,这会造成实际的影响范围,而不是假设性的风险。
以下 3 项控制措施按收益从高到低排列:
- 使用专用的非特权用户运行。 不要使用 root,也不要使用
sudo的成员账户。创建一个agent用户,为其配置独立的主目录,并在该用户下安装 Node 和 CLI。这样,即使指令被误解,影响也会限制在该账户内。这是价值最高的单项决策。 - 不要将生产凭据保留在服务器上。 不要存放生产环境的
~/.aws/credentials,不要从生产环境复制.env,也不要使用对重要资源具有写入权限的数据库密码。应提供 staging 凭据或只读凭据。 - 使用内置沙箱。 安装 Docker 或 Podman 后,
gemini --sandbox(或GEMINI_SANDBOX=docker)会在与主机文件系统和网络隔离的容器中运行代理的工具调用。这不能替代非特权用户,但当同一台 VPS 承担实际生产工作时,它可以作为有效的第二层防护。
如果你在其他自托管工具旁边运行 Gemini CLI,例如在同一台 VPS 上运行一个向代理提供工具的 MCP 服务器,应将每项新增功能都视为代理可以访问的额外攻击面,并将提供给它的令牌严格限制为单一用途。
配额、费用和所选的身份验证路径
身份验证路径决定计费方式。个人 Google 账号(OAuth 路径)使用免费的 Gemini Code Assist 层级,但有实际的每分钟和每日限制;超过限制后,请求会返回限流错误,直到时间窗口重置。来自 AI Studio 的 API key 可能使用免费层级,也可能按项目计费;计费的 key 会提高限制,并按 token 收费。Vertex 和 Cloud 项目的身份验证费用通过 Google Cloud 结算。
有两点需要注意。无人值守的 agent 在循环中运行时可能会快速消耗配额,因此在将其交给 cron job 前,先观察它运行几次,确认行为符合预期。如果您选择服务器端模型的原因是隐私或不受计量限制的推理,而不是使用 Google 托管的模型,那么应使用其他工具:在 VPS 上使用 Ollama 自托管开放式 LLM 会将模型权重和提示词保存在您自己的服务器上,但可运行的模型会比 Gemini 小得多。
保持更新
Gemini CLI 经常发布新版本。由于您将它安装到了由用户拥有的前缀目录中,更新时无需使用 sudo:
npm install -g @google/gemini-cli@latest
gemini --versionGemini CLI 提供多个发布渠道:@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。
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'。 这是将全局包安装到 root 所有的前缀目录导致的。不要使用 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 使用 ssh -L 转发,然后在本地打开该 URL。
SSH 断开后进程消失。 你直接在 SSH shell 中运行了 gemini,因此它是该 shell 的子进程,断开连接时随 pty 一起退出。没有可恢复的进程。每次会话都先使用 tmux new -A -s gemini,再在其中运行 CLI。
已设置密钥,但身份验证仍然失败;CLI 返回身份验证选择器;或者请求返回 API key not valid,HTTP 状态码为 400。 CLI 看不到其运行环境中的密钥。使用 printenv GEMINI_API_KEY 确认。如果输出为空,说明你的 ~/.gemini_env 从未被加载。请检查该行是否位于 ~/.bashrc 中。交互式 shell(包括 tmux)会读取该文件,但 cron 及其他非交互式 shell 不会读取。密钥值中多余的空格或引号也会产生 API key not valid。
429 / RESOURCE_EXHAUSTED / 限流消息。 你已达到身份验证所用层级的配额。等待配额窗口重置,降低代理运行速度,或改用计费 API key。陷入重试循环的代理会持续触发此问题。请停止它并检查其行为。
FAQ
如何在无头服务器上为 Gemini CLI 进行身份验证?
使用 API key,不要使用浏览器登录。在 Google AI Studio 中创建密钥,将其写入由 shell 加载的 600 权限文件(export GEMINI_API_KEY=...)中,CLI 就会完全跳过 OAuth 浏览器流程。如果确实需要使用个人账号的免费层级,请使用 OAUTH_CALLBACK_PORT=8085 固定 loopback 端口,通过 ssh -L 8085:localhost:8085 user@server 将该端口转发回笔记本电脑,然后在本地打开输出的 URL。但这要求您在浏览器前操作,因此不适合脚本。
为什么 npm 全局安装要求使用 sudo,如何避免?
因为 npm 的默认全局前缀是 /usr/lib/node_modules,当前用户没有写入权限,所以直接运行 npm install -g 会因 EACCES 失败。错误做法是使用 sudo npm -g,这会留下属于 root 的文件,导致后续安装失败。正确做法是将前缀指向您的主目录(npm config set prefix ~/.npm-global),并将其 bin 添加到 PATH;也可以使用 nvm,它会自动将全局软件包安装到您的主目录下。
如何在断开连接后让 Gemini CLI 继续运行?
在 tmux 中运行它。从 SSH shell 启动的进程是该 shell 的子进程,连接断开后会随之退出;tmux 会在一个可分离的服务器中运行该 shell,因此即使连接断开也能继续运行。使用 tmux new -A -s gemini,在其中运行 gemini,使用 Ctrl-b d 分离,之后使用 tmux attach -t gemini 重新连接。
在生产服务器上运行 Gemini CLI 是否安全?
只有谨慎操作才安全,因为具有 shell 访问权限的代理可以执行其运行用户能够执行的任何操作。应使用没有 sudo 权限的专用非特权用户运行它,不要将生产凭据保存在该机器上,避免使用 --yolo 自动批准,并使用 --sandbox(Docker 或 Podman)将工具调用与主机隔离。运行该程序的账号比您设置的任何单个标志都更重要。
Gemini CLI 是否需要开放防火墙端口?
不需要。它是一个向 Google API 发起出站 HTTPS 请求的客户端,因此只需要出站端口 443,不需要入站端口。如果使用 OAuth 隧道,固定的回调端口(例如 8085)位于 localhost 上,并通过 SSH 转发访问,而不是作为开放的入站端口。请继续限制入站连接。