Skip to main content

Overview

The SmolVM class provides a user-friendly interface for creating and managing microVMs. It supports automatic configuration, context manager usage, and SSH-based command execution.

Constructor

SmolVM(
    config: VMConfig | None = None,
    *,
    vm_id: str | None = None,
    data_dir: Path | None = None,
    socket_dir: Path | None = None,
    backend: str | None = None,
    mem_size_mib: int | None = None,
    disk_size_mib: int | None = None,
    ssh_user: str = "root",
    ssh_key_path: str | None = None,
)
config
VMConfig | None
default:"None"
VM configuration. Mutually exclusive with vm_id. If omitted (and vm_id is omitted), SmolVM auto-creates a default SSH-capable VM configuration.
vm_id
str | None
default:"None"
ID of an existing VM to reconnect to. Mutually exclusive with config.
data_dir
Path | None
default:"None"
Override the default data directory for VM state storage.
socket_dir
Path | None
default:"None"
Override the default socket directory.
backend
str | None
default:"None"
Runtime backend override. Options: "firecracker", "qemu", or "auto".
mem_size_mib
int | None
default:"None"
Guest memory in MiB for auto-config mode (when both config and vm_id are omitted). Default is 512 MiB.
disk_size_mib
int | None
default:"None"
Root filesystem size in MiB for auto-config mode. Default is 512 MiB. Minimum is 64 MiB.
ssh_user
str
default:"root"
SSH user for command execution via the run() method.
ssh_key_path
str | None
default:"None"
SSH private key path. If omitted, SmolVM first tries default SSH auth, then falls back to ~/.smolvm/keys/id_ed25519.

Raises

  • ValueError: If both config and vm_id are provided, or if mem_size_mib/disk_size_mib are set outside auto-config mode.

Class Methods

from_id

