SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor

How to Run Gemini CLI for Headless VPS Work

Run Gemini CLI for headless VPS work with current Node, no-sudo global npm install, API-key auth without browser login, and tmux for SSH drops.

Wetin you dey build

Na an always-on Gemini CLI for server wey you own, wey you fit reach through SSH, and wey dey run long agent tasks even after you close your laptop. The installation na three commands. The work dey come from things wey assume say desktop dey available: Google's CLI want open browser make you log in, but your server no get browser. So most of this guide na the headless path, current Node wey distro no go provide, global npm install wey no need root, browserless auth with API key wey you keep away from shell history, and tmux so dropped SSH session no go stop task wey dey run.

Gemini CLI na open-source (Apache-2.0) Node program (@google/gemini-cli) wey dey talk to Google's Gemini models and fit read and write files, run shell commands, and use tools for the working directory. For VPS, e be small, always-available agent wey you fit leave to work. Na why the account wey e run as, and the credentials wey dey for the server, matter pass any single setting for here.

Prerequisites and the honest gotchas

  • Fresh Ubuntu 24.04 KVM VPS wey get root or sudo. Any KVM plan go work; the CLI dey light, e dey use few hundred MB RAM when e no dey do anything.
  • Node.js 20 or newer. Na the only hard version limit be this, and distro package dey below am; see the next section. Node 20 reach end-of-life on April 30, 2026, so install 24.
  • Outbound HTTPS (port 443) go Google's APIs. No inbound port dey needed; this na client, no be server, so you no need open firewall hole for am.
  • Authentication method wey no need browser for server: either paid Gemini API key from Google AI Studio, or Google sign-in. For headless box, Google sign-in go make you paste code back into terminal. API-key method na the one wey fit scale for scripts and unattended runs.
  • Docker or Podman, only if you want the --sandbox isolation. E optional, and we go cover am near the end.

The gotcha wey dey catch everybody now na the account, no be the browser. Google announce on May 19, 2026 say from June 18, 2026 Gemini CLI go stop serving requests for Google AI Pro and Ultra subscribers, and for anybody wey dey use am free of charge. E direct those users go the replacement, Antigravity CLI (agy). Gemini CLI still dey work with paid Gemini API keys, Gemini Enterprise Agent Platform API keys, and organisation's Gemini Code Assist Standard or Enterprise licence. Decide the authentication method before you start.

Node: distro package dey too old

Ubuntu 24.04 dey ship Node 18.19.1 for im own repositories, together with npm 9.2.0. Gemini CLI's package.json dey declare engines: { node: ">=20" }, and npm no dey stop mismatch by default. E go install am anyway and print warning wey show the difference:

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 }

If you ignore that warning, the CLI go run for runtime wey no dey supported. E fit misbehave or crash once e reach Node 20+ API wey e expect to dey available. Node 18 sef reach end-of-life for April 2025, so either way, e no get future. Install current LTS before you install the CLI. The two clean options na NodeSource (system-wide signed apt repo) or nvm (per-user version manager). Pick one.

NodeSource, if you want make Node dey available to every user for the machine:

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 --version

node --version suppose print v20.x or higher to satisfy the CLI. But Node 20 don reach end-of-life since April 30, 2026, so target v24.x, wey be the current active LTS. Node 26 dey scheduled to become the active LTS on October 28, 2026. Check NodeSource page for the current setup script. The setup_24.x for the URL na the line wey you go increase when newer LTS come out.

nvm, if you prefer keep Node inside one user's home and never touch am with sudo:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install --lts
node --version

The v0.40.1 for that URL na the current version when dem write this. Check nvm's README for the latest release, then replace the version before you run am. nvm get one real advantage for this work: e dey install Node and im global packages under ~/.nvm, so the global-install permission problem for the next section no go happen at all. If you choose nvm, you fit skip the npm-prefix step.

Install CLI without sudo npm -g

The command wey fit tempt person na sudo npm install -g @google/gemini-cli. No run am. Global prefix wey root own go cause permission errors for every install wey follow, and e go leave root-owned files for your npm cache wey fit cause problem months later. Run normal (without sudo) npm install -g against system Node, and you go see the other error:

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'

