Skip to main content
Frequently asked questions and common issues with BoxLite.

General Questions

BoxLite is an embeddable virtual machine runtime for secure, isolated code execution. Think of it as “SQLite for sandboxing” — a lightweight library you can embed directly in your application without requiring a daemon or root privileges.
FeatureBoxLiteDocker
IsolationHardware VM (KVM/Hypervisor.framework)Container (namespaces/cgroups)
DaemonNo daemon requiredRequires Docker daemon
RootNo root requiredTypically needs root/sudo
ArchitectureEmbeddable libraryClient-server architecture
Use CaseEmbedded sandboxing, AI agentsApplication deployment, CI/CD
Startup~1-2 seconds~100-500ms
Isolation LevelSeparate kernel, hardware isolationShared kernel
When to use BoxLite:
  • AI agents that need full execution freedom
  • Untrusted code execution
  • Hardware-level isolation required
  • Embedded in applications (no daemon)
When to use Docker:
  • Application deployment
  • Development environments
  • CI/CD pipelines
  • Established Docker workflows
No. BoxLite doesn’t require root privileges.macOS: Hypervisor.framework is available to all users (no special permissions).Linux: Only requires access to /dev/kvm, which can be granted through group membership:
sudo usermod -aG kvm $USER
# Logout and login for changes to take effect
Yes, through WSL2 (Windows Subsystem for Linux).Requirements:
  • Windows 10 version 2004+ or Windows 11
  • WSL2 with a Linux distribution (Ubuntu recommended)
  • KVM support enabled in WSL2
Setup:
# Inside WSL2, add your user to the kvm group
sudo usermod -aG kvm $USER

# Apply the new group membership (pick one):
newgrp kvm
# OR restart WSL from Windows PowerShell:
# wsl.exe --shutdown

# Verify KVM access
python3 -c "open('/dev/kvm','rb').close(); print('kvm ok')"
Common Issue: If you see “Timeout waiting for guest ready (30s)” errors, your shell cannot open /dev/kvm. This happens when:
  • /dev/kvm is owned by root:kvm with mode 660
  • Your user is not in the kvm group
Run sudo usermod -aG kvm $USER and restart WSL with wsl.exe --shutdown.
Native Windows (without WSL2) is not supported. BoxLite requires KVM (Linux) or Hypervisor.framework (macOS).
Python 3.10 or later.Check your version:
python --version  # Should be 3.10+
Upgrade if needed:
# macOS (Homebrew)
brew install [email protected]

# Ubuntu/Debian
sudo apt install python3.11

# Or use pyenv
pyenv install 3.11.0
Yes. BoxLite v0.4.4 is stable and used in production.Production considerations:
  • Stable API (v0.4.x series)
  • Hardware-level isolation
  • Resource limits enforced
  • Error handling robust
  • Monitor resource usage
  • Test at expected scale
  • Configure appropriate limits
See Deployment Patterns for the production checklist.
Apache License 2.0. Free for commercial and non-commercial use.

Technical Questions

macOS: Hypervisor.framework (built into macOS 12+)Linux: KVM (Kernel-based Virtual Machine)How it works:
  • BoxLite uses libkrun as the hypervisor abstraction
  • libkrun provides a unified API over Hypervisor.framework (macOS) and KVM (Linux)
  • Each box runs as a separate microVM with its own kernel
Minimum: 128 MiB (configured via memory_mib)Default: 512 MiBRange: 128 MiB to 64 GiB (65536 MiB)Overhead:
  • VM overhead: ~50-100 MB per box
  • Guest kernel: ~20-40 MB
  • Container: Depends on image
Example:
# Lightweight box
boxlite.BoxOptions(memory_mib=128)  # Minimum for Alpine

# Standard box
boxlite.BoxOptions(memory_mib=512)  # Default, good for Python

# Heavy box
boxlite.BoxOptions(memory_mib=2048)  # For complex workloads
Typical: 1-2 secondsFactors:
  • Image size (cached vs first pull)
  • Disk I/O speed
  • Available resources
First run: 5-30 seconds (includes image pull)Subsequent runs: 1-2 seconds (image cached)Optimization:
  • Pre-pull images: runtime.create(boxlite.BoxOptions(image="..."))
  • Reuse boxes instead of creating new ones
  • Use smaller base images (alpine:latest vs ubuntu:latest)
