Skip to main content
The ImageManager class handles fetching, caching, and validating VM assets (kernels and root filesystems). It provides secure downloads with SHA-256 verification and atomic file operations.

Class Definition

from smolvm import ImageManager

manager = ImageManager(cache_dir=None, registry=None)

Constructor Parameters

cache_dir
Path | None
default:"~/.smolvm/images/"
Directory to store cached images. If not specified, defaults to ~/.smolvm/images/.
registry
dict[str, ImageSource] | None
default:"BUILTIN_IMAGES"
Custom image registry. If not provided, uses the built-in registry. Useful for adding custom images or testing.

Methods

list_available

def list_available() -> list[str]
List names of all registered images.
return
list[str]
Sorted list of image names available in the registry.

Example

from smolvm import ImageManager

manager = ImageManager()
images = manager.list_available()
print(f"Available images: {', '.join(images)}")
# Output: Available images: hello, quickstart-x86_64

is_cached

def is_cached(name: str) -> bool
Check if an image is fully cached locally.

Parameters

name
str
required
Image name to check.
return
bool
Returns True if both kernel and rootfs files exist in the cache, False otherwise.

Example

manager = ImageManager()

if manager.is_cached("hello"):
    print("Image is ready to use")
else:
    print("Image needs to be downloaded")

ensure_image

def ensure_image(name: str) -> LocalImage
Ensure an image is available locally, downloading if necessary. If the image is already cached and passes SHA-256 verification, it is returned immediately. Otherwise, it is downloaded with automatic checksum verification.

Parameters

name
str
required
Image name from the registry.

Returns

return
LocalImage
A LocalImage object containing paths to the kernel and rootfs:
  • name (str): Image name
  • kernel_path (Path): Absolute path to the kernel binary
  • rootfs_path (Path): Absolute path to the root filesystem

Raises

  • ImageError: If the image is not in the registry, download fails, or checksum verification fails
  • ValueError: If the image name is empty

Example

from smolvm import ImageManager, VMConfig, SmolVM

manager = ImageManager()

# Download and cache the image (or use cached version)
image = manager.ensure_image("hello")

print(f"Image: {image.name}")
print(f"Kernel: {image.kernel_path}")
print(f"Rootfs: {image.rootfs_path}")

# Use with SmolVM
config = VMConfig(
    vm_id="hello-vm",
    kernel_path=image.kernel_path,
    rootfs_path=image.rootfs_path,
)

with SmolVM(config) as vm:
    vm.start()

Data Models

ImageSource

class ImageSource(BaseModel):
    name: str
    kernel_url: str
    kernel_sha256: str | None = None
    rootfs_url: str
    rootfs_sha256: str | None = None
Definition of a downloadable VM image.

Attributes

name
str
required
Human-readable image name.
kernel_url
str
required
URL to download the kernel binary.
kernel_sha256
str | None
Expected SHA-256 hex digest of the kernel. If None, verification is skipped.
rootfs_url
str
required
URL to download the root filesystem.
rootfs_sha256
str | None
Expected SHA-256 hex digest of the rootfs. If None, verification is skipped.

LocalImage

class LocalImage(BaseModel):
    name: str
    kernel_path: Path
    rootfs_path: Path
A locally-cached VM image ready for use.

Attributes

name
str
Image name.
kernel_path
Path
Absolute path to the kernel binary.
rootfs_path
Path
Absolute path to the root filesystem.

Built-in Images

The default registry includes these images:

hello

A minimal “hello world” Firecracker image from the official Firecracker quickstart.
manager = ImageManager()
image = manager.ensure_image("hello")

quickstart-x86_64

Firecracker quickstart Ubuntu image for x86_64 architecture.
manager = ImageManager()
image = manager.ensure_image("quickstart-x86_64")

Custom Image Registry

You can create a custom registry with your own images:
from smolvm import ImageManager, ImageSource

