Skip to main content
SmolVM is optimized for low-latency agent workflows, providing sub-second boot times and minimal overhead for command execution.

Benchmark Results

Latest lifecycle timings (p50) on a standard Linux host:
PhaseTime
Create + Start~572ms
SSH ready~2.1s
Command execution~43ms
Stop + Delete~751ms
Full lifecycle (boot → run → teardown)~3.5s
Benchmarks measured on AMD Ryzen 7 7800X3D (8C/16T), Ubuntu Linux, KVM/Firecracker backend.

Running Your Own Benchmarks

You can benchmark SmolVM on your hardware using the included benchmark script:
python scripts/benchmarks/bench_subprocess.py --vms 10 -v
This will create, start, run commands in, and teardown 10 VMs sequentially, reporting detailed timing metrics.

Performance Characteristics

Boot Performance

  • MicroVM Creation: SmolVM allocates IP addresses, TAP devices, and network rules in ~572ms
  • SSH Ready Time: The VM boots and becomes accessible via SSH in ~2.1s total
  • Hardware Virtualization: Uses KVM on Linux and Hypervisor.framework on macOS for near-native performance

Runtime Performance

  • Command Execution: ~43ms average latency for running commands via SSH
  • Memory Overhead: Minimal host overhead beyond configured VM memory (default 512MB)
  • CPU Efficiency: Hardware virtualization provides near-native CPU performance

Teardown Performance

  • Graceful Shutdown: Firecracker VMs can be stopped in ~751ms
  • Resource Cleanup: Network rules, TAP devices, and disk images are cleaned up automatically
  • Fast Path for Ephemeral VMs: SIGKILL-based teardown for sandbox VMs that don’t need state preservation

Optimization Tips

1. Reuse VMs for Multiple Commands

Instead of creating a new VM for each command, reuse the same VM:
from smolvm import SmolVM

with SmolVM() as vm:
    vm.start()
    
    # Run multiple commands on the same VM
    result1 = vm.run("pip install requests")
    result2 = vm.run("python script.py")
    result3 = vm.run("cat output.txt")
This amortizes the ~2.1s boot time across multiple operations.

2. Use Appropriate Resource Allocation

Configure CPU and memory based on your workload:
from smolvm import SmolVM, VMConfig

# Lightweight workload
config = VMConfig(
    vcpu_count=1,
    mem_size_mib=256,
    disk_size_mib=2048
)

# Heavy workload
config = VMConfig(
    vcpu_count=4,
    mem_size_mib=2048,
    disk_size_mib=8192
)
Over-allocating resources can lead to host memory pressure and slower performance.

3. Pre-built Custom Images

For workloads requiring specific dependencies, build a custom rootfs image with pre-installed packages:
from smolvm.build import RootfsBuilder

builder = RootfsBuilder()
builder.add_package("python3-requests")
builder.add_package("python3-numpy")
rootfs_path = builder.build()

# Use the custom image
config = VMConfig(rootfs_path=rootfs_path)
vm = SmolVM(config)
This eliminates the need to install packages at runtime.

4. Shared vs Isolated Disk Mode

Choose the appropriate disk mode for your use case: Isolated Mode (default): Each VM gets its own copy of the rootfs
  • ✅ Complete isolation between VMs
  • ✅ No cross-VM contamination
  • ❌ Higher disk usage
  • ❌ Copy overhead on first boot
Shared Mode: All VMs use the same rootfs image
  • ✅ No disk copy overhead
  • ✅ Lower disk usage
  • ❌ Changes persist across VMs
  • ❌ Potential cross-VM contamination
config = VMConfig(disk_mode="shared")  # For read-only workloads

5. Backend Selection

SmolVM supports multiple backends with different performance characteristics:
  • Firecracker (Linux): Fastest boot times, lowest overhead, recommended for production
  • QEMU (macOS/Linux): Broader compatibility, slightly higher overhead
from smolvm import SmolVM

# Explicitly choose backend
vm = SmolVM(backend="firecracker")  # Linux only
vm = SmolVM(backend="qemu")         # macOS/Linux

Performance Monitoring

Check VM Status

from smolvm import SmolVM

manager = SmolVM()
vms = manager.list_vms()

for vm in vms:
    print(f"VM {vm.vm_id}: {vm.status}")
    print(f"  PID: {vm.pid}")
    print(f"  IP: {vm.network.guest_ip if vm.network else 'N/A'}")

Reconcile Stale VMs

Detect and clean up VMs marked as RUNNING but with dead processes:
manager = SmolVM()
stale_vms = manager.reconcile()

if stale_vms:
    print(f"Found {len(stale_vms)} stale VMs")
    for vm_id in stale_vms:
        manager.delete(vm_id)

Scalability Considerations

IP Address Pool

By default, SmolVM allocates IPs from 172.16.0.2 to 172.16.0.254, supporting 253 concurrent VMs.

SSH Port Pool

Host-side SSH forwarding uses ports 2200-2999, supporting 800 concurrent VMs.

System Limits

Check your system’s ulimit for open files and processes:
# Check file descriptor limit
ulimit -n

# Check process limit
ulimit -u

# Increase limits (add to /etc/security/limits.conf)
* soft nofile 65536
* hard nofile 65536

Profiling Tips

Measure Individual Phases

import time
from smolvm import SmolVM, VMConfig

config = VMConfig()

# Create phase
start = time.time()
manager = SmolVM()
vm_info = manager.create(config)
print(f"Create: {time.time() - start:.3f}s")

# Start phase
start = time.time()
manager.start(vm_info.vm_id)
print(f"Start: {time.time() - start:.3f}s")

# Command execution
from smolvm import VM
vm = VM.from_id(vm_info.vm_id)

start = time.time()
vm.run("echo hello")
print(f"Command: {time.time() - start:.3f}s")

# Cleanup
start = time.time()
manager.stop(vm_info.vm_id)
manager.delete(vm_info.vm_id)
print(f"Teardown: {time.time() - start:.3f}s")

Network Latency

Measure network roundtrip time:
from smolvm import SmolVM

with SmolVM() as vm:
    vm.start()
    
    # Ping test from host to guest
    result = vm.run("echo pong")
    print(f"SSH roundtrip: {result.duration_ms:.1f}ms")
Last modified on March 3, 2026