Self-hosted service
containers/bubblewrap avatar
containers/bubblewrap

bubblewrap: the unprivileged sandbox builder behind Flatpak

Low-level unprivileged sandboxing tool used by Flatpak and similar projects

8,869 stars389 forksCNOASSERTION

At a glance

What is it?
bubblewrap is a C tool that builds a sandbox with user namespaces and lets the caller decide every mount and namespace flag. It is a building block, not a finished sandbox, and that distinction decides whether it fits your project.
Who is it for?
Adopt bubblewrap if you are writing a launcher or runtime that needs to assemble a filesystem view and namespace set for an unprivileged process, and you are prepared to own the security policy yourself. Do not adopt it as a drop-in sandbox for untrusted code if you cannot review the argument list, because the README states the level of protection is entirely determined by the arguments passed.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem bubblewrap solves: container features for users who are not root

Tools like systemd-nspawn and docker are built for administrators and orchestration systems. The README is blunt about why they cannot simply be handed to a normal user: it says it is trivial to turn such access into a fully privileged root shell on the host. That is the gap bubblewrap fills. It uses Linux user namespaces so an unprivileged account can get container-like isolation without being granted root-equivalent powers first.

The audience is narrow and technical. The README lists Flatpak, rpm-ostree unprivileged and bwrap-oci as current consumers, and says the project would like to see the same capability in Kubernetes and OpenShift clusters for interactive debugging. If you are an application developer looking for a sandbox you can switch on with one flag, this is not aimed at you. If you are writing the layer that decides what a sandboxed process can see, it is.

One historical detail matters for anyone reading older guides: bubblewrap once had a setuid mode for systems without unprivileged user namespaces, and the README states that this has been removed. Advice that assumes a setuid bwrap binary is out of date.

How bwrap builds a sandbox from an empty tmpfs

The mechanism is stated plainly in the README: bubblewrap creates a new, completely empty mount namespace whose root is a tmpfs that is invisible from the host and is cleaned up when the last process exits. Nothing is visible inside unless you ask for it. Every --bind, --ro-bind and --symlink argument adds one piece of the filesystem tree, and the resulting view is what the sandboxed program sees.

On top of that base, the caller selects kernel namespaces. User namespaces hide all but the current uid and gid, and can remap them. IPC namespaces give the sandbox its own SysV shared memory and semaphores. PID namespaces hide outside processes, and the README notes that bubblewrap runs a trivial pid1 inside the container to reap children, which it frames as avoiding the Docker pid 1 problem. Network namespaces leave only a loopback device. UTS namespaces give the sandbox its own hostname. Seccomp filters can restrict which syscalls are allowed.

The design consequence is that bubblewrap has no opinion. The README says it is not a complete, ready-made sandbox with a specific security policy, and that whatever constructs the command line, often a larger framework, is responsible for defining its own security model. Directories mounted into the sandbox are mounted nodev by default and can be made read-only, but that is a default, not a policy.

Installing bubblewrap and running a first bwrap command

The README says bubblewrap is available in the package repositories of most Linux distributions and can be installed from there, so on a Debian or Ubuntu system the package name is the shortest path. If you need to build from source, the README gives a meson workflow.

bash
meson setup _builddir
meson compile -C _builddir
meson test -C _builddir
meson install -C _builddir

After installation the binary is bwrap. The README offers a trimmed version of a larger demo script in demos/bubblewrap-shell.sh, which runs a new shell reusing the host's /usr. It is presented as an incomplete illustration rather than a safe configuration.

bash
bwrap \
    --ro-bind /usr /usr \
    --symlink usr/lib64 /lib64 \
    --proc /proc \
    --dev /dev \
    --unshare-pid \
    --new-session \
    bash

What you should see is a bash prompt whose filesystem view is the tmpfs root plus the pieces you bound in, with /usr mounted read-only. The README notes that when targeting a chroot instead of the host tree, you would typically already have the lib64 symlink inside the target rootfs rather than creating it in the tmpfs. Treat the example as a starting point: it binds /dev and /proc, and it does not set up a user namespace, a network namespace or any seccomp filter.

Where bubblewrap stops protecting you

The Limitations section is the most important part of the README, and it is short. The first item concerns TIOCSTI. If you are not filtering out TIOCSTI commands with seccomp filters, the --new-session argument is needed to protect against out-of-sandbox command execution, and the README points at CVE-2017-5226. That means a command line without --new-session and without a seccomp filter has a known escape path on affected kernels. The trimmed usage example happens to include --new-session, but nothing enforces that.

