@finalclass SandboxOne MicroVM’s whole life.
The five transitions are build_image, run, suspend, resume, and terminate, and
every state guard lives in the core — see the module docs. Each has an awaitable twin
(run_async and so on) that drives the same future; [crate::runtime] says how.
Constructor
Section titled “Constructor”Sandbox( region: Region, *, retry: tuple[SupportsIndex, SupportsFloat | SupportsIndex] | None = None,)Resolves credentials for region and returns a sandbox with nothing launched.
region is a [PyRegion] and not a string, so a region name is parsed once, at
Region.parse or a named constructor, and a malformed one never reaches this call.
Properties
Section titled “Properties”adopted
Section titled “adopted”adopted: boolWhether this sandbox was built by adopt rather than by its own launch.
bootstrap_count
Section titled “bootstrap_count”bootstrap_count: intHow many times the token has been installed. Never above one (STATE-3).
endpoint
Section titled “endpoint”endpoint: str | NoneThe proxy endpoint, once launched.
image: Image | NoneThe image, once built.
image_exists
Section titled “image_exists”image_exists: boolWhether an image is recorded as existing (STATE-1).
is_detached
Section titled “is_detached”is_detached: boolWhether detach() handed this sandbox’s VM to another process.
launch_image_arn
Section titled “launch_image_arn”launch_image_arn: str | NoneThe image ARN run sent in RunMicrovm: the caller’s identifier with a bare name
resolved, or the built image’s.
Recorded before the call, so a launch the service refused still names the image it
asked for. None until run has resolved an image, and on an adopted sandbox.
lifecycle
Section titled “lifecycle”lifecycle: strThe lifecycle state: "PENDING", "RUNNING", "SUSPENDING", "SUSPENDED",
"TERMINATING", or "TERMINATED".
Spelled as the service spells it, because a reader compares it against a
GetMicrovm response and Suspended beside SUSPENDED reads like two facts.
microvm_id
Section titled “microvm_id”microvm_id: str | NoneThe VM id, once launched.
session
Section titled “session”session: Session | NoneThe session, once launched.
A new wrapper each call, all reaching the same session under the same lock. There is
no cached Py<PySession>: caching one would mean a session object that outlives the
VM it addresses, and the Held::InSandbox indirection exists precisely so a
post-terminate call reports the lifecycle rather than a dangling handle.
state_reason
Section titled “state_reason”state_reason: str | NoneWhy the VM is in its current state, when the service said.
The absence is information: TRAP-8’s message distinguishes “no stateReason” from an empty one.
suspended_window_seconds
Section titled “suspended_window_seconds”suspended_window_seconds: float | NoneThe suspended window this sandbox asked for at launch, in seconds.
None before this sandbox launches a VM. This accessor reports the requested
window; GetMicrovm also returns the service’s idle policy.
token_installed
Section titled “token_installed”token_installed: boolWhether the agent token has been installed (STATE-2).
Set by the platform reporting RUNNING, not by the launch call: the run hook is what delivers the token, and a launch that died during startup delivered nothing.
tunnel_identity
Section titled “tunnel_identity”tunnel_identity: TunnelIdentity | NoneThe tunnel identity a launch with identity=True generated, or that an adopted
record carried; None otherwise.
Holds the host’s secret half: pass it to Session.tunnel(verify_identity=...), and
store it only where the agent token goes.
was_terminated
Section titled “was_terminated”was_terminated: boolWhether this VM was ever terminated (STATE-11).
Methods
Section titled “Methods”@staticmethoddef adopt( region: Region, microvm_id: str, endpoint: str, agent_token: str, *, port: SupportsIndex | None = None,) -> SandboxA sandbox for a VM another process launched, from its private record.
The lifecycle is read from GetMicrovm, so suspend, resume, and terminate start
from the service’s state and keep every guard. The VM was bootstrapped by its own
launch, so run is refused and no run-hook payload is ever sent. Keep agent_token
in private encrypted storage; it never appears in repr or an error.
adopt_async
Section titled “adopt_async”@staticmethoddef adopt_async( region: Region, microvm_id: str, endpoint: str, agent_token: str, *, port: SupportsIndex | None = None,) -> SandboxThe awaitable twin of Sandbox.adopt.
build_artifact
Section titled “build_artifact”def build_artifact( *, name: str, binary: SupportsGetItem[int, SupportsIndex], code_artifact_uri: str, build_role_arn: str, base_image: BaseImage | None = None, dockerfile: str | None = None, project_dir: str | PathLike[str] | None = None, inherit_workdir: bool = False,) -> bytesThe artifact bytes to upload to code_artifact_uri.
The upload is the caller’s on this path (ensure_image uploads for itself). Same parameters
as [Self::build_image] so the bytes a caller puts in the bucket are the bytes the
build will receive.
build_image
Section titled “build_image”def build_image( *, name: str, binary: SupportsGetItem[int, SupportsIndex], code_artifact_uri: str, build_role_arn: str, size: SizeClass | None = None, base_image: BaseImage | None = None, base_image_version: str | None = None, dockerfile: str | None = None, project_dir: str | PathLike[str] | None = None, repair_guest_identity: bool = False, inherit_workdir: bool = False, run_hook_timeout: RunHookTimeout | None = None, build_hook_timeout: BuildHookTimeout | None = None, tags: dict[str, str] | None = None, log_group: str | None = None, log_stream: str | None = None, token_scope: str | None = None,) -> ImageBuilds an image and waits for it to become usable.
Every local guard runs before the call, which matters because the create happens after the caller’s artifact upload: a rejection AWS raises costs the upload first.
base_image_version pins the managed base to one version, a value
managed_base_versions lists. project_dir bakes the directory’s one
manifest+lockfile pair into an environment layer, by the rule the CLI’s --project
uses; a directory without exactly one pair raises PreconditionError before any call.
What is deliberately not a parameter
A client_token. There is no such field on the core’s request type and none here:
a digest-derived token replays the original create and wedges an image in CREATING
for fifteen hours with no error at all (TRAP-1). token_scope is a CloudTrail
label folded in beside a fresh nonce and cannot become the token.
A capabilities list. repair_guest_identity is a bool and the request injects
["ALL"] itself, so ["CAP_SYS_ADMIN"] — the request AWS rejects after the upload
— is not something a caller can write (TRAP-3).
An architecture. The model’s enum has exactly one value, so the only thing a field
could express is a rejected request.
log_stream is a prefix, never the exact stream name
The platform’s logging.logStream member is an EXACT stream name (prefixes
unsupported), and one build is three VMs writing three streams — so a fixed name
would collapse every build’s logs into one stream. The client appends /<16 hex>
of fresh randomness per build attempt, and the resolved exact name comes back on
Image.log_stream. log_stream requires log_group.
build_image_async
Section titled “build_image_async”def build_image_async( *, name: str, binary: SupportsGetItem[int, SupportsIndex], code_artifact_uri: str, build_role_arn: str, size: SizeClass | None = None, base_image: BaseImage | None = None, base_image_version: str | None = None, dockerfile: str | None = None, project_dir: str | PathLike[str] | None = None, repair_guest_identity: bool = False, inherit_workdir: bool = False, run_hook_timeout: RunHookTimeout | None = None, build_hook_timeout: BuildHookTimeout | None = None, tags: dict[str, str] | None = None, log_group: str | None = None, log_stream: str | None = None, token_scope: str | None = None,) -> ImageThe awaitable twin of build_image, with its keywords. A lifecycle transition:
cancelling the awaitable leaves the build running to completion on this sandbox.
create_async
Section titled “create_async”@staticmethoddef create_async( region: Region, *, retry: tuple[SupportsIndex, SupportsFloat | SupportsIndex] | None = None,) -> SandboxThe awaitable twin of Sandbox(region): resolves credentials on the shared runtime.
detach
Section titled “detach”def detach() -> DetachedHands the VM off to another process and returns what that process adopts it with.
For a workflow whose steps run in different processes: the launching step calls this
instead of dropping the sandbox (which warns that a live VM was abandoned), persists
the returned record privately, and a later step calls Sandbox.adopt with it. The VM
keeps running and no AWS call is made. Afterwards this sandbox is inert: its session
is gone and run, wait_until_running, suspend, resume, and terminate are
refused. Raises PreconditionError without a live VM or when already detached.
ensure_image
Section titled “ensure_image”def ensure_image( *, name_prefix: str, binary: SupportsGetItem[int, SupportsIndex], dockerfile: str, s3_bucket: str, build_role_arn: str, context_dir: str | PathLike[str] | None = None, s3_key_prefix: str | None = None, size: SizeClass | None = None, base_image: BaseImage | None = None, base_image_version: str | None = None, project_dir: str | PathLike[str] | None = None, force: bool = False, repair_guest_identity: bool = False, tags: dict[str, str] | None = None, wait_timeout: SupportsFloat | SupportsIndex | None = None,) -> EnsuredImageBuilds or reuses the content-addressed image for a task: one call from build inputs to a ready image.
The name is <name_prefix>-<hash12>, the hash over the daemon, the Dockerfile, the
build context, the base image and the size class, so equal inputs name one image.
The ARN is built from the caller’s account (looked up once per sandbox). The image
is described, then:
- ready: returned, with
reused=Trueand no upload; - building: waited out and returned,
reused=True; - failed, or any state under
force=True: deleted, the name awaited free, rebuilt; - absent: the artifact is uploaded to
s3://<s3_bucket>/<s3_key_prefix>/<name>/ artifact.zipand the image created and waited for,reused=False.
When a concurrent caller creates the name first, this call’s create is refused; it
describes again and waits for that build, returning it with reused=True.
dockerfile is usually wrap_dockerfile(task). context_dir is the directory the
Dockerfile’s COPY lines read, taken as docker build takes it:
Dockerfile.dockerignore, else .dockerignore, is honoured, and symlinks are skipped
with a line in warnings. base_image defaults to
BaseImage.from_dockerfile(dockerfile). base_image_version and project_dir are
build_image’s, and both join the name’s hash. wait_timeout is the build wait in seconds
(45 minutes by default). repair_guest_identity repairs identity during creation and
joins the name’s hash. Every local check runs before
the first AWS call.
ensure_image_async
Section titled “ensure_image_async”def ensure_image_async( *, name_prefix: str, binary: SupportsGetItem[int, SupportsIndex], dockerfile: str, s3_bucket: str, build_role_arn: str, context_dir: str | PathLike[str] | None = None, s3_key_prefix: str | None = None, size: SizeClass | None = None, base_image: BaseImage | None = None, base_image_version: str | None = None, project_dir: str | PathLike[str] | None = None, force: bool = False, repair_guest_identity: bool = False, tags: dict[str, str] | None = None, wait_timeout: SupportsFloat | SupportsIndex | None = None,) -> EnsuredImageThe awaitable twin of ensure_image, with its keywords. Cancellation stops the
local wait and releases this sandbox without recording an image. A build already
accepted by the service keeps running; ensure the same inputs again to join it.
from_name
Section titled “from_name”@staticmethoddef from_name( region: Region, name: str, registry: NameRegistry, *, port: SupportsIndex | None = None,) -> SandboxAdopts the VM registered as name in registry; see Sandbox.adopt.
region must match the record’s: an id from another region addresses nothing here.
from_name_async
Section titled “from_name_async”@staticmethoddef from_name_async( region: Region, name: str, registry: NameRegistry, *, port: SupportsIndex | None = None,) -> SandboxThe awaitable twin of Sandbox.from_name.
managed_base_versions
Section titled “managed_base_versions”def managed_base_versions(base_image_arn: str) -> list[ManagedBaseVersion]ListManagedMicrovmImageVersions, every page: the versions of a managed base, the
values build_image’s base-version pin takes.
base_image_arn is the base’s full ARN, such as
arn:aws:lambda:us-east-1:aws:microvm-image:al2023-1; a bare name is refused.
managed_base_versions_async
Section titled “managed_base_versions_async”def managed_base_versions_async(base_image_arn: str) -> list[ManagedBaseVersion]The awaitable twin of managed_base_versions.
preflight
Section titled “preflight”def preflight( *, name: str, binary: SupportsGetItem[int, SupportsIndex], code_artifact_uri: str, build_role_arn: str, size: SizeClass | None = None, base_image: BaseImage | None = None, base_image_version: str | None = None, dockerfile: str | None = None, project_dir: str | PathLike[str] | None = None, repair_guest_identity: bool = False, inherit_workdir: bool = False, run_hook_timeout: RunHookTimeout | None = None, build_hook_timeout: BuildHookTimeout | None = None, tags: dict[str, str] | None = None, log_group: str | None = None, log_stream: str | None = None, token_scope: str | None = None,) -> NoneEvery local guard build_image runs, with zero calls: raises the refusal build_image
would, so a caller who uploads its own artifact checks the request before paying for
the upload. Takes build_image’s keywords.
resume
Section titled “resume”def resume() -> SessionThaws the VM and returns a usable session.
What the core refuses, before any wire call
A resume after terminate (STATE-11) — a terminated VM never returns to RUNNING,
and even a call the service accepted would hand back a different machine. A handle
in neither RUNNING nor SUSPENDED also refuses locally (STATE-7).
What the service decides (STATE-12)
How long the VM has been suspended. The idlePolicy terminates a VM the idle policy
suspended once suspended_sec passes, and a VM this sandbox suspended stayed
resumable 1283 s into a 60 s window (measured 2026-10-12, us-east-1).
WindowClosedError reports a VM the service says is TERMINATING or TERMINATED,
whatever ended it. The sandbox records it as TERMINATED.
With a locally RUNNING handle, resume reads GetMicrovm first. It resumes an
observed SUSPENDED VM and reports WindowClosedError for a VM that has ended.
Other known service states refuse the request; a later call can read the service
again.
Nothing is re-delivered: no run-hook payload, no token, no bootstrap. The in-memory token survived the freeze, and re-delivering it would hit the daemon’s one-shot bootstrap and be refused — a 409 that reads like a broken VM.
resume_async
Section titled “resume_async”def resume_async() -> SessionThe awaitable twin of resume. A lifecycle transition: cancelling the awaitable leaves
the resume running to completion.
def run( *, image_identifier: str | None = None, image_version: str | None = None, execution_role_arn: str | None = None, agent_token: str | None = None, client_token: str | None = None, launch_env: dict[str, str] | None = None, identity: bool = False, egress: bool = False, egress_network_connectors: SupportsGetItem[int, str] | None = None, deny_egress: bool = False, shell: bool = False, max_idle_sec: SupportsIndex | None = None, suspended_sec: SupportsIndex | None = None, auto_resume: bool = False, max_duration_sec: SupportsIndex | None = None, ready_timeout: SupportsFloat | SupportsIndex | None = None, token_scope: str | None = None, wait: bool = True, log_group: str | None = None, log_stream: str | None = None, disable_logging: bool = False,) -> SessionLaunches a MicroVM and returns its session. By default it waits for RUNNING
and for the daemon to answer; wait=False skips those readiness waits.
ready_timeout bounds the wait for RUNNING (default
session_constants()["defaultRunningTimeoutSeconds"]); the wait for the daemon after it
is defaultReadyTimeoutSeconds, and a daemon that never answers raises
TimeoutError with the VM left RUNNING.
image_identifier is an image ARN or a bare image name, or omitted for the image
build_image built. The core resolves a name to its ARN with one ListMicrovmImages
read, and a name no image carries raises PreconditionError before anything launches.
egress requests the managed INTERNET_EGRESS connector. Omission does not block
outbound traffic. For no egress, pass existing VPC connector ARNs through
egress_network_connectors, using a VPC without an internet gateway or NAT
gateway. deny_egress sets advisory proxy variables that workloads can bypass.
identity=True generates a tunnel identity and delivers the VM’s half with the
launch, so Session.tunnel(port, verify_identity=sandbox.tunnel_identity) can prove
the far end is this VM’s daemon.
What the core refuses here, and this file does not
A second run on one sandbox, with zero control-plane calls: the agent token is
installed at most once per VM lifetime (STATE-3), and a second VM needs a second
Sandbox. A run with no image at all, before any call. Neither check is in this
file.
agent_token is optional because the common case is a per-VM secret nobody needs to
see; a caller who has one already — a harness minting its own, or a retry that must
reuse the first attempt’s — passes it. It rides in runHookPayload, which is what
keeps it out of the shared image snapshot.
run_async
Section titled “run_async”def run_async( *, image_identifier: str | None = None, image_version: str | None = None, execution_role_arn: str | None = None, agent_token: str | None = None, client_token: str | None = None, launch_env: dict[str, str] | None = None, identity: bool = False, egress: bool = False, egress_network_connectors: SupportsGetItem[int, str] | None = None, deny_egress: bool = False, shell: bool = False, max_idle_sec: SupportsIndex | None = None, suspended_sec: SupportsIndex | None = None, auto_resume: bool = False, max_duration_sec: SupportsIndex | None = None, ready_timeout: SupportsFloat | SupportsIndex | None = None, token_scope: str | None = None, wait: bool = True, log_group: str | None = None, log_stream: str | None = None, disable_logging: bool = False,) -> SessionThe awaitable twin of run, with its keywords. A lifecycle transition: cancelling the
awaitable stops pending retries. An in-flight request finishes so an accepted VM
is still recorded on this sandbox and remains available to terminate.
suspend
Section titled “suspend”def suspend() -> strFreezes the VM and waits for the platform to report it.
A freeze and restore rather than a stop and start: the guest keeps its memory, so the token, the filesystem, and every exec record survive. The one thing that does not is the guest’s view of time — it observes the whole suspension as a single jump, so any timeout, lease, or TLS session a running command holds expires at once on resume.
A suspend from anything but RUNNING is refused by the core with zero control-plane
calls (STATE-5). Returns the state reached, which may be "TERMINATED": a VM that
dies while suspending is a state to report rather than an exception out of the
middle of a teardown.
suspend_async
Section titled “suspend_async”def suspend_async() -> strThe awaitable twin of suspend. A lifecycle transition: cancelling the awaitable
leaves the suspend running to completion.
terminate
Section titled “terminate”def terminate( *, delete_image: bool = False, delete_log_group: bool = False, delete_attempts: SupportsIndex | None = None, delete_backoff: SupportsFloat | SupportsIndex | None = None, wait_for_terminated: bool | SupportsFloat | SupportsIndex = ...,) -> TeardownReportTears down, best-effort, never raising.
Order: VM, then image, then the log group last, because the service can recreate a group deleted before its image.
Both deletions are opt-in, because both destroy something a caller may still want:
the image is reusable across runs, and the log group is where a failed build’s only
evidence lives. delete_log_group=True names the group in
report.undeleted rather than deleting it — CloudWatch is not in the core’s
dependency set, and reporting a leak beats reporting a clean teardown over one.
wait_for_terminated=False by default: the caller is on the way out, and a teardown
that blocked five minutes on a state nobody reads is five minutes of a CI job. The
report then honestly ends in "TERMINATING". True waits for TERMINATED up to the
core’s lifecycle default; a number of seconds waits up to that instead.
terminate_async
Section titled “terminate_async”def terminate_async( *, delete_image: bool = False, delete_log_group: bool = False, delete_attempts: SupportsIndex | None = None, delete_backoff: SupportsFloat | SupportsIndex | None = None, wait_for_terminated: bool | SupportsFloat | SupportsIndex = ...,) -> TeardownReportThe awaitable twin of terminate, with its keywords; it never raises either. A
lifecycle transition: cancelling the awaitable leaves the teardown running to
completion, since a teardown abandoned halfway is a leak.
wait_until_running
Section titled “wait_until_running”def wait_until_running( *, timeout: SupportsFloat | SupportsIndex | None = None,) -> SessionFinishes a run(wait=False): waits for RUNNING and for the daemon to answer, and
returns the session. timeout bounds the wait for RUNNING, as ready_timeout does.
A launch whose client_token adopted an existing, idle-suspended VM resumes it;
a fresh launch that reaches a terminal state first raises LaunchDiedError with
the service’s stateReason. Refused unless the launch is still PENDING.
wait_until_running_async
Section titled “wait_until_running_async”def wait_until_running_async( *, timeout: SupportsFloat | SupportsIndex | None = None,) -> SessionThe awaitable twin of wait_until_running. Cancelling it stops the wait, not the
launch: the VM keeps starting, and a later wait_until_running picks it up.
Special methods
Section titled “Special methods”__aenter__
Section titled “__aenter__”def __aenter__() -> Sandboxasync with sandbox as s: — returns the sandbox itself.
__aexit__
Section titled “__aexit__”def __aexit__( exc_type: Any | None = None, exc_value: Any | None = None, traceback: Any | None = None,) -> bool__exit__’s teardown, awaited: discards the report, returns False, and, a lifecycle
transition, runs to completion even when the task awaiting it is cancelled.
__enter__
Section titled “__enter__”def __enter__() -> Sandboxwith sandbox as s: — returns the sandbox itself.
__exit__
Section titled “__exit__”def __exit__( exc_type: Any | None = None, exc_value: Any | None = None, traceback: Any | None = None,) -> boolTears down on the way out, whatever happened inside the block.
Returns False, so an exception raised inside the block propagates: the teardown is
what has to happen, not what has to be reported. Any leak is on the report the
caller can also get by calling terminate() themselves.
__repr__
Section titled “__repr__”def __repr__() -> strProvenance
Section titled “Provenance”This page is generated from bindings/microvms-py/microvms.pyi by Griffe 2.3.0, run from site/scripts/gen-reference.mjs on every pnpm run sync, so an edit made here is overwritten by the next run.
To change the page, change the doc comment in the binding’s Rust source, then regenerate the declarations with mise run stubs. mise run stubs:check fails when the committed file no longer matches the source.