froog / Blog

Switching from Runloop

Runloop is winding down. Its support for existing customers ends on Friday, November 13, 2026 at 11:59 PM Pacific. froog gives each agent a full Linux VM with a disk that persists, snapshots, forks and an HTTP API, so a Runloop devbox has a direct counterpart here. Most teams move in an afternoon. This post maps Runloop terms to froog, then walks through the move step by step.

Do this first: copy anything you need out of your Runloop devboxes and snapshots before the deadline. Once Runloop's service is gone, those disks may not be reachable. The migration guide shows how, with commands.

Runloop terms in froog

RunloopfroogNotes
DevboxSandboxA full KVM virtual machine with its own kernel, root, Docker and SSH. Runs until you stop it or its auto-stop timer does: 1 hour by default, up to 30 days, or never.
BlueprintSaved snapshot (--from)Set a sandbox up once, save its disk under a name, and start any number of sandboxes from it. A setup script can run at every start as well.
Disk snapshotSnapshot, forkAutomatic hourly snapshots plus named ones. Fork one sandbox into many copies, or restore it in place.
Suspend / resumeStop / startA stop keeps the disk and ends billing. A start within 10 minutes resumes with programs still running.
ShutdownDeleteOr just stop it: the disk is kept for 14 days, and saved snapshots are kept until you remove them.
Execute (sync / async)exec / background commandsWait for a command (up to 1 hour), or start it in the background and follow its output, reconnecting from anywhere.
TunnelPublic URL (froog host)An HTTPS address for any port, private with a token unless you make it public.
SecretsEnvironment variablesfroog env set NAME puts a variable in every new sandbox. Secret files such as .npmrc work too.
Repo connections, code mountsRepos in your environment, GitHubRepos are cloned into every new sandbox, and a GitHub App connection puts a one-hour token in each one for git and gh.
Benchmarks, scenariosHarbor, AgentEnvRun Terminal-Bench 2.0 and other Harbor tasks with one sandbox per trial, or use froog as an RL environment provider.
SDK (Python, TypeScript)SDK (Python, TypeScript, Go), CLI, MCPSingle-file SDKs with no dependencies, the froog CLI, an OpenAPI document and an MCP server for agents.

Step 6 maps each SDK call one to one.

What you get on froog

  • Machines that stay on. No session limit. A sandbox keeps its disk across stops and starts, and saved snapshots are kept until you delete them.
  • A real computer. Ubuntu 24.04 or 26.04, Debian 13 or Omarchy, with root, Docker, Chrome, a desktop in the browser and /dev/kvm for nested VMs.
  • Coding agents installed. Claude Code, Codex, Gemini CLI, OpenCode and more, driven by froog prompt with conversations that remember.
  • Sizes up to 8 vCPU, 16 GB of memory and 111 GB of disk, billed per second only while running: $0.0137 an hour for 2 vCPU / 4 GB, $0.0274 for 4 vCPU / 8 GB, $0.0685 for 8 vCPU / 16 GB. A 4 vCPU / 8 GB machine running all month is $20. See pricing.
  • Built for agent platforms. One sandbox per user or per task, scoped API keys, labels, webhooks, organizations and an audit log.

froog doesn't offer GPUs or bring-your-own-cloud. If you need those, check before you move.

1. Export from Runloop, before the deadline

froog can't read Runloop snapshots or blueprints, so copy the files themselves. For most devboxes a tarball of the home directory, or just the project, is all you need. Your blueprints' Dockerfiles and build scripts are usually already in your repo. If not, save them too.

This script uses Runloop's Python SDK (pip install runloop_api_client, RUNLOOP_API_KEY set). It tars each running devbox and downloads the tarball. Resume suspended devboxes first, and start a devbox from any snapshot you want to keep, so they show up as running.

# export_runloop.py: one tarball per running devbox
from runloop_api_client import Runloop
rl = Runloop()
for d in rl.devboxes.list(status="running"):
    name = d.name or d.id
    print("exporting", name)
    rl.devboxes.execute_sync(d.id, command=
        "tar czf /tmp/export.tgz -C ~ --exclude=.cache --exclude=node_modules --exclude=.venv .")
    rl.devboxes.download_file(d.id, path="/tmp/export.tgz").write_to_file(f"{name}.tgz")

