Skip to content
RALPH LOOP

How to Run a Ralph Loop With the Gemini CLI

A green CRT terminal showing the Gemini CLI running in a looping Ralph loop inside a Docker sandbox

A ralph loop gemini cli setup is the cheapest way to keep an agent working for hours: install Ralph into your project, log in to Gemini once inside its sandbox, then run ./ralph.sh --agent gemini. Ralph feeds the Gemini CLI a fresh context every iteration and keeps re-running it against your task list until the work is done or the iteration cap runs out. The reason to reach for Gemini specifically is money. A long unattended run is not one prompt, it is dozens, and the per-iteration model cost compounds across every pass.

This is the full recipe, from an empty repo to a reviewed diff, with cost as the through line. If the loop itself is new to you, start with what the Ralph technique is. If you want the flag-level internals of the Gemini backend, the companion piece is running the Gemini CLI in a loop. This post is the branded, end-to-end setup.

Why the Gemini CLI is the economical pick for a long loop

Section titled “Why the Gemini CLI is the economical pick for a long loop”

Think about what a long run actually is. The loop does not spend one model call and stop. It spends one call per task, and it keeps going until the task list is empty. Run fifty iterations overnight and you paid for fifty separate agent sessions, each reading state, editing files, running tests, and writing a commit. That is the arithmetic that matters: total cost equals the per-iteration cost multiplied by the number of iterations you allow.

Two properties make the Gemini CLI attractive for that arithmetic.

First, context size. Each Ralph iteration starts the agent from a clean context window, so it re-reads the task spec, the summary, and the relevant files every single pass. A model with a large context window swallows that reorientation without truncating, which keeps each pass from thrashing on a half-loaded picture of the repo. Fresh context is the point of Ralph, and a roomy window makes fresh context cheap to fill.

Second, model choice you can actually name. Ralph forwards agent-specific flags after its own -- separator, and the project README documents ./ralph.sh -a gemini -- --model pro as a real example. So you get an explicit lever: run the standard model for routine tasks, and pass --model pro when a batch of tasks needs more reasoning. You decide where the money goes instead of hoping a router guesses right.

Put those together and the emphasis of this whole post falls out: cost and context over a long unattended run. The iteration cap is not just a safety rail against runaway loops, it is your budget dial. Set -n and you have set a hard ceiling on how many model calls the run can make before it stops on its own.

The iteration count is the single most important number for cost control, so treat it as a budget, not a target.

Terminal window
# 10 iterations (the default)
./ralph.sh --agent gemini
# a tight budget: exactly one pass, good for a smoke test
./ralph.sh --agent gemini --once
# a small capped batch
./ralph.sh --agent gemini --max-iterations 5
# a long overnight run with a firm ceiling
./ralph.sh --agent gemini -n 50

Every one of those numbers is a spending limit. --once buys exactly one model call. -n 50 says the run may cost at most fifty passes before Ralph exits on its own, whether or not the task list is finished. The loop also stops early the moment the work is genuinely done, so you often pay for fewer iterations than the cap allows. You never pay for more.

This is why exit code 1 is not a failure (more on codes below). It means the run spent its budget with work still pending, which is exactly what a ceiling is for. Raise the cap and continue tomorrow, or shrink the task list, but you were never at risk of an open-ended bill.

The short form -a works everywhere the long --agent does, so ./ralph.sh -a gemini -n 50 is the same run. Pick the number that matches how much you are willing to spend while you sleep, then walk away.

Run the installer in your project root. It drops the ralph.sh script and the .agent/ directory into the repo.

Terminal window
npx @pageai/ralph-loop

The installer asks for your dev server URL and port and your default agent, then writes a default implementation-mode setup. Pick gemini when prompted, or set it per run with --agent gemini later. If the script is not executable yet, fix that:

Terminal window
chmod +x ralph.sh

Ralph runs each agent inside a Docker Sandbox, so install and sign in to Docker Sandboxes first. See the Docker Sandboxes docs for the official setup flow. The loop drives the standalone sbx CLI under the hood, and the sandbox is what lets Gemini edit files and run commands without that reach touching your real machine.

