feat(docker): allow snapshotting a Paused sandbox on the Docker runtime

Author: DrejTCreated Sep 18, 2026Updated Sep 18, 2026

Why do you need it?

Snapshotting a Paused sandbox is always refused, on every runtime:

POST /v1/sandboxes/{id}/snapshots   (sandbox state: Paused)
→ 409 {"code":"SNAPSHOT::INVALID_SOURCE_STATE","message":"Snapshot can only be created from a Running sandbox."}

The check is PersistedSnapshotService._ensure_source_sandbox_running (snapshot_service.py). It applies to every runtime and is asserted by a test. On Kubernetes that makes sense: pause is itself snapshot-based, so a Paused sandbox has no live container left to commit.

On the Docker runtime, though, pause is an in-place freeze, and the Docker snapshot path is just container.commit(...) (docker/snapshot_runtime.py). docker commit works fine on a paused container: by default Docker pauses a container itself for the duration of a commit. So on Docker the refusal is a policy choice, not a technical limit.

Repro (Docker runtime, gVisor runsc as the secure runtime):

  1. Create a sandbox and write /root/paused-proof.txt in it.
  2. POST /v1/sandboxes/{id}/pause. OpenSandbox reports Paused, and Docker reports paused=true.
  3. POST /v1/sandboxes/{id}/snapshots returns 409 SNAPSHOT::INVALID_SOURCE_STATE.
  4. docker commit of the same paused container succeeds in 8.6s. The container stays paused=true, and docker run <image> cat /root/paused-proof.txt prints written-before-pause.
  5. After resume, the same snapshot request returns 202.

Environment:

  • opensandbox/server:latest (image from 2026-07-12; the check is unchanged on main at bdfcd01)
  • execd v1.0.19
  • Docker 29.4.2, overlayfs
  • runsc release-20260817.0 with --overlay2=none

Why it matters: we build a swarm control plane on OpenSandbox (alineo), where agents fork children from their own live sandbox via this snapshot API.

  • Forks under a paused parent: an operator pausing a subtree is common, and every fork that lands under a paused parent fails. We currently hold the fork until the parent resumes, which unfreezes it just to copy its filesystem.
  • Consistent checkpoints: the natural way to checkpoint a group of sandboxes consistently is pause-all, snapshot-each, resume-all. That's impossible while snapshots require Running.

How could it be?

Make the source-state check runtime-aware, e.g. a runtime capability such as supports_snapshot_from_paused():

  • Docker: accept Running and Paused. container.commit needs no change; committing an already-paused container leaves it paused.
  • Kubernetes: keep refusing Paused as today.

Transitional states (Pausing, Resuming) can keep returning 409.

Other related information

  • #1790 (fast-sandbox snapshot/pause/resume) and #1273 (snapshot-to-replacement design) touch the same area.

Source: opensandbox-group/OpenSandbox