Skip to main content
A snapshot is a point-in-time copy of a sandbox’s filesystem. You never take one by hand: Boat captures them continuously in the background. Everything else on this page is a different way of using them.

Which one do I want?

Three things to do with a snapshot, and they get mixed up because they all come from the same one.
  • Resume gives you your sandbox back. Same Sandbox, same id, new machine underneath.
  • Fork gives you a second sandbox holding a copy of the filesystem as it is right now. The original keeps running, untouched.
  • Template freezes a named copy that stays put. You deploy from it whenever you like, and it still works long after the sandbox it came from is gone.
Forking and deploying both give you a new Sandbox with a copy of a filesystem, and you can do either as many times as you want. What differs is which copy: a fork takes the sandbox as it is at that moment and needs the sandbox to still exist, while a template is a point you named and froze, which you can return to weeks later and long after the original is gone. A rule of thumb: resume when you want your sandbox back, fork when you want a second one from where it stands right now, template when you want a fixed starting point to come back to.

What is captured

Captured

/home/user: your code, files, and configDocker named volumes (/var/lib/docker/volumes)Your changes under /etc, /usr, /opt, /root, /srv, cron tables, and the apt package database: installed packages, systemd services, system config

Not captured

The base OS and pre-installed tooling, which ship with the machine imageMachine identity: hostname, network config, SSH host keysRunning processes, memory, open portsDocker build cache, and images no container uses (/var/lib/docker; named volumes and the containers you had running, with their images, do come back)
Deletions count as changes. Remove a pre-installed coding agent (sudo rm -rf /opt/kimi-code /usr/local/bin/kimi, sudo npm uninstall -g @openai/codex) or an apt package, and it stays gone on every resume, fork and template deploy. Your own installs always shadow the pre-installed copies. Coming back from a snapshot behaves like rebooting a server. Your files, installed packages and system setup return, and systemd services you enabled start again on their own. Processes you ran by hand do not survive; restart them, or make them a service. Snap packages are recorded and reinstalled. See Long-Running Tasks.

Docker builds across resume and fork

Docker and BuildKit are installed, and docker build uses the layer cache normally while a sandbox runs: a rebuild with nothing changed takes well under a second. That cache lives in /var/lib/docker, which is not captured, so the first build after a resume or on a fork starts from scratch and pulls its base images again. Containers you had running are recreated with the image they were using; only the build cache is lost. To keep the cache, export it into your home directory, which is captured:
Measured on a Node app whose dependency install is the slow step: 38 s cold, 0.4 s rebuilt on the same machine, 8 s rebuilt on a fork with the exported cache. Named volumes keep their data either way.

Excluding files with .boxignore

Build artifacts and dependency trees are worth nothing in a snapshot and slow every restore down. Nothing is left out of a snapshot unless you ask for it: write a .boxignore and every capture from the next one on obeys it.
It uses gitignore syntax, and its rules are relative to the directory it sits in, so a .boxignore in a repo covers that repo and one in your home directory covers the whole sandbox. It is looked for up to six directories below your home directory, and the search skips node_modules, .next, target and vendor. Put the file at the root of the tree you want skipped, not inside it. Your .gitignore is not used. It answers “do not commit this”, which is a different question from “it is safe to lose this on resume”, and a build cache is routinely both. It is also not yours at all in a tool you installed as a git clone: ~/.nvm’s own .gitignore excludes v*, which is every node version you have installed. .git is always captured, since it is the one thing you cannot regenerate. Sandboxes created before this file was renamed still honour the old .oneignore name.

Automatic snapshots

Snapshots are incremental: the first is a full base, each later one stores only what changed, compressed, content-addressed and deduped against the base image. They are taken every minute while the sandbox is ready or idle, and a final one when it stops. A snapshot does not reserve a second copy of your entire root disk. Capture uploads compressed chunks; restore extracts chunks onto a fresh machine and removes staging files as they are consumed. The machine restore budgets apply to the uncompressed data restored onto that machine, not the compressed download size. If that final snapshot fails, the stop is aborted and the machine keeps running, so stopping can never lose data. Stopping at any moment is safe; the snapshot is always complete. Snapshots are kept for the life of the sandbox. Its latest can be resumed or forked whether it stopped yesterday or months ago. Superseded ones are cleaned up continuously as new ones are taken.

