Bubblewrap Sandboxing Guide: Lightweight Process Isolation on Linux

When you need to run untrusted code or isolate applications without spinning up full containers, bubblewrap offers an elegant solution. Used by Flatpak and other major projects, bubblewrap provides unprivileged sandboxing using Linux kernel namespaces. This guide covers everything you need to know to start sandboxing processes effectively.
What is Bubblewrap?
Bubblewrap (bwrap) is a low-level sandboxing tool that creates isolated environments using Linux kernel features like namespaces and seccomp filters. Unlike Docker or systemd-nspawn, bubblewrap is designed for unprivileged users and focuses on minimal attack surface.
Key characteristics:
- Unprivileged operation: No root access required (uses user namespaces)
- Minimal footprint: Small, auditable codebase
- Flexible isolation: Fine-grained control over what the sandbox can access
- No daemon: Runs as a simple command wrapper
Why Use Bubblewrap Over Docker?
| Feature | Bubblewrap | Docker |
|---|---|---|
| Root required | No | Yes (or rootless mode) |
| Daemon process | No | Yes |
| Image management | No | Yes |
| Startup time | Instant | Seconds |
| Resource overhead | Minimal | Higher |
| Use case | Process isolation | Full containerization |
Choose bubblewrap when you need:
- Quick process isolation without infrastructure
- To sandbox a single command or script
- Minimal overhead for development workflows
- Unprivileged sandboxing on shared systems
Installation
Ubuntu/Debian:
sudo apt install bubblewrap
Fedora/RHEL/CentOS:
sudo dnf install bubblewrap
Arch Linux:
sudo pacman -S bubblewrap
From source:
git clone https://github.com/containers/bubblewrap.git
cd bubblewrap
meson _builddir
meson compile -C _builddir
sudo meson install -C _builddir
Verify installation:
bwrap --version
Basic Usage
The simplest bubblewrap invocation creates an isolated environment with access to system binaries:
bwrap \
--ro-bind /usr /usr \
--symlink usr/lib64 /lib64 \
--symlink usr/bin /bin \
--proc /proc \
--dev /dev \
--unshare-pid \
bash
This creates a sandbox where:
/usris mounted read-only/procand/devare properly set up- PID namespace is isolated (processes inside can't see host processes)
- A bash shell runs inside the sandbox
Understanding Bubblewrap Options
Filesystem Mounts
--ro-bind /source /dest # Read-only bind mount
--bind /source /dest # Read-write bind mount
--dev-bind /source /dest # Bind mount preserving device nodes
--tmpfs /path # Mount tmpfs at path
--dir /path # Create empty directory
--symlink target /link # Create symbolic link
--file FD /path # Create file from file descriptor
Namespace Isolation
--unshare-user # Create new user namespace
--unshare-pid # Create new PID namespace
--unshare-net # Create new network namespace (no network)
--unshare-ipc # Create new IPC namespace
--unshare-uts # Create new UTS namespace (hostname)
--unshare-cgroup # Create new cgroup namespace
Security Options
--new-session # Create new terminal session
--die-with-parent # Kill sandbox when parent dies
--as-pid-1 # Run as PID 1 inside sandbox
--cap-drop ALL # Drop all capabilities
--seccomp FD # Apply seccomp filter from file descriptor
Practical Examples
Sandbox an Untrusted Script
Run a downloaded script with minimal filesystem access:
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--symlink usr/bin /bin \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--unshare-all \
--new-session \
--die-with-parent \
/bin/bash ./untrusted-script.sh
Isolated Development Environment
Create a sandbox with access to your project directory:
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/ssl /etc/ssl \
--symlink usr/bin /bin \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--bind "$PWD" "$PWD" \
--chdir "$PWD" \
--unshare-pid \
--new-session \
bash
This gives you:
- Read-only access to system files
- Network access (DNS resolution works)
- Read-write access to current directory only
- Isolated PID namespace
Network-Isolated Sandbox
For complete network isolation:
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--symlink usr/bin /bin \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--unshare-net \
--new-session \
bash
Inside this sandbox, only loopback (127.0.0.1) is available. Perfect for testing applications that shouldn't have network access.
Sandbox with Custom Home Directory
Isolate an application from your real home directory:
mkdir -p /tmp/fake-home
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--symlink usr/bin /bin \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--bind /tmp/fake-home "$HOME" \
--setenv HOME "$HOME" \
--unshare-pid \
--new-session \
bash
Sandboxing GUI Applications
For GUI apps, you need to expose X11 or Wayland sockets:
# X11
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--symlink usr/bin /bin \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--ro-bind /etc/fonts /etc/fonts \
--ro-bind ~/.Xauthority ~/.Xauthority \
--bind /tmp/.X11-unix /tmp/.X11-unix \
--setenv DISPLAY "$DISPLAY" \
--new-session \
firefox
Creating Reusable Sandbox Scripts
For complex sandboxes, create wrapper scripts:
#!/bin/bash
# sandbox-dev.sh - Development sandbox
exec bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/ssl /etc/ssl \
--ro-bind /etc/hosts /etc/hosts \
--ro-bind /etc/localtime /etc/localtime \
--ro-bind /etc/passwd /etc/passwd \
--ro-bind /etc/group /etc/group \
--symlink usr/bin /bin \
--symlink usr/sbin /sbin \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--tmpfs /run \
--bind "$PWD" "$PWD" \
--chdir "$PWD" \
--unshare-pid \
--unshare-uts \
--hostname sandbox \
--new-session \
--die-with-parent \
"$@"
Usage:
chmod +x sandbox-dev.sh
./sandbox-dev.sh bash
./sandbox-dev.sh python3 script.py
./sandbox-dev.sh npm install
Debugging Sandbox Issues
When your sandboxed application fails, use strace to identify missing dependencies:
strace -e trace=open,openat,stat,access -f bwrap \
--ro-bind /usr /usr \
... \
your-command 2>&1 | grep -E "ENOENT|EACCES"
This shows which files the application tried to access but couldn't find.
Common issues:
- DNS not working: Bind mount
/etc/resolv.conf - SSL certificates missing: Bind mount
/etc/ssland/usr/share/ca-certificates - Timezone wrong: Bind mount
/etc/localtime - Dynamic libraries missing: Check
ldd your-binaryand bind required paths
Security Considerations
Bubblewrap provides defense in depth, but understand its limitations:
What bubblewrap protects against:
- Filesystem access outside bind mounts
- Process visibility (with PID namespace)
- Network access (with network namespace)
- IPC between sandboxed and host processes
What bubblewrap does NOT protect against:
- Kernel exploits (sandbox shares the kernel)
- Side-channel attacks
- Data exfiltration through allowed network access
- Covert channels through shared resources
For higher security requirements, combine bubblewrap with:
- Seccomp filters to restrict syscalls
- SELinux/AppArmor policies
- Resource limits via cgroups
- Network filtering with iptables/nftables
Comparison with Alternatives
Bubblewrap vs Firejail
Firejail includes many pre-built profiles for common applications but has a larger attack surface. Bubblewrap is more minimal and requires manual configuration.
Bubblewrap vs systemd-nspawn
systemd-nspawn is designed for full system containers and requires root. Bubblewrap targets lightweight, unprivileged process sandboxing.
Bubblewrap vs Docker
Docker provides complete container lifecycle management with images, networking, and orchestration. Bubblewrap is simpler, faster, and runs without a daemon.
Real-World Use Cases
- Flatpak: Uses bubblewrap to sandbox desktop applications
- GNOME: Sandboxes document viewers and other apps
- CI/CD: Isolate build processes without full containers
- Development: Run untrusted code or AI coding agents safely
- Testing: Isolate test environments from the host system
Conclusion
Bubblewrap fills an important gap between running processes directly and full containerization. Its minimal design, unprivileged operation, and fine-grained control make it ideal for lightweight sandboxing scenarios. Whether you're isolating development environments, running untrusted scripts, or building secure application launchers, bubblewrap provides the tools you need. For comprehensive infrastructure monitoring and incident management when running sandboxed workloads in production, consider Akmatori, an open-source AI agent platform that helps SRE teams respond to infrastructure incidents faster.
FAQ
Can bubblewrap run as a non-root user?
- Yes, that's its primary design goal. It uses user namespaces to provide sandboxing without root privileges.
Is bubblewrap as secure as a VM?
- No. Bubblewrap shares the host kernel, so kernel exploits could escape the sandbox. VMs provide stronger isolation.
Can I use bubblewrap on systems without user namespace support?
- Yes, bubblewrap can be installed setuid root as a fallback, though this is less common on modern systems.
How do I persist changes made inside the sandbox?
- Use
--bindinstead of--ro-bindfor directories where you want to save changes. Changes to--bindmounts persist to the host.
- Use
Can bubblewrap limit CPU or memory usage?
- Not directly. Use cgroups (via
systemd-runor manual configuration) alongside bubblewrap for resource limits.
- Not directly. Use cgroups (via
