FAQ & Troubleshooting

July 28, 2026 · View on GitHub

Frequently asked questions and common issues with BoxLite.

General Questions

What is BoxLite?

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.

What's the difference between BoxLite and Docker?

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

Do I need root or sudo?

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

Can I use BoxLite on Windows?

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.

Note: Native Windows (without WSL2) is not supported. BoxLite requires KVM (Linux) or Hypervisor.framework (macOS).

What Python versions are supported?

Python 3.10 or later.

Check your version:

python --version  # Should be 3.10+

Upgrade if needed:

# macOS (Homebrew)
brew install python@3.11

# Ubuntu/Debian
sudo apt install python3.11

# Or use pyenv
pyenv install 3.11.0

Is BoxLite production-ready?

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 Deployment Patterns for production checklist.

What's the license?

Apache License 2.0. Free for commercial and non-commercial use.

See LICENSE for details.

Technical Questions

What hypervisor does BoxLite use?

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

How much memory does each box use?

Minimum: 128 MiB (configured via memory_mib)

Default: 512 MiB

Range: 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

What's the box startup time?

Typical: 1-2 seconds

Factors:

  • 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)

Can I persist data between boxes?

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

How do I debug BoxLite issues?

1. Enable debug logging:

RUST_LOG=debug python script.py

2. Check box status:

info = await box.info()
print(f"Status: {info.state.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 Debugging Guide for comprehensive troubleshooting.

Networking

Does BoxLite support internet access?

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)

How do I expose ports from a box?

Use the ports parameter for port forwarding:

boxlite.BoxOptions(
    ports=[
        (8080, 80, "tcp"),      # Host 8080 → Guest 80
        (5432, 5432, "tcp"),    # PostgreSQL
        {"guest_port": 3000},   # OS-selected host port
    ]
)

Access from host:

curl http://localhost:8080

Port publication is local-only and TCP-only. It creates a listener that ordinary host applications can use for repeated connections. Remote runtimes reject ports; use box.network.tunnel(port) for portable SDK access. Each tunnel handle carries one connection. Image EXPOSE declarations do not publish host ports.

See Configuring Networking for details.

Can boxes communicate with each other?

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 port
    • Box B connects to host.boxlite.internal:port
    • This bypasses allow_net; if networking is enabled, host loopback services are reachable from inside the box
  3. External service:

    • Both boxes connect to Redis/database on host or network

Performance

Why is my box slow?

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"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

Can I run 100 boxes concurrently?

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: (await 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)

What's the maximum box size?

No hard limit, but practical constraints:

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

"Image pull failed" error

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

"Box fails to start" error

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
    

Ubuntu 24.04: "Timeout waiting for guest ready" / Box only starts with sudo

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, AdvancedBoxOptions

boxlite.BoxOptions(
    advanced=AdvancedBoxOptions(security=SecurityOptions.development()),
    # ... other options
)

"Command hangs" or "Execution timeout"

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()

"Port forward not working"

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
    

"Permission denied" errors

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

"Out of memory" / "Box killed"

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
  4. Use swap (Linux only, not recommended):

    • Better to increase memory_mib

"KVM not available" (Linux)

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
    

"Hypervisor.framework not available" (macOS)

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

Note: If you have an Intel Mac, consider:

  • Using a Linux VM
  • Deploying to cloud (AWS, GCP, Azure)
  • Using a cloud-based sandboxing service

Getting Help

Where can I get 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

How do I report a bug?

1. Search existing issues: GitHub Issues

2. 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

How do I request a feature?

1. Check roadmap:

2. Search for similar requests:

  • May already be planned or discussed

3. Create feature request:

  • Use feature request template
  • Describe use case (why you need it)
  • Provide examples of desired API/behavior
  • Explain benefits to other users

4. Participate in discussion:

  • Respond to questions
  • Refine proposal based on feedback
  • Consider implementing it yourself (see CONTRIBUTING.md)

How do I contribute?

See CONTRIBUTING.md for:

  • Development setup
  • Running tests
  • Code style guidelines
  • Pull request process

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