When a stop is refused

Stopping saves the disk first, and if that save is failing we refuse the stop and leave the sandbox running rather than throw away your work. We retry on our own and email you. Billing pauses by itself. The meter stops at the first refused attempt, in the balance you see as well as on your invoice. A Sandbox held open by a broken snapshot costs you nothing from that moment on, so there is nothing to claim and no refund to ask for. The pause lifts when you start using the sandbox again. If it keeps happening, snapshots are genuinely failing on that sandbox. We are alerted automatically, but contact us so we look at yours. To stop anyway and accept losing everything written since the last successful snapshot, pass --force.

Resume

Brings the same sandbox back on a fresh machine, from its latest snapshot. Same Sandbox id, same filesystem, new hardware.
Requires a completed snapshot, which means the sandbox was stopped cleanly with boat stop. Shrinking to a smaller machine is refused if the sandbox holds more data than it can take, and the sandbox is left untouched. See Machines. Omit ttlSeconds to keep the sandbox’s current lifetime. Pass null (--no-auto-stop) to switch auto-stop off entirely.

Fork

Clones a sandbox from its latest snapshot into a new, independent sandbox. The source keeps running and is never modified.
The fork inherits the whole filesystem and the source’s per-Sandbox variables unless you pass your own env. It also inherits the source’s exact environment version, so a fork never picks up configuration its source never had. See Environments. A fork does not inherit the source’s lifetime. It defaults to 1 hour, so a fork of a sandbox with auto-stop switched off is not itself left running forever. Pass ttlSeconds (or --no-auto-stop) when you want something else. Use fork for a throwaway copy right now: a second branch of work, a risky experiment, one machine per user of your product. If you find yourself forking the same sandbox repeatedly, make it a template instead.

Template Sandboxes

When many sandboxes need the same stack pre-installed, build it once, save it under a name, and deploy from that name instead of installing on every fresh sandbox.
  1. Create a sandbox and install everything: runtimes, packages, your app or daemon.
  2. Save it: boat snapshot <id> <name>. That freezes the sandbox’s disk at this moment under the name.
  3. For each new Sandbox: boat new --from <name>. Deploys are usable in a few seconds, at roughly constant cost regardless of how much the template holds.
from is a reserved word in Python, so build that one request with from_dict as above rather than keyword arguments.
Saving from a running sandbox captures its current disk, taking a moment while a fresh snapshot lands. Saving from a stopped sandbox freezes its last snapshot. A named snapshot is a frozen copy, independent of the sandbox it came from. The source can keep changing, stop, or disappear, and the name still deploys the exact state you saved. Independent means independent: a sandbox’s snapshots form a chain that is periodically rebuilt from a fresh base with the old links cleaned up, and none of that touches your saved name. The save takes its own complete copy of everything needed to restore, so it never has to walk the source sandbox’s chain, and underlying data both still need is kept for as long as either one refers to it. A saved name has no expiry. You can keep up to 10, and each shows its size in boat snapshots.

Templates are not environments

A template holds the disk. An environment holds the configuration. They are different tools and most setups use both. If you would put it in a Dockerfile it belongs in a template; if you would put it in a .env it belongs in an environment. Compose them:
That Sandbox boots with your stack already installed, none of your credentials inside it, and one variable of its own.
Deploying from a template does not carry the source sandbox’s named environment. The new Sandbox is pinned by --environment, or by your default environment when you omit it. A template built while you were logged into your own GitHub does not leak that access to its deploys.Its per-Sandbox variables do carry, the same way they do on a fork: anything you passed as --env KEY=VALUE to the source sandbox is set on every deploy from that name, unless the deploy passes its own env. Do not put a secret in --env on a sandbox you are about to save as a template.

Updating a template

Save the same name again: resume the sandbox (or any sandbox set up the way you want), update the stack, and run boat snapshot <id> <name> with the existing name. The name points at the new state and the old artifact is released. Sandboxes already deployed from it are unaffected. If a re-save fails, the name keeps deploying the last good save.

