VM Lifecycle States
A SmolVM instance progresses through these states:
CREATED - VM configured but not started
RUNNING - VM is booted and operational
STOPPED - VM has been gracefully shut down
Creating a VM
Import SmolVM
from smolvm import SmolVM
Create VM instance
# Auto-configuration mode
vm = SmolVM()
print(f"Created VM: {vm.vm_id}")
print(f"Status: {vm.status}") # VMState.CREATED
Verify VM is created
The VM is now registered in SmolVM’s state database but not yet running.
Starting a VM
Start the VM 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:
- Launches the Firecracker/QEMU process
- Boots the kernel with configured boot arguments
- Waits for the VM to become responsive
- 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 VM is already running, calling start() is a no-op and returns immediately.
Stopping a VM
Gracefully shut down a running VM:
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.
"""
Deleting a VM
Permanently delete a VM 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 VM lifecycle is using Python’s context manager:
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
When you reconnect with SmolVM.from_id(vm_id):
__enter__: Does NOT start the VM (preserves state)
__exit__: Stops but does NOT delete
# Reconnect to existing VM
with SmolVM.from_id("my-vm") as vm:
vm.run("echo 'hello'")
# VM is stopped but still exists
Reconnecting to Existing VMs
Reconnect to a VM 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 VMs
For persistent VMs that survive across Python sessions:
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
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)
Clean up when done
vm = SmolVM.from_id("persistent-agent-vm")
vm.stop()
vm.delete()
print("VM permanently deleted")
Waiting for SSH
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 VM State
VM 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
Next Steps