custom_registry = {
    "my-custom-image": ImageSource(
        name="my-custom-image",
        kernel_url="https://example.com/vmlinux.bin",
        kernel_sha256="abc123...",
        rootfs_url="https://example.com/rootfs.ext4",
        rootfs_sha256="def456...",
    ),
}

manager = ImageManager(registry=custom_registry)
image = manager.ensure_image("my-custom-image")
You can also combine the built-in registry with custom images:
from smolvm.images import BUILTIN_IMAGES

registry = dict(BUILTIN_IMAGES)
registry["my-image"] = ImageSource(...)

manager = ImageManager(registry=registry)

Download Security

The ImageManager implements secure download practices:
  1. Atomic Writes: Files are downloaded to a temporary location and atomically renamed into place, preventing partial downloads from corrupting the cache
  2. SHA-256 Verification: Downloaded files are verified against expected checksums before being used
  3. Cache Validation: Cached files are re-verified on access and re-downloaded if checksums don’t match
  4. Streaming Downloads: Large files are streamed in 8KB chunks to minimize memory usage
manager = ImageManager()

# This will:
# 1. Download kernel to temporary file
# 2. Verify SHA-256 checksum
# 3. Atomically rename to final location
# 4. Repeat for rootfs
image = manager.ensure_image("hello")

Cache Management

Images are cached under ~/.smolvm/images/<name>/ by default:
~/.smolvm/images/
├── hello/
│   ├── vmlinux.bin
│   └── rootfs.ext4
└── quickstart-x86_64/
    ├── vmlinux.bin
    └── rootfs.ext4
You can override the cache directory:
from pathlib import Path

# Use a custom cache directory
manager = ImageManager(cache_dir=Path("/var/cache/smolvm"))
The cache directory can be shared across multiple ImageManager instances. Images are identified by name, so make sure image names are unique if using a shared cache.

Complete Example

Here’s a complete example showing how to use ImageManager with the high-level VM API:
from smolvm import ImageManager, VM

# Initialize image manager
manager = ImageManager()

# List available images
print("Available images:")
for img_name in manager.list_available():
    cached = "✓" if manager.is_cached(img_name) else "✗"
    print(f"  [{cached}] {img_name}")

# Ensure image is downloaded and cached
image = manager.ensure_image("hello")

print(f"\nUsing image: {image.name}")
print(f"  Kernel: {image.kernel_path}")
print(f"  Rootfs: {image.rootfs_path}")

# Create and run VM
with VM(kernel_path=image.kernel_path, rootfs_path=image.rootfs_path) as vm:
    print(f"\nVM started with IP: {vm.get_ip()}")
    result = vm.run("echo 'Hello from SmolVM!'")
    print(f"Output: {result.stdout.strip()}")

Error Handling

Handle download and verification errors:
from smolvm import ImageManager
from smolvm.exceptions import ImageError

manager = ImageManager()

try:
    image = manager.ensure_image("nonexistent-image")
except ImageError as e:
    print(f"Failed to get image: {e}")
    # Output: Failed to get image: Unknown image: 'nonexistent-image'. 
    #         Available images: hello, quickstart-x86_64

try:
    image = manager.ensure_image("")  # Empty name
except ValueError as e:
    print(f"Invalid input: {e}")
    # Output: Invalid input: image name cannot be empty

Advanced: Bypassing SHA-256 Verification

For development or testing, you can create images without SHA-256 verification:
from smolvm import ImageManager, ImageSource

registry = {
    "dev-image": ImageSource(
        name="dev-image",
        kernel_url="http://localhost:8000/vmlinux.bin",
        kernel_sha256=None,  # Skip verification
        rootfs_url="http://localhost:8000/rootfs.ext4",
        rootfs_sha256=None,  # Skip verification
    ),
}

manager = ImageManager(registry=registry)
image = manager.ensure_image("dev-image")
Only skip SHA-256 verification for trusted sources or development purposes. Always use checksums for production images.

Build docs developers (and LLMs) love