This one mean say npm dey try write inside /usr/lib, but your user no get permission. The fix no be sudo. Set npm global prefix to your home directory, so global installs go enter place wey you own:

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, no be ~/.profile, na deliberate choice. tmux, wey you go run the CLI inside two sections from now, starts non-login shell wey dey read ~/.bashrc and skip ~/.profile. So if you put PATH line for wrong file, gemini go remain invisible exactly where you need am. If gemini --version print version number, the test don pass. But if you see gemini: command not found instead, your PATH export no take effect; check the failure modes. For nvm, skip the prefix lines completely: e already installs globals under your home.

If you run sudo npm before and you now see Your cache folder contains root-owned files, repair am once with sudo chown -R $(id -u):$(id -g) ~/.npm.

The headless auth problem, and how to get past it

Run gemini interactively the first time and e go offer you make you log in with your Google account. For desktop, e go open browser tab. For headless Linux VPS, the CLI go first check for display, and when none of DISPLAY, WAYLAND_DISPLAY or MIR_SOCKET dey set, e no go try launch browser at all. E go print sign-in link and wait make you paste code back:

Please visit the following URL to authorize the application:

https://accounts.google.com/o/oauth2/v2/auth?...

Enter the authorization code:

That flow dey work over plain SSH, but e need you dey near browser every time. Also, since June 18, 2026, e go only let you enter if your Google account still dey among the ones Gemini CLI dey serve.

Two honest ways dey to pass this.

The first one na API key, and na the correct default for server. Create key for Google AI Studio (aistudio.google.com) inside project wey billing dey turned on, because Gemini CLI no longer dey serve free-of-charge use. Give the key to CLI as environment variable; e dey read GEMINI_API_KEY and skip the browser flow completely. Now make we handle the part about keeping am out of history and files wey everybody fit read. Do not type export GEMINI_API_KEY=AIza... for prompt, because e go enter ~/.bash_history as cleartext. Also, do not put am inside file wey other users fit read. Write am to mode-600 file wey shell go source when e start:

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 ~/.bashrc

chmod 600 mean say na only your user fit read the file. Confirm say the key enter the environment with printenv GEMINI_API_KEY; if e print nothing, CLI go return to its auth picker instead of using the key. E also dey read .env file inside ~/.gemini/ if you prefer that layout. Same rule still apply, so chmod 600 ~/.gemini/.env.

The second way na Google sign-in itself. For headless machine, e no need tunnel or open port. Since June 18, 2026, e mainly useful for organisation wey get Gemini Code Assist Standard or Enterprise licence (set GOOGLE_CLOUD_PROJECT too). Free personal accounts and Google AI Pro or Ultra subscriptions no longer dey served. Start CLI through normal SSH session and choose Sign in with Google:

gemini

Copy the URL wey e print into your laptop browser and approve am. Google go redirect to https://codeassist.google.com/authcode, wey go show authorization code; paste am at Enter the authorization code: prompt for VPS and the login go complete. Nothing for server dey wait for callback, so no port to pin and no ssh -L to set up. OAUTH_CALLBACK_PORT only matter for the loopback flow wey CLI dey use when e finds display. If you connect with X11 forwarding (ssh -X), DISPLAY go dey set and CLI go use that loopback flow instead, with redirect to http://127.0.0.1:PORT/oauth2callback for server; run NO_BROWSER=true gemini to force the paste-a-code flow. E dey work, but e need you sit near browser, so e no good for scripts. For anything wey you leave running, use API key.

For Vertex AI or Google Cloud project instead of AI Studio, set GOOGLE_API_KEY together with GOOGLE_GENAI_USE_VERTEXAI=true, or GOOGLE_CLOUD_PROJECT for Code Assist licence. Use the same environment-variable discipline and the same mode-600 file.

Run am inside tmux so dropped SSH session no go kill am

A gemini process wey you launch directly from your SSH shell na child of that shell. If connection cut, laptop close, Wi-Fi drop, or idle timeout happen, sshd go tear down the pseudo-terminal. The shell go receive SIGHUP, then e go hang up the CLI too. Task wey don spend ten minutes editing files go die with am, and when you reconnect, no process dey to recover.