Yes, using persistent disks.Ephemeral (default):
boxlite.BoxOptions()  # Data lost when box is removed
Persistent:
boxlite.BoxOptions(
    disk_size_gb=10  # 10 GB persistent QCOW2 disk
)

# Data survives stop/restart
await box.stop()
# ... later ...
box = runtime.get(box_id)  # Disk intact
Also:
  • Use volume mounts for host-box data sharing
  • Read-write volumes persist changes to host filesystem
1. Enable debug logging:
RUST_LOG=debug python script.py
2. Check box status:
info = await box.info()
print(f"Status: {info.status}")

metrics = await box.metrics()
print(f"Memory: {metrics.memory_usage_bytes / (1024**2):.2f} MB")
3. Inspect filesystem:
# Check disk space
df -h ~/.boxlite

# Check box data
ls -la ~/.boxlite/boxes/

# Check image cache
ls -la ~/.boxlite/images/
4. Check hypervisor:
# Linux
ls -l /dev/kvm
lsmod | grep kvm

# macOS
sw_vers  # Should be 12+
uname -m  # Should be arm64
See the Debugging Guide for comprehensive troubleshooting.

Networking

Yes. All boxes have full internet access by default.Outbound connections:
  • HTTP/HTTPS requests
  • DNS resolution
  • Any protocol (TCP/UDP)
Example:
async with boxlite.SimpleBox(image="alpine:latest") as box:
    # Test internet access
    result = await box.exec("wget", "-O-", "https://api.github.com/zen")
    print(result.stdout)
Use the ports parameter for port forwarding:
boxlite.BoxOptions(
    ports=[
        (8080, 80, "tcp"),      # Host 8080 -> Guest 80
        (5432, 5432, "tcp"),    # PostgreSQL
        (53, 53, "udp"),        # DNS (UDP)
    ]
)
Access from host:
curl http://localhost:8080
See Configuring Networking for details.
Not directly. Boxes are isolated from each other.Alternatives:
  1. Share data via volumes:
    volumes=[("/host/shared", "/mnt/shared", "rw")]
    
  2. Use host network: Box A exposes a port, and Box B connects to host.docker.internal:port (or localhost on Linux).
  3. External service: Both boxes connect to Redis/database on host or network.

Performance

Common causes:
  1. Insufficient resources:
    # Increase limits
    boxlite.BoxOptions(
        cpus=4,          # More CPUs
        memory_mib=4096, # More memory
    )
    
  2. Disk I/O:
    • Use ephemeral storage (faster than QCOW2)
    • Check host disk speed: dd if=/dev/zero of=test bs=1M count=1024
  3. Too many boxes:
    metrics = runtime.metrics()
    print(f"Active boxes: {metrics.active_boxes}")
    # Reduce concurrency or increase host resources
    
  4. Image size:
    • Use smaller images: alpine:latest (5 MB) vs ubuntu:latest (77 MB)
    • Check image size: docker images
It depends on host resources.Resource calculation:
Total Memory = (boxes * memory_mib) + overhead
Total CPUs = boxes * cpus (can oversubscribe)

Example:
100 boxes * 512 MiB = 51.2 GB memory needed
100 boxes * 1 CPU = 100 CPUs (oversubscribed, shares-based)
Best practices:
  • Start small (10 boxes) and scale up
  • Monitor metrics: runtime.metrics().active_boxes
  • Use resource pooling (reuse boxes)
  • Test at expected load
Example:
import asyncio

async def run_100_boxes():
    tasks = []
    for i in range(100):
        task = run_box(i)
        tasks.append(task)

    results = await asyncio.gather(*tasks)
No hard limit, but practical constraints apply:Memory:
  • Range: 128 MiB to 64 GiB (65536 MiB)
  • Limited by host RAM
Disk:
  • Range: 1 GB to 1 TB
  • Limited by host storage
CPUs:
  • Range: 1 to host CPU count
  • Can oversubscribe (shares-based)
Tested configurations:
  • 64 GiB memory
  • 1 TB disk
  • 16 CPUs

Troubleshooting

Causes:
  1. Network connectivity issues
  2. Invalid image name/tag
  3. Private image requires authentication
  4. Registry not reachable
Solutions:
# Test with Docker first
docker pull <image>

# Check network
ping registry-1.docker.io

# For private images, authenticate
docker login

# Check image name format
# Correct: "python:3.11-slim"
# Wrong: "python/3.11-slim"

