By default, SmolVM boots a minimal Alpine Linux image with SSH. If you need specific packages, libraries, or a different base OS, you can build a custom image using the ImageBuilder class. Custom images are built with Docker and cached locally so you only build once.
Quick start
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 = "my-custom-vm" ,
vcpu_count = 1 ,
mem_size_mib = 512 ,
kernel_path = kernel,
rootfs_path = rootfs,
boot_args = SSH_BOOT_ARGS ,
)
with SmolVM(config) as vm:
result = vm.run( "cat /etc/os-release" )
print (result.output)
ImageBuilder requires Docker to be installed and running. On macOS, use Docker Desktop. On Linux, install docker.io.
Build methods
SmolVM provides three image build methods. Each creates a Linux filesystem with SSH pre-configured and returns a (kernel_path, rootfs_path) tuple.
Alpine with SSH password
The simplest option — builds a minimal Alpine Linux image with root password authentication:
kernel, rootfs = builder.build_alpine_ssh(
name = "my-alpine" , # Cache name (default: "alpine-ssh")
ssh_password = "mypassword" , # Root password (default: "smolvm")
rootfs_size_mb = 512 , # Disk size in MB (default: 512)
)
Alpine with SSH key (recommended)
More secure — uses public key authentication instead of a password:
from smolvm.utils import ensure_ssh_key
# Generate or load SSH keys
private_key, public_key = ensure_ssh_key()
kernel, rootfs = builder.build_alpine_ssh_key(
ssh_public_key = public_key,
name = "alpine-key" ,
rootfs_size_mb = 512 ,
)
config = VMConfig(
vm_id = "key-vm" ,
vcpu_count = 1 ,
mem_size_mib = 512 ,
kernel_path = kernel,
rootfs_path = rootfs,
boot_args = SSH_BOOT_ARGS ,
)
with SmolVM(config, ssh_key_path = str (private_key)) as vm:
result = vm.run( "whoami" )
print (result.output) # "root"
Debian with SSH key
For workloads that need apt, glibc, or broader package compatibility. Debian images are larger but more compatible:
from smolvm.utils import ensure_ssh_key
private_key, public_key = ensure_ssh_key()
kernel, rootfs = builder.build_debian_ssh_key(
ssh_public_key = public_key,
name = "debian-key" ,
rootfs_size_mb = 2048 , # Debian needs more space
base_image = "debian:bookworm-slim" ,
)
config = VMConfig(
vm_id = "debian-vm" ,
vcpu_count = 2 ,
mem_size_mib = 1024 ,
kernel_path = kernel,
rootfs_path = rootfs,
boot_args = SSH_BOOT_ARGS ,
)
with SmolVM(config, ssh_key_path = str (private_key)) as vm:
result = vm.run( "cat /etc/debian_version" )
print (result.output)
Boot arguments
Use the SSH_BOOT_ARGS constant for any SSH-capable image:
from smolvm.build import SSH_BOOT_ARGS
print ( SSH_BOOT_ARGS )
# "console=ttyS0 reboot=k panic=1 pci=off root=/dev/vda rw init=/init"
The init=/init parameter is required for SmolVM’s SSH functionality. Without it, the custom init script will not run and SSH will not start.
Image caching
ImageBuilder caches built images to avoid rebuilding. The first build downloads base images and runs Docker. Subsequent calls with the same name return cached paths instantly:
builder = ImageBuilder()
# First build — downloads and builds everything
kernel1, rootfs1 = builder.build_alpine_ssh( name = "my-image" )
# Second call — returns cached paths immediately
kernel2, rootfs2 = builder.build_alpine_ssh( name = "my-image" )
For key-based images, the cache is automatically invalidated if the SSH key is newer than the cached image.
Custom cache directory
from pathlib import Path
builder = ImageBuilder( cache_dir = Path( "/mnt/fast-ssd/vm-images" ))
kernel, rootfs = builder.build_alpine_ssh()
print (kernel) # /mnt/fast-ssd/vm-images/alpine-ssh/vmlinux.bin
print (rootfs) # /mnt/fast-ssd/vm-images/alpine-ssh/rootfs.ext4
Custom kernel URLs
Override the default kernel URL if you need a specific kernel version:
kernel, rootfs = builder.build_alpine_ssh(
kernel_url = "https://example.com/my-custom-kernel"
)
SmolVM ships default kernel URLs for both Firecracker and QEMU backends, targeting x86_64 and aarch64 architectures. You can inspect them:
from smolvm.build import FIRECRACKER_KERNEL_URLS , QEMU_KERNEL_URLS
print ( FIRECRACKER_KERNEL_URLS )
print ( QEMU_KERNEL_URLS )
How the init script works
All SSH-capable images include a custom /init script that runs as PID 1 inside the sandbox. This script:
Mounts essential filesystems (/proc, /sys, /dev, /tmp)
Configures networking from kernel boot arguments
Sets up DNS resolution (8.8.8.8)
Generates SSH host keys if missing
Starts the SSH daemon
Stays alive as PID 1 to keep the sandbox running
You do not need to modify this script for most use cases. It is included automatically when you use any ImageBuilder method.
Real-world example: OpenClaw
This example from examples/openclaw.py builds a 4 GB Debian image for the OpenClaw application:
import os
from smolvm import SmolVM, VMConfig
from smolvm.build import ImageBuilder, SSH_BOOT_ARGS
from smolvm.utils import ensure_ssh_key
private_key, public_key = ensure_ssh_key()
kernel, rootfs = ImageBuilder().build_debian_ssh_key(
ssh_public_key = public_key,
name = "debian-ssh-key-openclaw-4g" ,
rootfs_size_mb = 4096 ,
)
config = VMConfig(
vcpu_count = 1 ,
mem_size_mib = 2048 ,
kernel_path = kernel,
rootfs_path = rootfs,
boot_args = SSH_BOOT_ARGS ,
env_vars = {
"OPENROUTER_API_KEY" : os.getenv( "OPENROUTER_API_KEY" , "" ),
},
)
with SmolVM(config, ssh_key_path = str (private_key)) as vm:
vm.run( "apt-get update && apt-get install -y nodejs npm" )
vm.run( "npm install -g openclaw" )
Troubleshooting
When an image build fails, SmolVM diagnoses the root cause and returns a specific error message. You do not need to debug Docker issues manually.
SmolVM raises an ImageError with install instructions:
macOS : Install Docker Desktop
Linux : sudo apt-get install docker.io (Debian/Ubuntu)
You can check Docker availability before building: builder = ImageBuilder()
if not builder.check_docker():
print (builder.docker_requirement_error())
Docker daemon not running
Start the Docker service: sudo systemctl start docker
Permission denied on Docker socket
Add your user to the docker group: sudo usermod -aG docker $USER
newgrp docker # or log out and back in
Build fails with disk space errors
Image builds require sufficient disk space. Check available space and clean the Docker cache if needed: df -h ~/.smolvm/images/
docker system prune -a
Next steps
Environment variables Inject configuration into custom images
AI agent integration Build specialized images for AI agent sandboxes
VM lifecycle Manage sandboxes built with custom images