Frequently asked questions and common issues with 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.
| Feature | BoxLite | Docker |
|---|---|---|
| Isolation | Hardware VM (KVM/Hypervisor.framework) | Container (namespaces/cgroups) |
| Daemon | No daemon required | Requires Docker daemon |
| Root | No root required | Typically needs root/sudo |
| Architecture | Embeddable library | Client-server architecture |
| Use Case | Embedded sandboxing, AI agents | Application deployment, CI/CD |
| Startup | ~1-2 seconds | ~100-500ms |
| Isolation Level | Separate kernel, hardware isolation | Shared 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 effectYes, 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/kvmis owned byroot:kvmwith mode660- Your user is not in the
kvmgroup
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).
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.0Yes. 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.
Apache License 2.0. Free for commercial and non-commercial use.
See LICENSE for details.
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 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 workloadsTypical: 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:latestvsubuntu:latest)
Yes, using persistent disks.
Ephemeral (default):
boxlite.BoxOptions() # Data lost when box is removedPersistent:
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 intactAlso:
- 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.py2. 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 arm64See Debugging Guide for comprehensive troubleshooting.
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:8080See Configuring Networking for details.
Not directly. Boxes are isolated from each other.
Alternatives:
-
Share data via volumes:
volumes=[("/host/shared", "/mnt/shared", "rw")]
-
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
-
External service:
- Both boxes connect to Redis/database on host or network
Common causes:
-
Insufficient resources:
# Increase limits boxlite.BoxOptions( cpus=4, # More CPUs memory_mib=4096, # More memory )
-
Disk I/O:
- Use ephemeral storage (faster than QCOW2)
- Check host disk speed:
dd if=/dev/zero of=test bs=1M count=1024
-
Too many boxes:
metrics = runtime.metrics() print(f"Active boxes: {metrics.active_boxes}") # Reduce concurrency or increase host resources
-
Image size:
- Use smaller images:
alpine:latest(5 MB) vsubuntu:latest(77 MB) - Check image size:
docker images
- Use smaller 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:
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
Causes:
- Network connectivity issues
- Invalid image name/tag
- Private image requires authentication
- 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 outputDebug checklist:
-
Check disk space:
df -h ~/.boxlite # Should have at least 1 GB free
-
Verify hypervisor:
# Linux ls -l /dev/kvm lsmod | grep kvm # macOS sw_vers | grep ProductVersion # Should be 12+ uname -m # Should be arm64
-
Check image:
docker pull <image> # Should succeed
-
Enable debug logging:
RUST_LOG=debug python script.py
-
Check permissions:
# Linux: Ensure user in kvm group groups | grep kvm # If not, add and relogin sudo usermod -aG kvm $USER
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 installedFix (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-restrictFix (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.confFix (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
)Causes:
- Command is waiting for input
- Long-running operation
- 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:
-
Check port is not in use:
lsof -i :8080 # Should be empty, or show boxlite process -
Verify configuration:
# Correct ports=[(8080, 80, "tcp")] # Wrong (swapped) # ports=[(80, 8080, "tcp")] # Don't do this
-
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")
-
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 ~/.boxlite2. /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/kvm3. Volume mounts:
# Ensure host path is accessible
chmod 755 /host/pathCause: Box exceeded memory limit.
Solutions:
-
Increase memory limit:
boxlite.BoxOptions( memory_mib=2048, # Increase from 512 to 2048 )
-
Check actual usage:
metrics = await box.metrics() print(f"Memory: {metrics.memory_usage_bytes / (1024**2):.2f} MB")
-
Optimize code:
- Reduce memory footprint of executed code
- Process data in chunks instead of loading all at once
- Clear variables when no longer needed
-
Use swap (Linux only, not recommended):
- Better to increase
memory_mib
- Better to increase
Cause: KVM module not loaded or not accessible.
Solutions:
-
Load KVM module:
sudo modprobe kvm kvm_intel # For Intel CPUs sudo modprobe kvm kvm_amd # For AMD CPUs # Verify lsmod | grep kvm
-
Check CPU support:
grep -E 'vmx|svm' /proc/cpuinfo # Should show vmx (Intel) or svm (AMD)
-
Enable in BIOS:
- Reboot and enter BIOS/UEFI
- Enable "Intel VT-x" or "AMD-V"
- Save and reboot
-
Add user to kvm group:
sudo usermod -aG kvm $USER # Logout and login
Cause: Running on unsupported macOS version or architecture.
Solutions:
-
Check macOS version:
sw_vers # ProductVersion should be 12.0 or higher -
Check architecture:
uname -m # Should output: arm64 (Apple Silicon) -
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
Documentation:
- Getting Started - Quick onboarding
- Python SDK README - Complete Python API
- How-to Guides - Practical guides
- Reference Documentation - API and configuration reference
- Architecture Documentation - How BoxLite works
Community:
- GitHub Issues - Bug reports and feature requests
- GitHub Discussions - Questions and community support
Before posting:
- Check this FAQ
- Search existing issues/discussions
- Enable debug logging:
RUST_LOG=debug - Include BoxLite version, platform, and minimal reproduction
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.log5. 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
enhancementlabel
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)
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:pythonAreas to contribute:
- Bug fixes
- Documentation improvements
- New examples
- SDK improvements (Python, Node.js, C)
- Performance optimizations