tmux dey solve this by owning the shell instead of sshd owning am. Na the same pattern as run AI coding agent for remote VPS inside tmux, and e dey work the same way here:

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

tmux new -A -s gemini dey attach to session wey dem name gemini if e exist, and e go create am if e no exist. So na the one command to run immediately after every login. The shell inside belong to detached tmux server, no be your SSH session, so connection drop no go stop the CLI. Reconnect, attach, and you go return to the same scrollback. If you later run several agent sessions for one box, one for each tmux session, dem no get any way to talk to each other here. This different from Claude Code, where one session fit hand text give another one for the same VPS. So make each Gemini job independent, or coordinate dem through files for disk.

For non-interactive, scripted runs, Gemini CLI get headless mode: gemini -p "summarise the failing tests in this repo" dey print answer and exit, while --output-format json dey give machine-readable output wey you fit pipe elsewhere. Headless mode with API key na exactly wetin you want inside tmux session wey dey run long batch job, or wey cron entry start. But get one caveat: cron job no dey source any of your login files. So give the crontab line im own GEMINI_API_KEY, or make the command source ~/.gemini_env. Otherwise, the run go fail because credentials no dey available.

Sandboxing and permissions on one box wey still dey run production

Agent wey get shell access na shell. Gemini CLI fit run commands, and by default e dey ask before each risky one. But people dey use --yolo (auto-approve every tool call), then e fit delete files, push to git, or reach internal services with the full authority of the user wey run am. For box wey still dey run production, this na real blast radius, no be something wey dey only for theory.

Three controls, arranged from the one wey give you the most protection:

  • Run am as dedicated, unprivileged user. No be root, and no be member of sudo. Create agent user with e own home directory, install Node and the CLI there, and if e misunderstand instruction, the effect go remain inside that account. Na this decision get the highest value.
  • Keep production credentials away from the box. No prod ~/.aws/credentials, no .env copied from production, and no database password wey get write access to anything important. Give am staging credential or read-only credential.
  • Use the built-in sandbox. If Docker or Podman dey installed, gemini --sandbox (or GEMINI_SANDBOX=docker) go run the agent tool calls inside container wey mount only the current project directory, not the rest of the host filesystem. E no replace the unprivileged user, but e add strong second layer when the same VPS dey do real work.

If you dey run Gemini CLI beside other self-hosted tools, like an MCP server wey expose tools to the agent for the same VPS, treat every extra capability as more surface wey the agent fit reach, and limit the tokens you give am to exactly one job.

Quota, cost, and which auth path you choose

The auth path dey decide how dem go bill you, and since June 18, 2026 whether dem go serve you at all. From that date, according to Google's announcement, Gemini CLI stop to serve requests for Google AI Pro and Ultra subscribers and for anybody wey dey use am free, including personal-account free tier and API keys wey no dey billed. E still dey available through paid Gemini API keys and Gemini Enterprise Agent Platform API keys, wey dem dey charge per token, and for organisations wey get Gemini Code Assist Standard or Enterprise licence or other Google Cloud access, wey dem dey bill through Google Cloud. Each tier still get per-minute and per-day limits; if you pass dem, requests go return rate-limit error until the window reset.

Two practical notes. Unattended agent wey dey run inside loop fit finish quota quickly, so monitor am the first few times before you trust am with a cron job. And if your reason for server-side model na privacy or inference wey no get usage metering, instead of Google's hosted models, na different tool be that: self-hosting open LLM with Ollama on VPS go keep the weights and prompts for your own box, but you go run model wey much smaller than Gemini.

E dey updated

Gemini CLI dey release often. Since you install am inside prefix wey user own, updates no ever need sudo:

npm install -g @google/gemini-cli@latest
gemini --version

Release channels dey: @latest na stable, @preview na weekly preview, @nightly na bleeding edge. Pin anything wey you rely on to @latest. For nvm, global packages dey under the active Node version. So after nvm use to switch Node, you fit need install the CLI again. Read the release notes instead of chasing every patch.

Wetin fit fail, with the exact strings