# Clear cache if corrupted
rm -rf ~/.boxlite/images/*
Debug:
RUST_LOG=debug python script.py
# Look for image-related errors in output
Debug checklist:
  1. Check disk space:
    df -h ~/.boxlite
    # Should have at least 1 GB free
    
  2. Verify hypervisor:
    # Linux
    ls -l /dev/kvm
    lsmod | grep kvm
    
    # macOS
    sw_vers | grep ProductVersion  # Should be 12+
    uname -m  # Should be arm64
    
  3. Check image:
    docker pull <image>
    # Should succeed
    
  4. Enable debug logging:
    RUST_LOG=debug python script.py
    
  5. Check permissions:
    # Linux: Ensure user in kvm group
    groups | grep kvm
    
    # If not, add and relogin
    sudo usermod -aG kvm $USER
    
This is a known issue specific to Ubuntu 24.04. Ubuntu 25.04+ ships the required AppArmor profile by default.
Symptom: Box creation fails with “Timeout waiting for guest ready (30s)” or “VM subprocess exited before guest became ready” on Ubuntu 24.04. Works with sudo or on Ubuntu 25.04+.Root Cause: Ubuntu 24.04 restricts unprivileged user namespaces via AppArmor (kernel.apparmor_restrict_unprivileged_userns=1) but does not ship the bwrap-userns-restrict profile that Ubuntu 25.04+ includes. bwrap (bubblewrap) needs user namespaces for sandbox isolation.Diagnosis:
# Check for AppArmor denials
dmesg | grep apparmor
# Look for: apparmor="DENIED" ... comm="bwrap" capability=8

# Check if bwrap profile exists
aa-status | grep bwrap
# Should show "bwrap-userns-restrict" if profile is installed
Fix (Option A — targeted, recommended):Install the bwrap AppArmor profile that Ubuntu 25.04+ ships. Create the file /etc/apparmor.d/bwrap-userns-restrict with the following content, then reload:
sudo tee /etc/apparmor.d/bwrap-userns-restrict << 'PROFILE'
abi <abi/4.0>,

include <tunables/global>

profile bwrap /usr/bin/bwrap flags=(attach_disconnected,mediate_deleted) {
  allow capability,
  allow file rwlkm /{**,},
  allow network,
  allow unix,
  allow ptrace,
  allow signal,
  allow mqueue,
  allow io_uring,
  allow userns,
  allow mount,
  allow umount,
  allow pivot_root,
  allow dbus,
  allow pix /** -> &bwrap//&unpriv_bwrap,
  include if exists <local/bwrap-userns-restrict>
}

profile unpriv_bwrap flags=(attach_disconnected,mediate_deleted) {
  allow file rwlkm /{**,},
  allow network,
  allow unix,
  allow ptrace,
  allow signal,
  allow mqueue,
  allow io_uring,
  allow userns,
  allow mount,
  allow umount,
  allow pivot_root,
  allow dbus,
  allow pix /** -> &unpriv_bwrap,
  audit deny capability,
  include if exists <local/unpriv_bwrap>
}
PROFILE

sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict
Fix (Option B — quick, less secure):Disable the restriction globally:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

# To persist across reboots:
echo "kernel.apparmor_restrict_unprivileged_userns=0" | \
  sudo tee /etc/sysctl.d/99-boxlite-userns.conf
Fix (Option C — disable jailer):If you don’t need sandbox isolation (e.g., development environment), disable the jailer:
from boxlite.boxlite import SecurityOptions

boxlite.BoxOptions(
    security=SecurityOptions(jailer_enabled=False),
    # ... other options
)
Causes:
  1. Command is waiting for input
  2. Long-running operation
  3. Deadlock or infinite loop
Solutions:
import asyncio

# Add timeout
async def execute_with_timeout():
    execution = await box.exec("command")

    try:
        result = await asyncio.wait_for(
            execution.wait(),
            timeout=30  # 30 second timeout
        )
        return result
    except asyncio.TimeoutError:
        await execution.kill()
        print("Command timed out")
Check if command needs input:
# Provide stdin if needed
execution = await box.exec("command")
stdin = execution.stdin()
await stdin.write("input\n")
await stdin.close()
Debug steps:
  1. Check port is not in use:
    lsof -i :8080
    # Should be empty, or show boxlite process
    
  2. Verify configuration:
    # Correct
    ports=[(8080, 80, "tcp")]
    
    # Wrong (swapped)
    # ports=[(80, 8080, "tcp")]  # Don't do this
    
  3. Test from inside box:
    # Start server in box
    await box.exec("python", "-m", "http.server", "80", background=True)
    
    # Test from host
    import requests
    response = requests.get("http://localhost:8080")
    
  4. Check gvproxy:
    ps aux | grep gvproxy
    # Should show gvproxy process
    
    ls ~/.boxlite/gvproxy/
    # Should contain gvproxy binary
    
Common scenarios:1. ~/.boxlite directory:
chmod 755 ~/.boxlite
chown -R $USER ~/.boxlite
2. /dev/kvm (Linux):
# Check permissions
ls -l /dev/kvm
# Should be: crw-rw---- 1 root kvm

# Add user to kvm group
sudo usermod -aG kvm $USER
# Logout and login required

# Or temporarily (not recommended)
sudo chmod 666 /dev/kvm
3. Volume mounts:
# Ensure host path is accessible
chmod 755 /host/path
Cause: Box exceeded memory limit.Solutions:
  1. Increase memory limit:
    boxlite.BoxOptions(
        memory_mib=2048,  # Increase from 512 to 2048
    )
    
  2. Check actual usage:
    metrics = await box.metrics()
    print(f"Memory: {metrics.memory_usage_bytes / (1024**2):.2f} MB")
    
  3. Optimize code:
    • Reduce memory footprint of executed code
    • Process data in chunks instead of loading all at once
    • Clear variables when no longer needed
Cause: KVM module not loaded or not accessible.Solutions:
  1. Load KVM module:
    sudo modprobe kvm kvm_intel  # For Intel CPUs
    sudo modprobe kvm kvm_amd    # For AMD CPUs
    
    # Verify
    lsmod | grep kvm
    
  2. Check CPU support:
    grep -E 'vmx|svm' /proc/cpuinfo
    # Should show vmx (Intel) or svm (AMD)
    
  3. Enable in BIOS:
    • Reboot and enter BIOS/UEFI
    • Enable “Intel VT-x” or “AMD-V”
    • Save and reboot
  4. Add user to kvm group:
    sudo usermod -aG kvm $USER
    # Logout and login
    
Cause: Running on unsupported macOS version or architecture.Solutions:
  1. Check macOS version:
    sw_vers
    # ProductVersion should be 12.0 or higher
    
  2. Check architecture:
    uname -m
    # Should output: arm64 (Apple Silicon)
    
  3. Upgrade if needed:
    • BoxLite requires macOS 12+ (Monterey or later)
    • Apple Silicon (M1, M2, M3, M4) only
    • Intel Macs are not supported
If you have an Intel Mac, consider using a Linux VM, deploying to cloud (AWS, GCP, Azure), or using a cloud-based sandboxing service.

Getting Help

Documentation:Community:Before posting:
  1. Check this FAQ
  2. Search existing issues/discussions
  3. Enable debug logging: RUST_LOG=debug
  4. Include BoxLite version, platform, and minimal reproduction
1. Search existing issues: GitHub Issues2. Gather information:
  • BoxLite version: python -c "import boxlite; print(boxlite.__version__)"
  • Platform: uname -a
  • Python version: python --version
  • Error message and stack trace
3. Minimal reproduction:
import asyncio
import boxlite

async def reproduce():
    # Minimal code that reproduces the issue
    async with boxlite.SimpleBox(image="python:slim") as box:
        result = await box.exec("command")

asyncio.run(reproduce())
4. Debug logs:
RUST_LOG=debug python reproduce.py 2>&1 | tee debug.log
5. Create issue:
  • Use bug report template
  • Include all gathered information
  • Attach debug logs if relevant
  • Be specific and clear
  1. Check roadmap: Review GitHub Issues with the enhancement label
  2. Search for similar requests: May already be planned or discussed
  3. Create feature request: Use the feature request template, describe your use case, provide examples of desired API/behavior, and explain benefits to other users
  4. Participate in discussion: Respond to questions, refine the proposal based on feedback, and consider implementing it yourself
Quick start:
git clone https://github.com/boxlite-labs/boxlite.git
cd boxlite
git submodule update --init --recursive
make setup
make dev:python
Areas to contribute:
  • Bug fixes
  • Documentation improvements
  • New examples
  • SDK improvements (Python, Node.js, C)
  • Performance optimizations