The second item is broader: everything mounted into the sandbox can potentially be used to escalate privileges. The README gives a concrete case. If you bind a D-Bus socket into the sandbox, it can be used to execute commands via systemd, and it suggests xdg-dbus-proxy as the mitigation. This is the failure mode to internalise. A bind mount is not a neutral act of convenience. Each one is a channel into the host, and the sandbox is only as tight as the union of those channels.

The README also states that the maintainers believe the tool does not allow privilege escalation even in combination with typical distribution software, but that it may increase a logged-in user's ability to perform denial of service attacks. So the threat model is asymmetric: escaping to root is treated as out of scope, exhausting host resources is not. If your requirement is protecting the host from a hostile tenant, bubblewrap alone does not answer that question; your argument list does.

bubblewrap compared with a full container runtime

The obvious alternative is a container runtime such as docker or systemd-nspawn. The difference is not the kernel features, which overlap, but who holds the privilege and who owns the policy. Those runtimes run a daemon or a privileged helper that sets up the container, which is why the README says they are unsuitable to give to unprivileged users directly. bubblewrap pushes the setup into the calling process, which needs no special privilege because user namespaces do the work.

A second comparison is a language-level sandbox, for example a Python sandbox library. Those restrict what interpreted code can call inside one process, and they inherit the privileges of the interpreter. bubblewrap instead changes the process's view of the filesystem and its namespace membership before the target program starts, which works for any binary, not just one language. The cost is that it knows nothing about your program's semantics.

A third is a chroot. The README links to the classic limitations of chroot, and the key difference is PR_SET_NO_NEW_PRIVS, which bubblewrap uses to turn off setuid binaries. That removes the traditional chroot escape route where a setuid binary inside the new root grants elevated privileges. If you are still using a plain chroot for untrusted code, that is the specific gap bubblewrap closes.

Maintenance status, licensing and upgrade cost

The repository is not archived, and the last push was on 2026-09-18. Release 0.12.0 was published on 2026-08-26, following 0.11.2 on 2026-04-23 and 0.11.1 on 2026-03-21. The cadence is steady rather than rapid, which fits a tool whose surface is a set of command-line flags and a small C codebase with files such as bubblewrap.c, bind-mount.c and network.c.

Upgrade cost is mostly about the callers, not the binary. Removing the setuid mode is the clearest example: a change of that kind invalidates any deployment that depended on it, and it does not show up as a compile error in a wrapper script. Because the security properties live in the argument list, a version bump can change defaults or add required flags without changing your code. The repository carries a NEWS.md and a release-checklist.md, so the changelog is the place to check before moving a pinned version.

Licensing needs care rather than a summary. The repository has COPYING, COPYING.LIB and LICENSE files, and the project metadata marks the licence as NOASSERTION, which means the automated classification did not resolve it to a single identifier. The presence of both a GPL-style COPYING and an LGPL-style COPYING.LIB suggests a split between the program and library parts, but the only reliable answer is to read those files and, if you are redistributing or linking, get your own legal review. Nothing here should be read as legal advice.

Editorial conclusion

Adopt bubblewrap if you are writing a launcher or runtime that needs to assemble a filesystem view and namespace set for an unprivileged process, and you are prepared to own the security policy yourself. Do not adopt it as a drop-in sandbox for untrusted code if you cannot review the argument list, because the README states the level of protection is entirely determined by the arguments passed. Before shipping, verify that --new-session is present or that TIOCSTI is filtered with seccomp, and check which sockets you bind into the sandbox.

Frequently asked questions

How do I install bubblewrap on Ubuntu?

The README states that bubblewrap is available in the package repositories of most Linux distributions and can be installed from there, so use your distribution's package manager rather than building it. If you need to build from source, the README gives a meson workflow with meson setup _builddir, meson compile, meson test and meson install.

How do I use bubblewrap to run a sandboxed shell?

The README shows a trimmed example that runs bash with --ro-bind /usr /usr, a lib64 symlink, --proc /proc, --dev /dev, --unshare-pid and --new-session. It describes that example as incomplete and intended for illustration, and points to the larger demo script at demos/bubblewrap-shell.sh in the source tree.

Is bubblewrap a complete sandbox with its own security policy?

No. The README states that bubblewrap is a tool for constructing sandbox environments, not a complete, ready-made sandbox with a specific security policy, and that the level of protection is entirely determined by the arguments passed to it. The program that builds those arguments is responsible for defining the security model.

Does bubblewrap still support a setuid mode?

No. The README says bubblewrap historically supported a setuid mode for systems where unprivileged user namespaces were not supported, and that this has been removed.

Official sources

  1. containers/bubblewrap on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/containers-bubblewrap.svg)](https://hysenlabs.com/projects/containers-bubblewrap)