Safeguard
Containers

Docker Privileged Mode: What It Unlocks and Why to Avoid It

One flag, --privileged, hands a container almost the same power as root on the host. Here is exactly what it turns on, why it breaks isolation, and the narrow capabilities that replace it.

Safeguard Research Team
Research
6 min read

Running a container with Docker privileged mode — the --privileged flag — disables nearly every isolation boundary Docker normally enforces, giving the container all Linux capabilities, access to all host devices, and the ability to reconfigure the kernel. It is the single most dangerous common Docker flag, because a process that escapes a privileged container effectively lands as root on the host. Almost every legitimate use case actually needs one or two specific capabilities, not the blanket grant. This post explains what the flag turns on and what to reach for instead.

What does Docker privileged mode actually turn on?

By default, Docker runs containers with a deliberately restricted profile: a dropped set of Linux capabilities, a seccomp filter blocking dangerous syscalls, masked paths in /proc and /sys, an AppArmor or SELinux profile, and no direct access to host devices. --privileged switches most of that off at once. Concretely, a privileged container gets:

  • All Linux capabilities, including CAP_SYS_ADMIN, CAP_SYS_MODULE (load kernel modules), and CAP_SYS_PTRACE.
  • Access to all host devices under /dev — disks, not just the ones the container was given.
  • Write access to normally masked /proc and /sys paths, which control kernel and host behavior.
  • A disabled default seccomp filter, so syscalls Docker normally blocks are available.

The combination is the problem. With CAP_SYS_ADMIN and raw device access, a container can mount the host's root filesystem and read or write any file on it, or load a kernel module that owns the machine. This is not a theoretical escape — it is the documented mechanism, which is why container-escape proofs of concept so often start with "run it privileged."

Why is docker run privileged so risky?

The core issue is that containers are not virtual machines. Every container shares the host kernel. Isolation comes from capabilities, namespaces, cgroups, and syscall filtering layered on top of that shared kernel — and docker run privileged removes most of those layers. Once they are gone, the only thing standing between a compromised container process and the host is the kernel's own correctness, with no defense in depth behind it.

That matters most for anything exposed to untrusted input. A privileged container running a web service means a single remote code execution in that service is very likely a full host compromise, because the attacker inherits all those capabilities. It also matters in CI: privileged build runners that execute code from pull requests are a well-known path to compromising the build infrastructure itself.

What legitimately needs Docker privileged mode?

Honestly, very little. The situations people reach for it are usually narrower than the flag:

  • Docker-in-Docker — building images inside a container. Often solvable with rootless build tools like Buildah or Kaniko instead.
  • Low-level system tools — network debugging, filesystem or device manipulation.
  • Some hardware access — GPUs, USB devices, specific /dev nodes.

In each case the real requirement is one or two capabilities or a single device, not the entire host. The right move is to grant exactly that and nothing more.

What are the alternatives to --privileged?

Replace the blanket grant with targeted permissions.

Add only the capabilities you need. Drop everything, then add back the specific capability:

docker run --cap-drop=ALL --cap-add=NET_ADMIN mynetworktool

A tool that needs to configure network interfaces needs NET_ADMIN, not SYS_ADMIN and disk access.

Pass through only the specific device. For hardware, use --device for the exact node instead of privileged mode's all-devices access:

docker run --device=/dev/ttyUSB0 myserialtool

Keep the guardrails on. Where you must relax one restriction, keep the rest: retain the seccomp profile, keep --security-opt no-new-privileges, and run as a non-root user inside the container so an escape has less to work with.

In Compose, do the same. A docker-compose privileged: true line hides the same all-or-nothing grant behind clean YAML. Prefer scoped cap_add and devices entries:

services:
  tool:
    image: mynetworktool
    cap_drop: ["ALL"]
    cap_add: ["NET_ADMIN"]
    security_opt: ["no-new-privileges:true"]

If you genuinely need full isolation for an untrusted workload, the better answer is a stronger sandbox — a microVM runtime like Firecracker, or gVisor — rather than a privileged container, which is the weakest option, not the strongest.

How do you find privileged containers you already run?

Audit before you refactor. Search manifests for privileged: true and CI configs for --privileged, and inspect running workloads (docker inspect reports the Privileged flag; in Kubernetes, check securityContext.privileged across pods). Treat each hit as a question: which one capability or device does this actually need? Most convert cleanly to a scoped cap_add. Baking that check into your pipeline — flagging any new privileged container in review — keeps the count from creeping back up, and it pairs naturally with the SCA and container scanning you already run on images. We cover more container-hardening patterns across the blog.

FAQ

Is docker run privileged the same as running as root?

It is worse in scope. Running as root inside a normal container still leaves Docker's default restrictions in place — dropped capabilities, seccomp, masked paths. docker run privileged removes those restrictions too, so it is closer to root on the host than root in a sandbox. You can be root inside a locked-down container and still be well contained; privileged mode removes the containment.

Can I use --privileged just for local development?

For throwaway local work with trusted code, the risk is lower because the attack surface is small. The danger is habit: privileged flags copied from a local script into a shared Compose file or a CI pipeline is a common way the setting reaches production. Prefer scoped capabilities even locally so there is nothing risky to copy.

What replaces privileged mode for Docker-in-Docker?

Rootless, daemonless image builders — Buildah, Kaniko, or BuildKit in rootless mode — build images without the privileged daemon-in-daemon pattern. Where a true nested Docker daemon is unavoidable, isolate it in a dedicated VM rather than a privileged container on a shared host.

How do I know if a container really needs a capability?

Start from --cap-drop=ALL and run the workload. When it fails, the error usually points to the missing capability (an EPERM on a specific operation). Add back that one capability and retest. This bottom-up approach almost always lands on a far smaller set than --privileged grants.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.