# How the Bubblewrap Backend Uses User Namespaces for Linux Sandboxing

> Discover how Bubblewrap creates unprivileged Linux sandboxes using user namespaces for isolated user, PID, IPC, and UTS environments without root access. Learn more about the microsoft/mxc repository.

- Repository: [Microsoft/mxc](https://github.com/microsoft/mxc)
- Tags: deep-dive
- Published: 2026-06-07

---

**The Bubblewrap backend creates unprivileged Linux sandboxes by invoking the `bwrap` binary with `--unshare-user` and related flags, establishing isolated user, PID, IPC, and UTS namespaces without requiring root privileges.**

The Microsoft MXC (Multiplatform eXecution Container) SDK provides secure workload isolation through its Bubblewrap backend, which leverages Linux kernel features to create lightweight sandboxes. By utilizing **user namespaces**, the backend enables unprivileged users to spawn isolated processes that run with distinct UID/GID mappings separate from the host system. This approach eliminates the need for elevated privileges while maintaining strong security boundaries between the sandboxed application and the underlying host.

## How User Namespaces Enable Unprivileged Sandboxing

The Bubblewrap backend achieves **unprivileged Linux sandboxing** by relying on the kernel's ability to create new user namespaces without root access. When the Linux kernel parameter `/proc/sys/kernel/unprivileged_userns_clone` is set to `1`, an unprivileged user can invoke the `bwrap` binary to create a namespace-isolated process. According to the documentation in [`docs/bwrap-support/bubblewrap-backend.md`](https://github.com/microsoft/mxc/blob/main/docs/bwrap-support/bubblewrap-backend.md) (lines 24-28), this kernel configuration allows the SDK to sandbox processes without requiring sudo or root privileges.

Inside the new user namespace, the sandboxed process receives a distinct set of UID and GID mappings that are independent of the host's identifiers. This mapping effectively gives the process root privileges within the sandbox (UID 0) while mapping to a non-privileged UID on the host, preventing privilege escalation outside the namespace.

## Namespace Isolation Architecture

The Bubblewrap backend constructs a multi-layered isolation environment by unsharing several Linux namespaces simultaneously. As documented in [`docs/bwrap-support/bubblewrap-backend.md`](https://github.com/microsoft/mxc/blob/main/docs/bwrap-support/bubblewrap-backend.md) (lines 56-58), the backend passes specific `--unshare-*` flags to the `bwrap` binary to create these isolation boundaries.

### User Namespace Creation

The `--unshare-user` flag instructs `bwrap` to create a new **user namespace** before executing the target command. This namespace provides the foundation for all other sandboxing features, as it allows the process to acquire capabilities within the sandbox that are isolated from the host's privilege model. The user namespace ensures that any capability adjustments or permission changes remain contained within the sandbox environment.

### Process and System Isolation

Beyond user namespaces, the backend unshares three additional critical namespaces. The `--unshare-pid` flag creates a new PID namespace, causing the sandboxed process to run as PID 1 and treating it as the init process for that process tree. The `--unshare-ipc` flag isolates inter-process communication resources, while `--unshare-uts` creates a separate UTS namespace for hostname isolation. These flags work together to ensure that process IDs, shared memory, and system identifiers remain private to the sandbox.

### Filesystem and Device Views

The sandboxed process receives a restricted view of the host filesystem through specific bind mounts and virtual filesystems. The configuration applies `--ro-bind / /` to provide a read-only view of the host root, while `--dev /dev`, `--proc /proc`, and `--tmpfs /tmp` create private instances of these critical directories (as noted in [`docs/bwrap-support/bubblewrap-backend.md`](https://github.com/microsoft/mxc/blob/main/docs/bwrap-support/bubblewrap-backend.md), lines 57-60). This setup prevents the sandboxed application from modifying system devices or persistent host data while maintaining access to necessary pseudo-filesystems.

## SDK Configuration and Execution Flow

The MXC SDK does not execute `bwrap` commands directly; instead, it delegates to a native binary that interprets high-level configuration objects.

### Detecting Bubblewrap Availability

Before attempting to create a sandbox, the SDK verifies that the `bwrap` binary exists on the host system. In [`sdk/src/platform.ts`](https://github.com/microsoft/mxc/blob/main/sdk/src/platform.ts) (lines 316-320), the `isBubblewrapAvailable` function executes `bwrap --version` via `execSync` to confirm the binary is present and functional. This check prevents runtime errors and allows the SDK to fall back to alternative containment strategies if Bubblewrap is unavailable.

### Building the Container Configuration

When a user requests a Bubblewrap sandbox, the SDK constructs a `ContainerConfig` object with the containment field explicitly set. In [`sdk/src/sandbox.ts`](https://github.com/microsoft/mxc/blob/main/sdk/src/sandbox.ts) (lines 90-99), the `buildBubblewrapConfig` function sets `containment: 'bubblewrap'` and applies Linux-specific network policies. This configuration object is then serialized to JSON and passed to the native `wxc-exec` binary on Linux hosts.

### Native Binary Invocation and Cleanup

The `wxc-exec` binary translates the JSON configuration into the final `bwrap` command line, automatically injecting the necessary `--unshare-*` flags. When the sandboxed command exits, the `bwrap` process terminates automatically, triggering cleanup of all associated namespaces. As documented in [`docs/bwrap-support/bubblewrap-backend.md`](https://github.com/microsoft/mxc/blob/main/docs/bwrap-support/bubblewrap-backend.md) (lines 63-64), this design ensures that namespace resources are released immediately upon process completion without requiring manual intervention.

## Practical Implementation Example

The following TypeScript example demonstrates how to launch a sandboxed process using the Bubblewrap backend via the MXC SDK:

```typescript
import { spawnSandbox } from "mxc/sdk";

const policy = {
  version: "0.6.0-alpha",
  containment: "bubblewrap",
  process: { commandLine: "echo 'Hello from Bubblewrap'" },
};

spawnSandbox("echo 'Hello from Bubblewrap'", policy, { experimental: true })
  .onData(data => console.log(data))
  .onExit(e => console.log(`Exited with code ${e.exitCode}`));

```

Behind the scenes, the SDK generates a configuration flagged with `containment: 'bubblewrap'`, which the native binary translates into a command equivalent to:

```bash
bwrap \
  --unshare-user \
  --unshare-pid \
  --unshare-ipc \
  --unshare-uts \
  --ro-bind / / \
  --dev /dev \
  --proc /proc \
  --tmpfs /tmp \
  --setenv PATH /usr/bin \
  --setenv HOME /tmp \
  -- sh -c "echo 'Hello from Bubblewrap'"

```

This execution model ensures the command runs inside an **unprivileged user namespace** with automatic resource cleanup upon completion.

## Summary

- The Bubblewrap backend leverages **Linux user namespaces** to create sandboxes without requiring root privileges, provided the kernel allows unprivileged namespace creation.
- The backend unshares **user, PID, IPC, and UTS namespaces** via `--unshare-*` flags to establish process isolation and independent resource views.
- The MXC SDK detects `bwrap` availability in [`sdk/src/platform.ts`](https://github.com/microsoft/mxc/blob/main/sdk/src/platform.ts) and configures sandboxes through [`sdk/src/sandbox.ts`](https://github.com/microsoft/mxc/blob/main/sdk/src/sandbox.ts) by setting `containment: 'bubblewrap'`.
- The native `wxc-exec` binary constructs the final command line and ensures **automatic namespace cleanup** when the sandboxed process terminates.
- Sandboxed processes receive **read-only host filesystem access** with private `/dev`, `/proc`, and `/tmp` directories to prevent host modification.

## Frequently Asked Questions

### Do I need root privileges to use the Bubblewrap backend?

No. The Bubblewrap backend is designed specifically for **unprivileged sandboxing**. As long as your Linux kernel has `/proc/sys/kernel/unprivileged_userns_clone` set to `1`, the `bwrap` binary can create new user namespaces without root access. The SDK performs this operation through the standard `bwrap` invocation, allowing regular users to spawn isolated environments safely.

### Which specific namespaces does the Bubblewrap backend create?

The backend creates four distinct namespaces using the `--unshare-user`, `--unshare-pid`, `--unshare-ipc`, and `--unshare-uts` flags. The **user namespace** isolates privileges and UID mappings, the **PID namespace** provides a separate process tree with the sandboxed process as PID 1, the **IPC namespace** isolates shared memory and semaphores, and the **UTS namespace** provides an isolated hostname.

### How does the MXC SDK detect if Bubblewrap is installed?

The SDK checks for the `bwrap` binary at runtime using the `isBubblewrapAvailable` function in [`sdk/src/platform.ts`](https://github.com/microsoft/mxc/blob/main/sdk/src/platform.ts) (lines 316-320). This function attempts to execute `bwrap --version` synchronously via `execSync`. If the command succeeds, the SDK proceeds with Bubblewrap containment; otherwise, it may fall back to alternative isolation methods or report an error depending on the configuration.

### What happens to the namespaces when the sandboxed process exits?

The namespaces are automatically destroyed when the sandboxed process terminates. The `bwrap` binary monitors the child process (the user-specified command) and exits immediately when that child exits. According to [`docs/bwrap-support/bubblewrap-backend.md`](https://github.com/microsoft/mxc/blob/main/docs/bwrap-support/bubblewrap-backend.md) (lines 63-64), this behavior ensures that all associated namespace resources, including user and PID namespaces, are cleaned up by the kernel without requiring manual intervention or explicit unmount operations.