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

General Questions

BoxLite is a local-first micro-VM sandbox for AI agents — think “SQLite for sandboxing.” It’s a library you embed directly in your application, no daemon or cloud account required. Unlike ephemeral sandboxes that destroy state after each execution, BoxLite Boxes are stateful workspaces — install packages, create files, build up environment state, then come back later and pick up where you left off.
FeatureBoxLiteDocker
IsolationHardware VM (KVM/Hypervisor.framework)Container (namespaces/cgroups)
DaemonNo daemon requiredRequires Docker daemon
RootNo root requiredTypically needs root/sudo
ArchitectureLocal-first libraryClient-server architecture
Default stateStateful by default — full filesystem persists across stop/restartEphemeral by default — needs explicit volumes to persist data
Use CaseAI agent sandboxes, secure code executionApplication 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 is stable and used in production.Production considerations:
  • Stable API
  • Hardware-level isolation
  • Resource limits enforced
  • Error handling robust
  • Monitor resource usage
  • Test at expected scale
  • Configure appropriate limits
See the AI Agent Integration guide for production configuration, concurrency, and security patterns.
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: 2048 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 (default)
boxlite.BoxOptions(memory_mib=2048)  # Default

# Heavy box
boxlite.BoxOptions(memory_mib=4096)  # 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 = box.info()
print(f"State: {info.state}")
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 macOS Sandbox 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 the Architecture: Networking & Storage page for details.
Not directly. Boxes are isolated from each other.Alternatives:
  1. Share data via volumes:
    volumes=[("/host/shared", "/mnt/shared", False)]  # False = read-write
    
  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 = await runtime.metrics()
    print(f"Running boxes: {metrics.running}")
    # 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.send_input(b"input\n")
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() is available on the low-level Box object (via runtime.create())
    metrics = await box.metrics()
    print(f"Memory: {metrics.mem / (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-ai/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