Skip to main content
Every SmolVM sandbox follows a simple lifecycle: you create it, start it, run your work, and tear it down. Most of the time the context manager (with SmolVM() as vm) handles all of this for you. This guide covers the full lifecycle for cases where you need more control.

Lifecycle states

A sandbox progresses through these states:
  • CREATED - VM configured but not started
  • RUNNING - VM is booted and operational
  • PAUSED - VM execution is suspended (Firecracker only)
  • STOPPED - VM has been gracefully shut down
  • ERROR - VM encountered a fatal error

Creating a sandbox

1

Import SmolVM

from smolvm import SmolVM
2

Create VM instance

# Auto-configuration mode
vm = SmolVM()
print(f"Created VM: {vm.vm_id}")
print(f"Status: {vm.status}")  # VMState.CREATED
3

Verify VM is created

The VM is now registered in SmolVM’s state database but not yet running.

Starting a sandbox

Start the sandbox to boot the guest operating system:
from smolvm import SmolVM

vm = SmolVM()
vm.start(boot_timeout=30.0)
print(f"VM started: {vm.status}")  # VMState.RUNNING
print(f"IP address: {vm.get_ip()}")

Boot process

When you call start(), SmolVM:
  1. Launches the Firecracker/QEMU process
  2. Boots the kernel with configured boot arguments
  3. Waits for the VM to become responsive
  4. Injects environment variables if configured (see env_vars in VMConfig)

Method signature

def start(self, boot_timeout: float = 30.0) -> SmolVM:
    """Start the VM.

    Args:
        boot_timeout: Maximum seconds to wait for boot.

    Returns:
        self for method chaining.
    """
If a sandbox is already running, calling start() is a no-op and returns immediately.

Stopping a sandbox

Gracefully shut down a running sandbox:
vm.stop(timeout=3.0)
print(f"VM stopped: {vm.status}")  # VMState.STOPPED
The stop() method:
  • Sends a shutdown signal to the guest
  • Waits up to timeout seconds for graceful shutdown
  • Cleans up port forwarding rules
  • Closes SSH connections

Method signature

def stop(self, timeout: float = 3.0) -> SmolVM:
    """Stop the VM.

    Args:
        timeout: Seconds to wait for graceful shutdown.

    Returns:
        self for method chaining.
    """

Pausing and resuming a VM

On the Firecracker backend, you can pause a running VM and resume it later. This suspends execution without stopping the VM process, so the guest resumes exactly where it left off.
vm.pause()
print(f"Status: {vm.status}")  # VMState.PAUSED

# Later, resume execution
vm.resume()
print(f"Status: {vm.status}")  # VMState.RUNNING
Pausing cleans up port forwarding rules and closes SSH connections. After resuming, the next run() call re-establishes SSH automatically.
If you call start() on a paused VM, it automatically calls resume() instead.

Deleting a sandbox

Permanently delete a sandbox and release all resources:
vm.delete()
# VM is now removed from state database
# All network resources are cleaned up
Deletion is permanent. The VM cannot be recovered after deletion.

Context manager pattern

The recommended way to manage the lifecycle is with Python’s with statement:
from smolvm import SmolVM

with SmolVM() as vm:
    # VM automatically starts on context entry
    print(f"VM running: {vm.vm_id}")
    result = vm.run("hostname")
    print(result.output)
    # VM automatically stops and deletes on context exit

Context manager behavior

When you create a VM with SmolVM(config) or SmolVM():
  • __enter__: Automatically starts the VM
  • __exit__: Stops AND deletes the VM
# VM is created, started, and cleaned up automatically
with SmolVM() as vm:
    vm.run("echo 'hello'")
# VM is now deleted

Reconnecting to existing sandboxes

Reconnect to a sandbox that was created earlier:
from smolvm import SmolVM

# Create a VM without context manager
vm = SmolVM()
vm.start()
vm_id = vm.vm_id
vm.close()  # Release resources but don't delete

# Later, reconnect to the same VM
vm2 = SmolVM.from_id(vm_id)
print(f"Reconnected to {vm2.vm_id}")
print(f"Status: {vm2.status}")  # VMState.RUNNING

# Continue using the VM
result = vm2.run("uptime")
print(result.output)

Class method signature

@classmethod
def from_id(
    cls,
    vm_id: str,
    *,
    data_dir: Path | None = None,
    socket_dir: Path | None = None,
    backend: str | None = None,
    ssh_user: str = "root",
    ssh_key_path: str | None = None,
) -> SmolVM:
    """Reconnect to an existing VM by ID."""

Manual lifecycle management

For advanced use cases where you need explicit control:
from smolvm import SmolVM

try:
    # Create but don't start
    vm = SmolVM()
    print(f"VM created: {vm.vm_id} (status: {vm.status})")

    # Manually start when ready
    vm.start()
    print(f"VM started (status: {vm.status})")

    # Do work
    result = vm.run("cat /etc/os-release")
    print(result.output)

    # Manually stop
    vm.stop()
    print(f"VM stopped (status: {vm.status})")

finally:
    # Clean up resources
    vm.delete()
    print("VM deleted")

Long-running sandboxes

For persistent sandboxes that survive across Python sessions:
1

Create a VM with a stable ID

from smolvm import SmolVM, VMConfig
from smolvm.build import ImageBuilder, SSH_BOOT_ARGS

builder = ImageBuilder()
kernel, rootfs = builder.build_alpine_ssh()

config = VMConfig(
    vm_id="persistent-agent-vm",  # Stable, reusable ID
    vcpu_count=2,
    mem_size_mib=2048,
    kernel_path=kernel,
    rootfs_path=rootfs,
    boot_args=SSH_BOOT_ARGS,
)

vm = SmolVM(config)
vm.start()
print(f"Created persistent VM: {vm.vm_id}")
vm.close()  # Release SDK resources, keep VM running
2

Reconnect in another session

from smolvm import SmolVM

# In a different Python session or script
vm = SmolVM.from_id("persistent-agent-vm")
print(f"Reconnected to {vm.vm_id}")
print(f"Status: {vm.status}")

result = vm.run("uptime")
print(result.output)
3

Clean up when done

vm = SmolVM.from_id("persistent-agent-vm")
vm.stop()
vm.delete()
print("VM permanently deleted")

Waiting for SSH

You can explicitly wait for SSH to become available:
vm = SmolVM()
vm.start()

# Wait up to 60 seconds for SSH
vm.wait_for_ssh(timeout=60.0)
print("SSH is ready")

# Now run commands
result = vm.run("whoami")
print(result.output)
You typically don’t need to call wait_for_ssh() explicitly. The run() method automatically waits for SSH on first use.

Refreshing state

Properties like status and info are cached. Refresh them from the state store:
vm = SmolVM.from_id("my-vm")
print(vm.status)  # Cached value

vm.refresh()
print(vm.status)  # Fresh value from database

Snapshots

You can save the full state of a running VM and restore it later using snapshots. This is useful for checkpointing before risky operations or reusing a configured environment.
# Create a snapshot
snapshot = vm.snapshot(snapshot_id="my-checkpoint")

# Later, restore it
restored_vm = SmolVM.from_snapshot("my-checkpoint", resume_vm=True)
See the Snapshots guide for full details.

Next steps

Snapshots

Save and restore sandbox state

Port forwarding

Expose services running inside sandboxes to your host

Environment variables

Configure sandbox environment dynamically

AI agent integration

Build secure AI agent sandboxes
Last modified on April 6, 2026