Method names follow Runloop's Python SDK, so check them against the version you have installed. For a very large disk, copy over SSH instead, or upload straight to your own storage from inside the devbox (aws s3 cp, gsutil cp, rclone). Keep a list of each devbox's size, environment variables and open tunnels: you'll set those up again in steps 4 to 6.

2. Set up froog

$ curl -fsSL https://froogcorp.com/install | sh      # macOS, Linux; Windows: irm https://froogcorp.com/install.ps1 | iex
$ froog login                                    # opens the browser
$ froog billing plan 20                          # starts the 7-day free trial
$ froog api-key create migration --preset full-sandbox --ttl 30d   # for your code: FROOG_API_KEY

The free trial can't save new saved snapshots, caps sandboxes at 2 at once and needs an auto-stop of 2 hours or less. To rebuild blueprints right away, end it with froog billing end-trial and the plan starts paid. Every plan dollar comes back as usage. If you're moving many devboxes at once, email us to raise your limits.

Pick sizes by vCPU and memory. Runloop's resource_size_request sizes become one of these:

froog sizevCPUMemoryDiskPrice
small24 GB51 GB$0.0137 an hour
default48 GB51 GB$0.0274 an hour
large816 GB111 GB$0.0685 an hour

3. Bring a devbox's files over

$ froog new my-box --size default --ttl 4h       # ready in about a minute
$ froog scp my-box.tgz my-box:~/
$ froog exec my-box -- tar xzf my-box.tgz
$ froog ssh my-box                               # look around, reinstall what the tarball didn't hold
$ froog snapshot my-box --name my-box-base       # keep it as a template

A stopped sandbox keeps its disk for 14 days, and a saved snapshot is kept until you remove it. Packages installed system-wide (apt, global npm) aren't in a home-directory tarball, so install them again, ideally in a setup script you keep (next step).

4. Blueprints become saved snapshots

A Runloop blueprint is built from a Dockerfile. On froog you set a sandbox up once, save its disk under a name, and start new sandboxes from that name. Turn the Dockerfile's RUN lines into a shell script:

# setup.sh, from the blueprint's Dockerfile
set -e
sudo apt-get update
sudo apt-get install -y build-essential python3-venv
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/acme/app ~/app && cd ~/app && ~/.local/bin/uv sync
$ froog new build-py --size default
$ froog scp setup.sh build-py:~/
$ froog exec build-py --timeout 3600 -- bash setup.sh
$ froog snapshot build-py --name py-stack         # the blueprint's new name
$ froog delete build-py
$ froog new task-1 --from py-stack                # like creating a devbox from the blueprint
  • Sandboxes run Ubuntu 24.04 by default (--os for 26.04, Debian 13 or Omarchy) with passwordless sudo, Docker, Chrome, git and ten coding agents already installed, so many Dockerfile steps aren't needed.
  • Docker works inside every sandbox. If your blueprint is a complex image, you can docker build it in the sandbox and save that.
  • To rebuild a template, save under the same name again. Sandboxes already started from it don't change.
  • Snapshots of a sandbox (froog fork my-box copy) work like creating a devbox from a disk snapshot: many copies of one state, for parallel attempts.

5. Secrets, repos and setup

Your account's environment is what every new sandbox starts with. It replaces Runloop secrets, repo connections and launch commands:

$ froog env set ANTHROPIC_API_KEY                # prompts for the value; a variable in every sandbox
$ echo "$DATABASE_URL" | froog env set DATABASE_URL
$ froog environment add-repo https://github.com/acme/app   # cloned into ~ at every start
$ froog environment add-file .npmrc ~/.npmrc     # a secret file, mode 600
$ froog environment setup setup.sh               # runs at every start: make it safe to run twice
$ froog github                                   # connect the GitHub App: private repos, git and gh

Run untrusted code with froog new --no-environment: none of your variables, repos or files reach it. Details: Repos, files & setup and Environment variables.

6. SDK calls, one to one

Get the SDK: curl -fsSLO https://froogcorp.com/sdk/froog.py (or froog.ts, froog.go). Each is one file with no dependencies. froog addresses sandboxes by name, where Runloop uses devbox ids. A sandbox object carries its name for you.

