Skip to content

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

Description

@DrejT

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions