
Run Firefox in a rootless Podman container with dropped capabilities, isolated networking, and ephemeral storage to contain sandbox escapes and prevent host compromise.
Run Firefox in a rootless Podman container for security isolation. Your browser runs with almost no Linux capabilities, in its own user and network namespace, isolated from the host — while still having full GPU acceleration, audio, and DRM support.
Firefox already has a multi-process sandbox that isolates web content renderers using Linux namespaces and seccomp-bpf. For most threats, this is effective. foxcage adds a second wall: if an attacker exploits a vulnerability that escapes Firefox's sandbox (which happens — there are CVEs for this), they land inside a locked-down container instead of your full user session.
~/.ssh, ~/.gnupg, browser profiles for other browsers, password manager databases, documents, source code. In foxcage, the attacker sees only what you've explicitly mounted in.@tmp ephemeral cage leaves zero trace on disk after the window closes — including extensions, HSTS state, TLS session cache, and DNS cache that Firefox's Private Browsing still persists. Multiple @tmp cages run concurrently without interfering with each other.~/.config/autostart, ~/.bashrc, cron, or anywhere else to survive a reboot. foxcage's ephemeral container (--rm) means nothing persists unless you've bind-mounted it.localhost. On bare Firefox, a sandbox escape has full network access. (Use [network] mode = "host" if a cage needs localhost access, e.g. for local development — but see the caveat under "Networking": host mode also exposes the host's abstract Unix sockets.)CAP_SYS_CHROOT and blocks new privilege acquisition. Setuid binaries, kernel exploits via obscure syscalls, and similar escalation paths are cut off.profile, downloads_dir, extra bind mounts) is fully accessible to a compromised browser. If you mount a host profile directory, an attacker can tamper with it just as on bare Firefox.The container runs with:
CAP_SYS_CHROOT added back for Firefox's content sandbox; CAP_SETUID/CAP_SETGID added temporarily when init.root is configured)no-new-privileges to prevent privilege escalation--userns keep-id)/dev/shm (not shared with host) — configurable size via shm_sizenetwork.dns)XDG_RUNTIME_DIR are bind-mounted in (Wayland, PulseAudio, PipeWire, and the filtered D-Bus proxy) — the full host runtime directory is never exposedxdg-dbus-proxy running on the host. Only org.freedesktop.Notifications, org.freedesktop.portal.Desktop, org.mozilla.*, and (for forks) the fork's own namespace (e.g. org.librewolf.*) are reachable — session services like the keyring and the SSH/GPG agent are blockedorg.freedesktop.portal.Desktop is allowed as a whole, because that is how the file picker, "open link in another app", and screen sharing work. It also exposes RemoteDesktop (synthetic keyboard/mouse for the whole session), Camera and Location. Those are gated by your desktop's own approval dialogs rather than by foxcage — and the RemoteDesktop prompt resembles the screen-share prompt, so read approval dialogs before accepting them. xdg-dbus-proxy has no "deny one interface" rule, so narrowing this means enumerating every interface Firefox needs; see docs/DESIGN.md for why that is not done by defaultprofile, downloads_dir, extra [mounts] bind) use nosuid,noexecgpg --verify, which exits 0 for a signature made by a revoked key and for any key in the keyring. foxcage additionally requires that the signature chains to the pinned primary key, and refuses any release signed by a subkey its owner revoked as compromised — see Revoked signing keys--rm) — filesystem writes are lost on exitEach [network] and [mounts] option you enable trades some isolation for convenience. The defaults are the most restrictive configuration that still gives you a usable browser.
sudo apt install passt) — unless network.mode = "host"sudo apt install xdg-dbus-proxy)/dev/dri foxcage warns and Firefox renders in software. VA-API drivers for Intel, AMD and nouveau are installed in the image, so hardware video decoding works without host driver packages — see Hardware video decodingRun foxcage as your normal desktop user, not as root or via sudo — the sandbox maps your user into the container, and running as root removes the isolation foxcage exists to provide. It refuses to start as root.
Tested environment: Debian 13 (Trixie) with GNOME 3. Other Linux distributions and Wayland compositors may work but have not been tested.
foxcage is a single Python script with no dependencies outside the Python standard library. Copy it to a directory in your PATH:
sudo cp foxcage /usr/local/bin/foxcage
Or for a user-local install:
cp foxcage ~/.local/bin/foxcage
Make sure the script is executable (chmod +x foxcage).
Check which revision you have with foxcage --version — useful when reporting a problem, since foxcage is installed by copying a single file.
./foxcage
On first run the script builds the container image (downloads Firefox from Mozilla, installs minimal Debian dependencies) and then starts Firefox. On subsequent runs, foxcage checks for Firefox updates and rebuilds the image automatically when a new version is available. The image is also rebuilt periodically (every 7 days by default) to pick up system package updates. If the update check fails (network error, timeout), a warning is logged and the existing image is used — startup is never blocked.