Runloop (Python)froog (Python)
rl = Runloop()froog = Froog() (reads FROOG_API_KEY)
rl.devboxes.create_and_await_running(blueprint_name="py")sb = froog.sandboxes.create(from_="py-stack", keep=True) (waits until it's running)
create(snapshot_id=...)create(from_="saved-name"), or sb.fork("copy")
launch_parameters={"resource_size_request": ...}size="small" | "default" | "large"
launch_parameters={"keep_alive_time_seconds": 3600}ttl_secs=3600 (auto-stop, 0 for none)
environment_variables={...}env={...}
metadata={...}labels={...}, then froog.sandboxes.list(label="team=a")
rl.devboxes.execute_sync(id, command=c)r = sb.exec(c), then r.stdout, r.stderr, r.exit_code
execute_async(...) and polling the executionp = sb.spawn(c), then p.follow(), p.wait(), p.kill()
write_file_contents(id, file_path=p, contents=s)sb.files.write(p, s)
read_file_contents(id, file_path=p)sb.files.read_text(p)
upload_file(...) / download_file(...)sb.files.write(p, data) / sb.files.read(p) (bytes)
snapshot_disk(id, name=n)sb.snapshot(n)
suspend(id) / resume(id)sb.stop() / sb.start()
shutdown(id)sb.delete()
create_tunnel(id, port=p)url = sb.host(p) (public=True for no token)
rl.devboxes.retrieve(id) / list()froog.sandboxes.get(name) / froog.sandboxes.list()

The TypeScript SDK has the same methods in camelCase (execStream, files.readText), and Go capitalizes them. A typical loop, before and after:

# Runloop
from runloop_api_client import Runloop
rl = Runloop()
d = rl.devboxes.create_and_await_running(blueprint_name="py")
r = rl.devboxes.execute_sync(d.id, command="pytest -q")
print(r.stdout)
rl.devboxes.shutdown(d.id)

# froog
from froog import Froog
froog = Froog()
with froog.sandboxes.create(from_="py-stack", size="default") as sb:   # deleted when the block ends
    r = sb.exec("pytest -q", cwd="app", timeout=600)
    print(r.stdout)
// TypeScript
import { Froog } from "./froog";
const froog = new Froog();
await using sb = await froog.sandboxes.create({ from: "py-stack", size: "default" });
const r = await sb.exec("pytest -q", { cwd: "app", timeout: 600 });
console.log(r.stdout);

Using an agent framework? There are ready tools for the OpenAI Agents SDK, Claude Agent SDK, Vercel AI SDK, LangChain and Mastra, and an MCP server (froog mcp, or https://api.froogcorp.com/mcp). Running benchmarks? See Harbor and Terminal-Bench. Every call is in the SDK docs and the HTTP API reference.

7. Checklist

  • Every devbox and snapshot you need is exported (before November 13, 11:59 PM Pacific).
  • Blueprint Dockerfiles and setup scripts are saved in your repo.
  • Each blueprint is a saved snapshot on froog, and froog new --from NAME works.
  • Secrets are in froog env, repos and secret files in froog environment.
  • Your code uses FROOG_API_KEY and the froog SDK, and its tests pass against a real sandbox.
  • Auto-stop timers (ttl_secs) are set, so nothing runs longer than you meant.
  • Webhooks (froog webhooks add) replace any Runloop status polling you had.

Stuck, moving a lot of devboxes, or something here doesn't match what you see? Email hello@froog.tech and we'll help, including higher limits for sandboxes at once.

Questions

When does Runloop stop?

Runloop says it is winding down its services, and support for existing customers continues through November 13, 2026 at 11:59 PM Pacific (12:59 AM Mountain, 2:59 AM Eastern on November 14). Export your data before then. See docs.runloop.ai.

Can I bring my Runloop snapshots over directly?

Not as images. Copy the files you need out of each devbox (a tarball of your home directory or project is usually enough), push them into a froog sandbox, and save that sandbox as a snapshot. Step 1 has the commands.

Do my blueprints' Dockerfiles work on froog?

froog starts sandboxes from a saved disk, not a Dockerfile. Run the Dockerfile's RUN steps once in a sandbox (or as its setup script) and save it under a name. Docker is installed in every sandbox, so you can also keep building and running your image inside one.

Is there a free trial?

Yes: 7 days, 25 hours of running time and 2 sandboxes at once. Moving a team and need more right away? Tell us.

7 days free: 25 hours of running time, 2 sandboxes at once. Then from $0.0137 an hour, billed per second.