Skip to main content
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)
)
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())
Start the Docker service:
open -a Docker
Add your user to the docker group:
sudo usermod -aG docker $USER
newgrp docker  # or log out and back in
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
Last modified on April 6, 2026