What happens on restore

A resume, fork or deploy is usable in a few seconds, whatever the sandbox holds. The full file tree is there immediately, every file is readable on demand, and content finishes downloading in the background. Permissions, ownership, timestamps and extended attributes come back with your files, and on directories too. Two details are worth knowing:
  • Extended attributes on a file become readable once that file’s content has arrived. Directories carry theirs from the moment the sandbox is up.
  • Directory modification times are restored once the background download finishes rather than immediately, because writing a file into a directory updates that directory’s timestamp, so setting it any earlier would just be overwritten.

Warming for faster first boots

A Sandbox records the order in which files are first opened while it is starting up, and keeps that order in .ascii/playbook.json. On the next start those files are fetched first, so your app reaches a working state before the rest of the disk has arrived. The playbook is an ordinary file, so it is captured into the snapshot and every fork or deploy inherits it. Recording only happens while a sandbox is starting up from a snapshot, so the order matters:
1

Start the sandbox from a snapshot

Resume a stopped sandbox, or deploy one from the template you are about to update. A Sandbox created from scratch has nothing to record against.
2

Boot your app straight away

Run the normal startup, right away, while the sandbox is still filling in. Every file it opens is recorded in the order it asks for them. Files opened later, once the sandbox has finished filling in, are not recorded.
3

Let it finish, then save

The playbook is written when the sandbox finishes filling in. Save after that, or the run you just did is not in the template.
Each run merges into the previous playbook rather than replacing it, with the newest run weighted highest, so a template warms up further every time you repeat this. It holds the first 5000 paths.

Retention

By default, snapshots are kept for the life of the archived sandbox: its latest snapshot can be resumed or forked whether it stopped yesterday or months ago. Superseded snapshots are cleaned up continuously as new ones are taken. Permanent deletion is different from archive. Deleting a sandbox or snapshot returns a background operation and makes the target unavailable; it cannot be resumed. Named snapshots remain independent of their source sandbox. When you remove one, its backing data is scheduled no earlier than six hours later so already-issued signed upload URLs expire first. With zero data retention, archived sandbox data and named snapshots are queued for deletion instead of retained.

Inspect and download

pull writes two subfolders, home_user/ (your /home/user) and docker/ (named volumes), reflecting exactly the files that were live at that snapshot. There is no SDK equivalent of pull: it is getSnapshotDownload plus local reassembly. Browse the same tree, and download files from it, on the Snapshots tab of the dashboard. See the Snapshots API for the full surface.

Deleting a sandbox’s snapshots

Stopping a sandbox keeps its snapshots, that is the whole point of stopping. Deleting a sandbox throws them away.
Deleting a sandbox force-stops it and permanently deletes its snapshots. It cannot be resumed, forked or recovered afterwards, and there is no deleted-Sandboxes list to restore from.If you want the data usable later, stop the sandbox instead of deleting it. A stopped sandbox is free, keeps its disk, and resumes where you left off.
Every delete has to name its own target: X-Ascii-Confirm-Delete must equal the sandbox id exactly, or the request is refused with 409 and nothing is deleted. See Data retention and deletion for the operation you get back and how to poll it. You can also delete a sandbox from the menu on its row in the dashboard. The Sandbox leaves your account immediately; the snapshot data goes once the machine has finished shutting down. What survives, and why. Snapshots are shared: a fork, a resume and a deploy from a template all read the same physical objects as the sandbox they came from. Deleting a sandbox therefore only removes the snapshot data nothing else is using. Kept are:
  • chains a sandbox forked or resumed from this one still restores from
  • named snapshots saved from this sandbox, which are meant to outlive it. Delete those with boat snapshot rm <name> when you want the bytes gone.
Storage is released once the last sandbox or named snapshot using a chain is gone.

Why filesystem, not the VM

Snapshots capture the filesystem, independent of the machine running underneath. Today a sandbox is a Hetzner VPS, but a sandbox is meant to become anything: a Linux server, a Mac or Windows machine in the cloud, or a physical device. A filesystem snapshot stays portable across all of them, restores fast, stays small, and lets us filter what is captured as needs grow. A whole-VM image would tie you to one kind of machine.