npm WARN EBADENGINE Unsupported engine ... required: { node: '>=20' }, then the CLI crash when e dey run. Node don old, and the distro version na 18.19.1, wey don pass end-of-life too. Install current LTS from NodeSource or nvm (Node 20 meet the CLI minimum version, but e don reach end-of-life since April 2026), confirm am with node --version, and if you get plenty Nodes installed, check say which node dey point to the new one, no be /usr/bin/node.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Na global install enter prefix wey root own. No use sudo, set npm config set prefix ~/.npm-global, put ~/.npm-global/bin for PATH, then reinstall as your normal user. If earlier sudo npm leave cache files wey root own (Your cache folder contains root-owned files), run sudo chown -R $(id -u):$(id -g) ~/.npm.

Failed to open browser, or a redirect_uri wey dey point to http://127.0.0.1:PORT wey you no fit reach. The CLI find display variable, often from ssh -X, and e use loopback flow, wey the callback go land for the server. Run NO_BROWSER=true gemini to get the paste-a-code flow, or use the API-key path (GEMINI_API_KEY). For plain SSH session, you suppose see Enter the authorization code: instead; approve the URL for your laptop browser and paste the code wey Google show you.

The process disappear when SSH disconnect. You run gemini directly from the SSH shell, so e be child of that shell and e die together with the pty when connection cut. Nothing dey to recover. Start every session with tmux new -A -s gemini and run the CLI inside am.

Auth still fail even though the key dey set, the CLI return to the auth picker, or request return API key not valid with HTTP 400. The key no dey inside the environment wey the CLI dey see. Confirm am with printenv GEMINI_API_KEY; if e empty, your ~/.gemini_env never source, check say the line dey inside ~/.bashrc. Interactive shells, including tmux, dey read am, but cron and other non-interactive shells no dey read am. Extra space or quote inside the key value fit also produce API key not valid.

429 / RESOURCE_EXHAUSTED / rate-limit message. You don reach the quota for the auth tier wey you dey use. Wait make the window reset, slow down the agent, or change to billed API key. Agent wey stuck for retry loop go continue to hit this; stop am and check wetin e dey do.

FAQ

How I go authenticate Gemini CLI for headless server?

Use paid API key, no be browser login. Create key for Google AI Studio on billed project, put am inside file wey shell dey source and wey get mode-600 permission (export GEMINI_API_KEY=...), then CLI go skip OAuth browser flow completely. If you sign in with Google instead, for example with Code Assist Standard or Enterprise licence, CLI for headless box go print URL; open am for your laptop, approve am, then paste the code wey Google show back for Enter the authorization code: prompt. No port to pin and no tunnel dey involved, but you need dey present for browser, so e no good for scripts. Gemini CLI never serve free personal accounts since June 18, 2026.

Why npm global install dey ask for sudo, and how I fit avoid am?

Na because npm default global prefix na /usr/lib/node_modules, and your user no get permission to write there, so plain npm install -g go fail with EACCES. The wrong fix na sudo npm -g, because e go leave root-owned files wey go cause later installs to break. The correct fix na point the prefix to your home (npm config set prefix ~/.npm-global) and add the prefix bin to PATH, or use nvm. nvm dey install global packages under your home automatically.

How I fit keep Gemini CLI running after I disconnect?

Run am inside tmux. Process wey start from your SSH shell go die when connection drop because na child of that shell; tmux dey run the shell under detached server wey go survive the disconnect. Use tmux new -A -s gemini, run gemini inside, detach with Ctrl-b d, then reattach later with tmux attach -t gemini.

E safe to run Gemini CLI for production box?

Only if you handle am carefully, because agent wey get shell access fit do anything the user wey run am fit do. Run am as dedicated unprivileged user wey no get sudo, keep production credentials off the machine, avoid --yolo auto-approval, and use --sandbox (Docker or Podman) to isolate tool calls from the host. The account wey run am matter pass any single flag wey you set.

I need open any firewall ports for Gemini CLI?

No. E be client wey dey make outbound HTTPS calls to Google's APIs, so e need outbound port 443 but no inbound ports. Google sign-in for headless box no need port too: you go paste code back into terminal instead of receiving callback for server. Keep inbound traffic locked down.

#gemini-cli#node#tmux#headless#ai#vps