@classmethod
SmolVM.from_id(
    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.
vm_id
str
required
VM identifier to reconnect to.
data_dir
Path | None
default:"None"
Override the default data directory.
socket_dir
Path | None
default:"None"
Override the default socket directory.
backend
str | None
default:"None"
Runtime backend override.
ssh_user
str
default:"root"
SSH user for command execution.
ssh_key_path
str | None
default:"None"
SSH private key path.
return
SmolVM
A SmolVM instance bound to the existing VM.

Raises

  • VMNotFoundError: If no VM with this ID exists.

Lifecycle Methods

start

def start(self, boot_timeout: float = 30.0) -> SmolVM
Start the VM. If the VM config contains env_vars, they are injected into the guest via SSH after boot completes.
boot_timeout
float
default:"30.0"
Maximum seconds to wait for boot to complete.
return
SmolVM
Returns self for method chaining.

Raises

  • SmolVMError: If env_vars is set but the image does not support SSH (missing init=/init in boot args).

stop

def stop(self, timeout: float = 3.0) -> SmolVM
Stop the VM gracefully.
timeout
float
default:"3.0"
Seconds to wait for graceful shutdown.
return
SmolVM
Returns self for method chaining.

delete

def delete(self) -> None
Delete the VM and release all resources.

Command Execution Methods

run

def run(
    self,
    command: str,
    timeout: int = 30,
    shell: Literal["login", "raw"] = "login",
) -> CommandResult
Execute a command on the guest via SSH. Lazily creates an SSH client on first call and reuses it for subsequent invocations.
command
str
required
Shell command to execute on the guest.
timeout
int
default:"30"
Maximum seconds to wait for the command to complete.
shell
Literal['login', 'raw']
default:"login"
Command execution mode:
  • "login" (default): run via guest login shell
  • "raw": execute command directly with no shell wrapping
return
CommandResult
Result object containing exit code, stdout, and stderr.

Raises

  • SmolVMError: If the VM is not running or has no network.
  • CommandExecutionUnavailableError: If SSH is not available on the guest.

wait_for_ssh

def wait_for_ssh(self, timeout: float = 60.0) -> SmolVM
Wait for SSH to become available on the guest.
timeout
float
default:"60.0"
Maximum seconds to wait.
return
SmolVM
Returns self for method chaining.

Raises

  • OperationTimeoutError: If SSH is not available within the timeout.
  • SmolVMError: If the VM is not running.

ssh_commands

def ssh_commands(
    self,
    *,
    ssh_user: str | None = None,
    key_path: str | Path | None = None,
    public_host: str | None = None,
) -> dict[str, str]
Get ready-to-run SSH commands for this VM.
ssh_user
str | None
default:"None"
SSH user override. Defaults to the instance’s configured ssh_user.
key_path
str | Path | None
default:"None"
SSH key path override.
public_host
str | None
default:"None"
Public hostname override for remote access.
return
dict[str, str]
Dictionary mapping command names to ready-to-run SSH command strings.

Environment Variable Methods

set_env_vars

def set_env_vars(
    self,
    env_vars: dict[str, str],
    *,
    merge: bool = True
) -> list[str]
Set environment variables on a running VM. Variables are persisted in /etc/profile.d/smolvm_env.sh and affect new SSH sessions/login shells.
env_vars
dict[str, str]
required
Key/value pairs to set.
merge
bool
default:"True"
If True, merge with existing variables. If False, replace all variables.
return
list[str]
Sorted list of variable names present after the update.

unset_env_vars

def unset_env_vars(self, keys: list[str]) -> dict[str, str]
Remove environment variables from a running VM.
keys
list[str]
required
Variable names to remove.
return
dict[str, str]
Mapping of removed keys to their previous values.

list_env_vars

def list_env_vars(self) -> dict[str, str]
Return SmolVM-managed environment variables for a running VM.
return
dict[str, str]
Dictionary of environment variable names to values.

Port Forwarding Methods

expose_local

def expose_local(
    self,
    guest_port: int,
    host_port: int | None = None
) -> int
Expose a guest TCP port on localhost only. Forwards 127.0.0.1:<host_port> on the host to <guest_ip>:<guest_port> inside the VM.
guest_port
int
required
Guest TCP port to expose (1-65535).
host_port
int | None
default:"None"
Host localhost port. If omitted, an available port is automatically chosen.
return
int
The host localhost port to connect to.

Raises

  • SmolVMError: If the VM is not running or has no network.
  • ValueError: If port numbers are out of valid range (1-65535).

unexpose_local

def unexpose_local(self, host_port: int, guest_port: int) -> SmolVM
Remove a previously configured localhost-only port forward.
host_port
int
required
Host localhost port (1-65535).
guest_port
int
required
Guest TCP port (1-65535).
return
SmolVM
Returns self for method chaining.

Properties

vm_id

@property
def vm_id(self) -> str
The VM identifier.

info

@property
def info(self) -> VMInfo
Current VM runtime information (cached). Call refresh() to update from the state store.

status

@property
def status(self) -> VMState
Current VM lifecycle state (cached). Values: CREATED, RUNNING, STOPPED, ERROR.

data_dir

@property
def data_dir(self) -> Path
Directory backing the VM state database and logs.

Utility Methods

get_ip

def get_ip(self) -> str
Return the guest IP address.
return
str
The guest VM’s IP address.

Raises

  • SmolVMError: If the VM has no network configuration.

refresh

def refresh(self) -> SmolVM
Refresh cached VM info from the state store.
return
SmolVM
Returns self for method chaining.

can_run_commands

def can_run_commands(self) -> bool
Whether this VM config supports command execution via SSH. Command execution requires SmolVM’s SSH init flow, enabled by booting with init=/init.
return
bool
True if SSH command execution is supported, False otherwise.

close

def close(self) -> None
Release underlying SDK resources for this facade instance.

Context Manager

SmolVM implements the context manager protocol for automatic lifecycle management:
with SmolVM() as vm:
    # VM auto-starts on context entry
    result = vm.run("uname -r")
    print(result.stdout)
# VM auto-stops and auto-deletes on context exit
On context entry (__enter__):
  • Auto-starts VMs created by this facade instance
On context exit (__exit__):
  • Best-effort stop if the VM is running
  • Auto-deletes only VMs created by this facade instance (not reconnected VMs)
  • Releases all resources via close()

Usage Examples

Auto-Configuration Mode

Create an SSH-ready VM with default settings:
from smolvm import SmolVM

with SmolVM() as vm:
    result = vm.run("echo 'Hello from SmolVM'")
    print(result.stdout.strip())

Custom Configuration

Create a VM with specific resources:
from smolvm import SmolVM

with SmolVM(mem_size_mib=1024, disk_size_mib=1024) as vm:
    result = vm.run("free -m")
    print(result.stdout)

Manual Lifecycle Management

from smolvm import SmolVM, VMConfig
from pathlib import Path

config = VMConfig(
    vm_id="my-vm",
    vcpu_count=2,
    mem_size_mib=512,
    kernel_path=Path("/path/to/kernel"),
    rootfs_path=Path("/path/to/rootfs"),
    boot_args="console=ttyS0 reboot=k panic=1 init=/init"
)

vm = SmolVM(config)
vm.start()

result = vm.run("hostname")
print(result.stdout)

vm.stop()
vm.delete()
vm.close()

Reconnecting to Existing VM

from smolvm import SmolVM

# Reconnect to a VM created earlier
vm = SmolVM.from_id("my-vm")
result = vm.run("uptime")
print(result.stdout)
vm.close()

Port Forwarding

from smolvm import SmolVM

with SmolVM() as vm:
    # Install and start a web server
    vm.run("apk add python3")
    vm.run("python3 -m http.server 8000 &")
    
    # Expose guest port 8000 on localhost
    host_port = vm.expose_local(guest_port=8000)
    print(f"Server available at http://localhost:{host_port}")
    
    # Access the server from host
    # ...
    
    # Clean up port forward
    vm.unexpose_local(host_port=host_port, guest_port=8000)

Environment Variables

from smolvm import SmolVM

with SmolVM() as vm:
    # Set environment variables
    vm.set_env_vars({"API_KEY": "secret", "ENV": "production"})
    
    # List current variables
    env_vars = vm.list_env_vars()
    print(env_vars)  # {'API_KEY': 'secret', 'ENV': 'production'}
    
    # Variables persist across sessions
    result = vm.run("echo $API_KEY")
    print(result.stdout.strip())  # secret
    
    # Remove variables
    removed = vm.unset_env_vars(["API_KEY"])
    print(removed)  # {'API_KEY': 'secret'}

Build docs developers (and LLMs) love