
Gemini CLI is Google's command-line agent, and it makes a different architectural bet from the tools in the previous two chapters. Claude Code and Codex both assemble their own sandbox from kernel primitives. Gemini CLI hands the entire problem to a container runtime, and in doing so it becomes a good vehicle for thinking about what containers are actually for.
- Install Gemini CLI on Linux and authenticate with a Google account or an API key
- Provide project context using GEMINI.md and understand its hierarchical loading
- Configure container-based sandboxing with Docker, Podman, gVisor, or LXC
- Explain the trade-offs between container isolation and kernel-primitive isolation
- Decide when a container-per-session model is the right choice for agent work
It is also the most accessible of the three to try, because the free tier is generous and requires nothing more than a Google account you probably already have.
Installing
Gemini CLI is an open source Node.js application, so unlike Codex it does need a JavaScript runtime present. Install it globally with npm:
npm install -g @google/gemini-cli
Homebrew works on Linux as well as macOS if you use it:
brew install gemini-cli
Or run it without installing anything at all, which is a good way to try it before committing:
npx @google/gemini-cli
If your distribution's Node.js is older than the version Gemini CLI requires, the cleanest fix is a version manager such as nvm or fnm rather than fighting your package manager. This is the standard advice from Chapter 11 about language ecosystems that move faster than distributions do.
Start a session by running gemini in a project directory.
Authentication and the Free Tier
Three authentication routes are available, and the choice has practical consequences.
Signing in with a personal Google account uses OAuth: a browser opens, you approve, and no key is stored in your shell environment. At the time of writing this grants 60 requests per minute and 1,000 requests per day at no cost, with access to Gemini models at a one-million-token context window. For learning, for personal projects, and for occasional work, that is a substantial allowance.
The alternative is a Gemini API key from Google AI Studio, exported into the environment in the usual way:
export GEMINI_API_KEY="your-key-here"
A key gives you explicit model selection and is the right choice for scripting, where an interactive browser login is not available. As always with credentials in environment variables, remember that every child process inherits them, which is precisely the exposure the sandboxing sections of the last two chapters were concerned with. Do not put the export in a file you might commit.
Enterprise users authenticate through Google Cloud with Vertex AI instead.
Context with GEMINI.md
Gemini CLI's equivalent of CLAUDE.md is GEMINI.md. The purpose is identical: persistent instructions loaded into every session so you are not re-explaining your project each time. The loading order is hierarchical, from broad to narrow:
Table 24.1: Where Gemini CLI looks for context files
| Scope | Location | Applies to |
|---|---|---|
| Global | ~/.gemini/GEMINI.md |
Every project on your machine |
| Project | GEMINI.md in the workspace and its parents |
This project, shared via version control |
| Just-in-time | GEMINI.md in directories as they are accessed |
The subtree being worked on |
The contents of every discovered file are concatenated and sent with each prompt, and the CLI footer shows how many context files are currently active. Two slash commands are worth knowing: /memory show prints what is loaded, and /memory reload re-reads the files after you have edited them.
One genuinely useful detail is that the filename is configurable. Setting context.fileName in settings.json lets you point Gemini CLI at AGENTS.md instead, which is the emerging cross-vendor convention that Codex already follows. If you work with more than one agent, a single AGENTS.md referenced by all of them saves maintaining three files that say the same thing.
Settings themselves live in ~/.gemini/settings.json for user scope, with project-level settings alongside your code.
Sandboxing with Containers
Here is the architectural difference. Enable the sandbox with a flag:
gemini -s -p "analyse the code structure"
Or set it in the environment, where the value selects the isolation technology:
export GEMINI_SANDBOX=docker
The accepted values are true, docker, podman, sandbox-exec, runsc, and lxc. On macOS, sandbox-exec uses the built-in Seatbelt framework. On Linux, the rest are all container runtimes, and the choice among them is a choice about how much isolation you want:
Table 24.2: Container backends available on Linux
| Backend | Isolation | When to choose it |
|---|---|---|
| Docker or Podman | Namespaces and cgroups | The default choice; Podman avoids a root daemon |
gVisor (runsc) |
User-space kernel | Strongest available; intercepts syscalls in userspace |
| LXC or LXD | Full-system container | When the workload needs systemd and a complete OS |
Your working directory is mounted at the same absolute path inside the container as outside it, so paths in the conversation match paths on disk and nothing needs translating. Everything else on the host is simply not present.
The gVisor option deserves a note, because it is the most interesting entry in that table. gVisor implements a substantial portion of the Linux system call interface in user space, so a containerised process talks to gVisor rather than to your kernel. The attack surface it presents is a Go program you can audit, not the full syscall table of a monolithic kernel. That is a meaningfully stronger boundary than ordinary namespace isolation, at some cost in compatibility and performance.
Containers Versus Kernel Primitives
Now the comparison the last chapter set up can be completed.
The case for containers is that the isolation boundary is not the agent's own code. Docker, Podman, and gVisor are widely deployed, heavily scrutinised, and maintained by people whose entire job is isolation. Delegating to them means the agent vendor is not in the business of writing security-critical confinement logic. There is also an operational benefit: a container is a familiar unit that your team already knows how to inspect, limit, and destroy.
The case against is friction. A container runtime must be installed, running, and usable by your user. Container startup adds latency to every session. Tooling that expects to reach the host, such as a running database or a GPU, needs explicit plumbing. And a container is not a security boundary in the strongest sense either: it shares the host kernel, which is why gVisor exists.
Kernel primitives, as used by Claude Code and Codex, invert every one of those trade-offs. Nothing to install, no startup cost, no plumbing, and confinement that applies to a single command rather than a whole session. But the confining logic is the agent's, and the isolation is thinner.
Table 24.3: Choosing an isolation strategy
| Situation | Reasonable choice |
|---|---|
| Learning, on a machine you care about | Virtual machine |
| Day-to-day work in a trusted repository | Kernel sandbox, working directory writable |
| Unattended or CI runs | Container, or an ephemeral VM |
| Reviewing code you do not trust | gVisor container, or a VM with no network |
| Handling secrets or production credentials | Do not run an agent against them |
That last row is not a joke. The most reliable control is still deciding that some material does not go near an agent at all.
Gemini Beyond the CLI
Google's models reach Linux users in other ways worth mentioning briefly. The Gemini web application works in any browser. The Gemini API is callable from curl like any other HTTP service, which makes it composable with shell scripts without installing an agent at all:
curl -s -X POST \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"contents":[{"parts":[{"text":"Explain the setuid bit in two sentences."}]}]}' \
| jq -r '.candidates[0].content.parts[0].text'
That pipeline is the whole of this book in miniature: a program that speaks HTTP, a program that parses JSON, and a shell that connects them. The model is remote and enormous, and the plumbing around it is the plumbing Doug McIlroy gave Unix in 1972.
The next chapter removes even the remote part, and runs the model on your own machine.