The loop executes a task list. It does not invent one. Before you run anything, turn your requirements into a PRD and a set of task specs with the prd-creator skill. This step is also a cost decision: a vague task list makes Gemini flail across extra iterations, and every wasted iteration is money.

Open any agent in plan mode and prompt it with your requirements:

Use the prd-creator skill to help me create a PRD and task list for the below requirements.
An app is already set up with React, Tailwind CSS and TypeScript.
Requirements:
- A SaaS product that helps users manage their finances.
- Track income and expenses, create invoices, generate reports.
- Use shadcn/ui for components and Supabase for the database.
// etc.

The skill writes .agent/prd/PRD.md, a short .agent/prd/SUMMARY.md, and per-task specs under .agent/tasks/, indexed by .agent/tasks.json. Review each task individually before you start. A clear task list with real acceptance criteria is the single biggest factor in whether the loop succeeds, and the fewer dead-end passes you buy. The deeper version of that workflow is in spec-driven development with AI.

Gemini runs inside an isolated sandbox, not on your host, so it needs credentials in that environment. Authenticate once with the login action:

Terminal window
./ralph.sh --login --agent gemini

Ralph prints the login command for every supported agent, highlights the Gemini one, and drops you into the sandbox shell. The command it runs to create the box looks like this:

Terminal window
sbx run --name ralph-gemini-<project>-<hash8> gemini .

Inside the sandbox, complete the Gemini sign-in flow and follow the prompt. If a run later fails on auth, Ralph surfaces a hint that points at gemini auth, which tells you the box has no valid session. The credential persists in that named sandbox, so later runs attach to the same box already logged in.

An API key is an alternative to interactive login, and it matters for cost math because Docker Sandboxes only forward environment variables when a sandbox is created. If you go the key route, delete the sandbox first, set your provider API key in your shell before recreating it, then run ./ralph.sh --login --agent gemini again so the fresh sandbox picks the value up. The README’s authentication section describes this create-time forwarding pattern in full. Interactive login is usually the simpler path.

Each agent gets its own deterministic sandbox name, built from the agent slug, the project directory, and an 8-character hash of the absolute path:

ralph-gemini-<project>-<hash8>

Print the exact name for your project without starting a run:

Terminal window
./ralph.sh --print-name --agent gemini

If your tasks run a dev server you want to reach from the host, publish the configured port to the Gemini sandbox. The port comes from the dev-server URL you set during install:

Terminal window
./ralph.sh --ports --agent gemini

That expands to an sbx ports call against the Gemini sandbox, for example sbx ports ralph-gemini-<project>-<hash8> --publish 3000:3000. Per-agent names keep state separate: your Gemini sandbox and your Claude sandbox never share credentials or installed tools, so you can trial Gemini on a repo without disturbing another agent’s box.

With a task list in place and Gemini authenticated, start the loop. The default agent is Claude, so name Gemini explicitly:

Terminal window
./ralph.sh --agent gemini -n 50

Under the hood, Ralph builds a Gemini CLI command and runs it inside the sandbox. For a first run that creates the box, the expansion is:

Terminal window
sbx run --name ralph-gemini-<project>-<hash8> gemini . -- -p "$PROMPT_CONTENT"

The -p flag is Gemini’s non-interactive prompt mode, which still has full tool access including file writes and shell. That is exactly what a loop needs: an agent that can do real work without a person approving each step.

Everything to the right of Ralph’s own -- separator is forwarded straight to the Gemini CLI, and Ralph inserts those extra args right before -p. So the README example:

Terminal window
./ralph.sh -a gemini -- --model pro

expands to:

Terminal window
sbx run --name ralph-gemini-<project>-<hash8> gemini . -- --model pro -p "$PROMPT_CONTENT"

That ordering is the mechanism to remember: the flags you pass land before the prompt flag, so the Gemini CLI sees your model choice and then the prompt. --model pro is the model the README documents for Gemini, so it is a safe lever to pull when a run needs stronger reasoning. Do not guess at other identifiers. Reach for the standard model on cheap, mechanical tasks and reserve pro for the passes that earn it, because that split is where a long run’s cost is won or lost.

The rule of the separator holds in general: everything left of -- configures Ralph (agent, iteration count, login), and everything right of -- configures Gemini. A practical full command for an overnight run:

Terminal window
./ralph.sh --agent gemini -n 50 -- --model pro

Every pass is mechanical and identical. The agent reorients from disk, does one task, proves it works, and commits. One iteration is one unit of spend, so it helps to know exactly what that unit does.

  1. Find the highest-priority incomplete task in .agent/tasks.json.
  2. Work the steps in .agent/tasks/TASK-{ID}.json.
  3. Run tests, linting, and type checking.
  4. Complete the task, take a screenshot, update the task status, and commit.
  5. Repeat until all tasks pass or the iteration cap is reached.

The key detail is that each iteration spawns a fresh Gemini process with a clean context window. The agent does not carry an hours-long transcript from one task to the next. It reads the current state from disk, does one task, and exits. This is why the loop deliberately does not reuse a running session: continuity is a liability here, not a feature, and a fresh window avoids paying to re-send a transcript that has drifted off the plot.

flowchart TD
    Start(["./ralph.sh --agent gemini -n 50"]) --> Budget{"Iterations left in budget?"}
    Budget -->|"no"| Cap(["exit 1, budget spent"])
    Budget -->|"yes"| Pick["Pick top task from .agent/tasks.json"]
    Pick --> Spawn["sbx run gemini . -- --model pro -p (fresh context)"]
    Spawn --> Work["Gemini reads state, edits files, runs commands"]
    Work --> Verify["Run tests, lint, type check, screenshot"]
    Verify --> Commit["Commit and update task status"]
    Commit --> Check{"Promise tag emitted?"}
    Check -->|"none"| Budget
    Check -->|"COMPLETE"| Done(["exit 0, all tasks done"])
    Check -->|"BLOCKED or DECIDE"| Stop(["exit 2 or 3, wants a human"])

The filesystem and git history are the memory layer. Progress lives in .agent/tasks.json, .agent/logs/LOG.md, the per-task specs, and the git log, not in a chat transcript. The prompt that drives every pass lives in .agent/PROMPT.md, and writing it well is its own skill, covered in how to write the PROMPT.md file.

One rule keeps the whole thing reliable and cheap: one task per invocation. Gemini completes exactly one task, commits, and stops. It never batches several tasks into a single pass, which keeps each commit small, each diff reviewable, and each unit of spend accountable to a single piece of work.

A loop that never ends is a money fire, so Ralph stops on an explicit signal rather than a guess. Gemini emits a semantic promise tag in its final message and Ralph reads it:

  • <promise>COMPLETE</promise> means every task is finished.
  • <promise>BLOCKED:reason</promise> means the agent needs human help.
  • <promise>DECIDE:question</promise> means it needs a decision you have to make.

Those map to exit codes, which is what makes the loop scriptable and its spend legible after the fact:

Terminal window
./ralph.sh --agent gemini -n 50
echo $? # 0 COMPLETE, 1 MAX_ITERATIONS, 2 BLOCKED, 3 DECIDE

Exit code 1 means the loop spent its iteration budget with work still pending, which is the expected outcome of a firm cap, not a bug. Codes 2 and 3 mean Gemini stopped early and wants a human, so you did not burn the rest of the budget on a task the agent could not finish alone. The full design lives in completion promises and exit codes. If Gemini is not authenticated when the loop starts, Ralph detects the auth failure, stops, and points you back at ./ralph.sh --login --agent gemini, so it never thrashes (and never spends) on a box that can never make progress.

Three habits keep a long Gemini run under control and keep you from paying twice for the same work.

If you notice the agent heading the wrong way, do not kill the loop and restart it, because a restart pays for a cold start again. Edit .agent/STEERING.md while it runs. The agent checks that file on each pass and handles the critical work there before resuming the normal task order. That is steering, not stopping, and it corrects course without wasting the budget you already committed.

The sandbox is a normal container you can poke at. List what exists, then open a shell in the Gemini box:

Terminal window
sbx ls
sbx exec -it ralph-gemini-<project>-<hash8> bash

From there you can re-run a failing test by hand, confirm the session is valid, or read .agent/logs/LOG.md and the per-iteration logs in .agent/history/. When an install fails, the deny-by-default network is usually the cause. Allow the domain it needs:

Terminal window
sbx policy allow network ralph-gemini-<project>-<hash8> registry.npmjs.org

That is a feature, not a limitation. The agent installs what the task needs without a path to exfiltrate your source. The full argument is in running AI coding agents in Docker sandboxes safely.

Because every task is committed separately, the morning review is a git review, not an archaeology dig:

Terminal window
git log --oneline
git diff main...HEAD

You read the commits in order, eyeball the screenshots the agent captured, and accept or revert. A single bad task is one revert, not a tangled mess, so a wasted iteration costs you one small commit rather than the whole night’s work. This is the whole premise of running an AI coding agent overnight, and the broader money discipline is in controlling AI agent cost.

Ralph is agent agnostic. The supported agents are claude (default), codex, copilot, cursor, gemini, and opencode, and switching is a single flag. The loop machinery is byte-for-byte identical across all of them, so the choice comes down to the economics of a multi-hour run: how much context each model can hold on a fresh start, and what each pass costs you. Gemini earns its place when you want a large context window and an explicit --model pro lever over a long, capped run. The shell-script guts that make the loop hackable are in the Ralph loop shell script, and the branded sibling recipe is running a Ralph loop with the Cursor CLI.

A real Gemini loop, start to finish, is four commands:

Terminal window
# 1. install Ralph and scaffolding
npx @pageai/ralph-loop
# 2. authenticate once (creates the sandbox, you log in inside it)
./ralph.sh --login --agent gemini
# 3. smoke test a single pass (one unit of spend)
./ralph.sh --agent gemini --once
# 4. run the loop for real with a model and a firm budget
./ralph.sh --agent gemini -n 50 -- --model pro

Define your tasks in .agent/tasks.json, write a clear .agent/PROMPT.md, set the iteration cap to a number you are comfortable paying for, start the loop, and read the commits in the morning. The Ralph technique itself traces back to Geoffrey Huntley’s original writeup, Ralph is a Bash loop, and everything above is that idea wired to the Gemini CLI inside a sandbox.

Frequently asked questions

How do I run a Ralph loop with the Gemini CLI?

Install Ralph with npx @pageai/ralph-loop, create a task list, log in with ./ralph.sh --login --agent gemini, then run ./ralph.sh --agent gemini. Ralph runs the Gemini CLI in non-interactive prompt mode inside a Docker sandbox, starts a fresh context each iteration, and repeats until every task in .agent/tasks.json is done or the iteration cap is reached.

Why choose Gemini for a long unattended loop?

Cost and context. A long run is many iterations, and the per-iteration model cost compounds across every pass, so the model you pick matters. Gemini offers a large context window that absorbs the fresh-context reorientation each iteration needs, and Ralph lets you name the model with --model pro when a batch of tasks needs stronger reasoning.

How do I control cost when looping Gemini?

Use the iteration cap as a budget. ./ralph.sh --agent gemini --once buys exactly one pass, -n 50 caps the run at fifty passes, and the loop still stops early when the work is done so you often pay for fewer. Exit code 1 means the budget was spent with work still pending, which is the cap doing its job.

How do I pass a Gemini model through Ralph?

Put it after the -- separator. Anything to the right of -- is forwarded to the Gemini CLI and inserted just before the prompt flag, so ./ralph.sh -a gemini -- --model pro expands to sbx run --name ralph-gemini-<project>-<hash8> gemini . -- --model pro -p with the Ralph prompt. --model pro is the model documented in the README.

How do I log in to Gemini for the loop?

Run ./ralph.sh --login --agent gemini. It creates the sandbox and drops you into its shell to complete the Gemini sign-in. If a run later fails on auth, Ralph points you back at gemini auth. As an alternative you can set your provider API key in your shell before recreating the sandbox, since Docker Sandboxes only forward environment variables at create time.