Minimal rootless Linux sandbox written in Zig.
Use cases: build environments, agentic AI sessions, foundation for container runtimes.
Each run creates a fresh, isolated filesystem with its own user/mount/PID/UTS/network namespaces — no sudo required.
- Direct syscall access — calls Linux syscalls directly without libc, making
clone,mount,chrootstraightforward - No runtime — no GC, no VM. Produces a tiny static binary ideal for a minimal sandbox
- Cross-compilation — built-in support for targeting different architectures
- x86_64 architecture — seccomp filter is hardcoded for x86_64 syscall numbers
- Linux kernel with namespace support (kernel 5.11+ recommended)
- Rootless namespaces — user namespaces must be enabled (check with
sysctl kernel.unprivileged_userns_clone) - A statically-linked shell binary (e.g. busybox) for testing
For network_access and port_forwards:
ipcommand (from iproute2 package)iptables- Root or
CAP_NET_ADMINcapability
zbox uses seccomp-BPF with a deny list approach (similar to Docker's default profile):
- Default action: Allow all syscalls
- Blocked syscalls: Up to 44 dangerous syscalls are explicitly blocked (29 always, 15 network syscalls conditionally)
- Blocked categories:
- Kernel module loading (
init_module,finit_module,delete_module) - Kernel execution (
kexec_load,kexec_file_load) - Hardware access (
ioperm,iopl,syslog) - Memory manipulation (
mbind,set_mempolicy, etc.) - Network operations (
socket,connect, etc.) — only blocked whennetwork_accessis disabled; allowed when enabled - Device access (
mknod,mknodat) - Accounting (
acct) - System control (
reboot,swapon,swapoff) - Debugging (
ptrace,process_vm_readv, etc.)
- Kernel module loading (
- Architecture check: Only x86_64 syscalls are processed
- NO_NEW_PRIVS: Required before seccomp filter installation
- Namespace isolation: User, mount, PID, UTS, and network namespaces
- Filesystem isolation: chroot into container root
BusyBox combines tiny versions of common UNIX utilities (sh, ls, cat, echo, etc.) into a single ~1 MB static binary. zbox uses it as the default binary executed inside the sandbox for interactive testing.
- toybox — minimal tool suite (used by Android)
- sbase — suckless community tools
- Static builds of coreutils
zig buildzig build test./zig-out/bin/zbox --config config.json-c, --config <path>— Path to JSON config file (required)-h, --help— Show help--— Forward remaining arguments to the sandboxed binary
Configure via JSON file passed with -c/--config:
| Field | Type | Description |
|---|---|---|
name |
string | Sandbox identifier (used for cgroup name) |
binary |
string | Absolute path to executable inside sandbox |
root |
string | Absolute path to sandbox root directory |
cpu_cores |
u32 | Number of CPU cores to allow |
cpu_limit_percent |
u32 | CPU limit as percentage (1-100) |
memory_limit_mb |
u32 | Memory limit in megabytes |
network_access |
bool | Enable internet access from sandbox (default: false) |
port_forwards |
array | Port mappings for accessing services inside sandbox |
| Field | Type | Description |
|---|---|---|
host |
u16 | Port on host to listen on |
sandbox |
u16 | Port inside sandbox to forward to |
Example:
{
"name": "zbox-sandbox",
"root": "/tmp/zbox_root",
"binary": "/bin/busybox",
"cpu_cores": 2,
"cpu_limit_percent": 10,
"memory_limit_mb": 3,
"network_access": true,
"port_forwards": [
{ "host": 8080, "sandbox": 80 },
{ "host": 2222, "sandbox": 22 }
]
}When network_access: true is set:
- A veth pair is created connecting the sandbox to the host
- Sandbox gets IP
10.0.2.2/24 - Host gets IP
10.0.2.1/24 - NAT/masquerading is enabled allowing the sandbox to access the internet
- Port forwards allow services inside the sandbox to be accessed from the host
Requirements for network features:
ipcommand (iproute2 package)iptables- Root or
CAP_NET_ADMINcapability for network operations
zbox uses Linux cgroups v2 for CPU and memory limits and veth pairs for networking. These features require sudo (or CAP_NET_ADMIN) because cgroup files in /sys/fs/cgroup/ and network device management are root-only:
sudo ./zig-out/bin/zbox -c config.jsonWithout sudo, the sandbox runs but resource limits and network features are not applied:
- cgroups (CPU/memory limits) — the kernel does not allow unprivileged users to create or manage cgroups. This is a fundamental Linux limitation; all container tools (Docker, Podman, etc.) require root for resource limits.
- Network access and port forwarding — creating veth pairs, configuring IP addresses, and setting up iptables rules all require root or
CAP_NET_ADMIN. Without privileges,network_accessandport_forwardsare silently skipped.
The sandbox still provides full isolation via namespaces (user, mount, PID, UTS, network) and seccomp filtering, but CPU/memory constraints and networking require privileged access.
- User namespace with UID/GID mapping (rootless)
- Mount namespace
- UTS namespace isolation
- Filesystem isolation (chroot)
- PID namespace isolation
- Mounts (proc, tmpfs for /dev and /tmp)
- Execute target binary
- Copy configured binary into container
- Fresh filesystem per run
- Interactive shell (stdin/stdout)
- Network namespace isolation
- Syscall filtering (seccomp-BPF deny list)
- Resource limits (CPU, Memory via cgroups, requires sudo)
- Network access (NAT/masquerading)
- Port forwarding (veth + iptables)
- pivot_root (more secure than chroot)
- OCI compatibility (run container images)