GuidesRunning & maintaining

“No usable sandbox!”: why Chromium won't start for your agent in Docker on Ubuntu 24.04

Two walls stop an agent's browser in a container: Docker's default seccomp profile refuses the user namespaces Chromium's sandbox needs, and Ubuntu 23.10+ restricts them with AppArmor. The seccomp rule, the sysctl, and why noSandbox is the last resort.

September 5, 2026The Everpod team
The short answer

Chromium’s sandbox puts each renderer in its own unprivileged user namespace, and inside a Docker container on a modern Ubuntu host two separate gates can refuse that. Docker’s default seccomp profile denies the clone and unshare calls that create namespaces, and Ubuntu 23.10 and later restrict unprivileged user namespaces with AppArmor. The fix that keeps the sandbox is a seccomp profile that adds those calls (and, if Chrome still aborts, the host sysctl or an AppArmor rule). The fix everyone reaches for, --no-sandbox or OpenClaw’s browser.noSandbox: true, works by removing the protection, and Chromium’s own docs say it “should never be used when browsing the open web,” which is exactly what an agent does.

The error, and what it is really saying

FATAL:... No usable sandbox! If you are running on Ubuntu 23.10+ or another
Linux distro that has disabled unprivileged user namespaces with AppArmor...

OpenClaw adds its own hint underneath: “If running in a container or as root, try setting browser.noSandbox: true.” Hermes, a Playwright script the agent wrote itself, or a bare chrome --headless all die the same way, because the problem is beneath the harness. Chromium’s renderer sandbox works by having each renderer process create a new user namespace and drop into it, which on Linux needs the clone or unshare system call with the CLONE_NEWUSER flag. When that call is refused, Chromium has no sandbox to use and, quite correctly, refuses to start rather than run unsandboxed by accident. The message blames AppArmor because that is the common cause on a desktop. Inside a container there is a second cause in front of it.

Gate one: Docker’s seccomp profile

Every container runs under a seccomp profile that lists the system calls it may make, and Docker’s default is deliberately conservative about namespaces. On the Docker version we tested (29.7), running unshare -U true as an unprivileged user inside a container under the default profile returned Operation not permitted; the same command under --security-opt seccomp=unconfined succeeded. That is the gate Chromium hits first, and it hits it before AppArmor is ever consulted.

The fix is a profile that is Docker’s default plus one rule allowing clone, unshare and setns unconditionally. Playwright publishes exactly such a file (their docs call it “a default Docker seccomp profile with extra user namespace cloning permissions”), and it is the right idea with one caveat we learned the hard way: it is a snapshot of an old default. Diffed against Docker 29.7’s actual default, Playwright’s copy lacked around fifty system calls that current Docker permits (clone3, close_range, openat2, the landlock_* and pidfd_* families, and more). Shipping it verbatim would silently tighten your container. So: take the default profile for the Docker version you actually run, add the three-syscall rule, and keep a note of which Docker version the base came from so you refresh it when the daemon upgrades.

# docker run
docker run --security-opt seccomp=/opt/agent/seccomp.json ...

# docker-compose.yml
services:
  gateway:
    security_opt:
      - seccomp=/opt/agent/seccomp.json

Verify from inside the container, as the user the browser runs as: unshare -Ur true should exit 0. If it does, the seccomp gate is open.

Gate two: Ubuntu’s AppArmor restriction

Ubuntu 23.10 introduced, and 24.04 LTS ships by default, an AppArmor feature that stops unconfined processes from creating unprivileged user namespaces unless a profile grants it. The switch is the sysctl kernel.apparmor_restrict_unprivileged_userns, which reads 1 on a fresh 24.04 host. This is the gate Chrome’s error message is actually about, and it is the one that bites a browser running directly on the host.

Whether it also bites inside your container depends on how the container is confined, which is why the advice is to test rather than assume. In our tests on Ubuntu 24.04.4, with the seccomp rule in place and the sysctl left at 1, Chromium’s sandbox worked under Docker’s standard docker-default AppArmor profile: the renderer ran in its own user and PID namespaces with no --no-sandbox anywhere. The reason is that Docker generates that profile under AppArmor ABI 3.0, which predates user-namespace mediation, so the kernel does not apply the new restriction to processes it confines. Docker’s own template comments that it keeps 3.0 for older distributions, so this may change with a Docker release; a profile compiled under ABI 4.0 without a userns, rule is denied like an unconfined process. If Chrome still aborts after the seccomp fix, this is your remaining gate, and Chromium’s documentation lists the options in order of preference:

Why not just set noSandbox

Because of what the sandbox is for. An agent’s browser visits pages the agent chose from search results, not pages you vetted, and the renderer sandbox is the layer that keeps a malicious page’s exploit inside one tab instead of in your agent’s process, its credentials and its files. Chromium’s guidance is unambiguous, and Playwright’s is the same: running without the sandbox is acceptable for trusted code like your own end-to-end tests, “not recommended for scraping untrusted websites.” There is also a practical point specific to harnesses: OpenClaw’s browser.noSandbox only affects the browser OpenClaw launches. A Chromium the agent starts itself with Playwright or Puppeteer still hits the same walls, which means teaching the agent to pass --no-sandbox everywhere. Fix the environment once and every browser on the machine works the way it does on a laptop.

Two neighbours you will meet next

Shared memory. Docker gives a container 64 MB of /dev/shm, and Playwright’s docs warn that without --ipc=host Chromium “can run out of memory and crash.” OpenClaw passes --disable-dev-shm-usage to its own browser so it does not hit this, but a Chromium the agent launches itself will; shm_size: "1gb" in the compose file costs nothing until used. The memory story on a small server has a bigger chapter than this: why an agent’s tabs can take the whole machine down.

The display. Once the sandbox works you will find a second, quieter problem: a server has no display, so Chromium runs headless and announces itself to every site as HeadlessChrome. In our measurements that string alone closed a dozen mainstream sites that opened for the identical browser with a display attached. The browser variant of OpenClaw’s image already ships Xvfb; start it before the Gateway and export DISPLAY (OpenClaw resolves to headed mode when a display is present and nothing is configured). One trap: xvfb-run fails in that image for want of xauth; starting Xvfb :99 -screen 0 1280x900x24 -nolisten tcp directly works. The site-by-site measurements show which walls the display removes and which are the server’s address, not its browser.

Your own cloud agent, set up for you.

Everpod runs OpenClaw on a private, always-on computer of its own: set up, secured and backed up, with model usage included. You name your agent, and say hello about fifteen minutes later.

Create your agent

First month half price, then $29/mo · model usage included · cancel anytime

Wondering what you’d do with one? See what a cloud agent can do