Capabilities Dropping in Docker

Capabilities Dropping in Docker

How Docker uses Linux capabilities to break the all-or-nothing power of root into dozens of separate privileges — and why removing the ones a container doesn't need is one of the highest-leverage security habits available.

Ask most engineers what “root” means on Linux and they will describe a single, all-powerful account that can do absolutely anything on the system — change any file, kill any process, reconfigure the network, load kernel modules, override every permission check. For decades that was true, and it created an uncomfortable reality for containers: a process running as root inside a container, even though it is walled off by namespaces from the rest of the host, still carries that same terrifying all-or-nothing power within its own container boundary, and history has shown that boundary is not always perfectly sealed. Linux capabilities exist to fix exactly this problem, by splitting root’s monolithic power into roughly forty separate, independently grantable privileges. Capabilities dropping is the practice of deliberately stripping away every one of those privileges a container does not genuinely need, so that even a process running as root inside the container is left with only a fraction of root’s original power. This guide explains what capabilities actually are, how Docker manages them, and how to drop them effectively without breaking legitimate workloads.

Chapter One

ACore Concepts

Understanding capabilities dropping starts with understanding what a Linux capability is, and why the kernel bothered inventing them in the first place.

What a Linux capability actually is

A capability is a single, named unit of privilege that the Linux kernel checks independently before allowing a specific class of operation. Instead of a binary “are you root or not” question, the kernel asks a much more specific question for each sensitive action: does this process hold the particular capability required for this particular operation? Binding to a network port below 1024, for example, requires the CAP_NET_BIND_SERVICE capability. Changing file ownership requires CAP_CHOWN. Loading a kernel module requires CAP_SYS_MODULE. A process can hold any combination of these roughly forty capabilities, meaning it can be granted exactly the narrow slice of root’s power it actually needs, and nothing more.

Everyday Analogy

Think of traditional root access like a single master key that opens every door in a large office building — the server room, the finance office, the roof access hatch, everything. Capabilities are like replacing that one master key with a keyring where each key opens exactly one door. A cleaning contractor might be issued the keys to every office but deliberately not the key to the server room or the roof — they can do their job completely, while a stolen or misused keycard still can’t compromise the areas they never needed.

Why containers make this especially important

On a traditional server, running a process as root is a well-understood, if risky, decision made once for a whole machine. In a container environment, dozens or hundraden of independently authored images — many pulled from public registries whose full contents were never audited line by line — frequently run as root inside their own containers by default, because it is the path of least resistance for image authors. Namespaces isolate a root process inside a container from directly seeing the host’s files or processes under normal conditions, but that isolation has historically had edge cases and kernel vulnerabilities that allowed a root-privileged container process to escalate beyond its boundary. Capabilities dropping adds a second, independent layer of defense: even if a container process is technically “root” and even if a namespace boundary is somehow bypassed, a process stripped of most capabilities simply lacks the kernel-level permission to perform the most dangerous operations that would make an escape or a host compromise possible.

i
Capabilities Are Not the Same as Users

Capabilities and user IDs are two separate, complementary security dimensions. Running a container as a non-root user is one valuable hardening step; dropping capabilities is a different, additional step that limits what even a root-privileged process inside the container can do. Using both together — a non-root user with a minimal capability set — is stronger than either one alone.

Docker’s default capability set

Rather than granting a new container every capability the kernel supports, Docker starts every container off with a curated default subset — historically around fourteen capabilities, covering common needs like changing file ownership, binding privileged ports, and sending signals to other processes in the same container. This default set is already a meaningful reduction from full root privilege, but it still includes several capabilities that the overwhelming majority of ordinary application containers never actually use, which is precisely the gap capabilities dropping is designed to close.

~40
TOTAL LINUX CAPABILITIES
14
GRANTED TO A CONTAINER BY DEFAULT
1
FLAG TO START FROM ZERO CAPABILITIES

Chapter Two

BArchitecture & Components

Capabilities are enforced by a specific kernel subsystem working alongside — not instead of — the container isolation mechanisms covered elsewhere in Docker’s security model.

flowchart TB
    subgraph KERNEL["Linux Kernel"]
        CHK["Privileged Syscall
(e.g. mount, kill, chown)"] CAPCHK["Capability Check"] ALLOW["Operation Permitted"] DENY["EPERM Returned"] end PROC["Container Process
(capability set attached)"] PROC --> CHK CHK --> CAPCHK CAPCHK -->|has required capability| ALLOW CAPCHK -->|missing capability| DENY
Fig. 1 — Every privileged operation is checked against the calling process’s specific capability set, not a single root/non-root flag
Kernel Structure

Capability Sets

Every process carries several distinct capability sets — permitted, effective, inheritable, bounding, and ambient — that together determine exactly which privileges it currently holds and which it could potentially gain.

Runtime Layer

runc / containerd

The low-level container runtime that actually configures a new container process’s capability sets at creation time, based on the values Docker’s daemon passes down to it.

Docker Daemon

Default Capability List

The daemon maintains the curated default set of roughly fourteen capabilities granted to any container that does not explicitly customize its capability configuration.

User Flags

–cap-drop / –cap-add

The command-line interface Docker exposes for removing specific capabilities from the default set, adding specific ones back, or dropping every capability at once as a starting point.

Enforcement Point

Kernel Syscall Layer

The actual check happens deep inside the kernel at the moment a privileged system call is made, independent of any container-aware logic — capabilities are a general Linux feature Docker configures, not something Docker enforces itself.

Related Layer

seccomp & AppArmor / SELinux

Capabilities restrict which broad privileges a process has; seccomp filters restrict which specific system calls it may invoke at all; mandatory access control systems like AppArmor add a further, independent layer — the three work together, not as substitutes for each other.

Where capabilities sit relative to namespaces

It helps to be clear about the division of labor. Namespaces (covered in topics like network namespace sharing) control what a process can see — which network interfaces, which process list, which mounted filesystem. Capabilities control what a process with root privilege is allowed to do once it can see something. A process might be namespace-isolated from the host’s real network interfaces entirely, and separately also lack the CAP_NET_ADMIN capability needed to reconfigure networking even within its own namespace. These two mechanisms are complementary layers of the same overall container security model, not competing approaches.

A look at exactly how the kernel decides, at the moment a privileged operation is attempted, whether a process is allowed to proceed.

The moment of the check

Every time a process attempts an operation the kernel considers privileged — changing a file’s owner, adjusting system time, binding a low-numbered network port, mounting a filesystem — the kernel’s system call handler for that operation checks whether the calling process’s effective capability set includes the specific capability that operation requires. If the capability is present, the operation proceeds exactly as it would for unrestricted root. If it is absent, the kernel immediately rejects the call with a permission-denied error, regardless of whether the process’s user ID is zero (root) or not. This is the crucial mechanical fact that makes capabilities dropping meaningful: user ID and capability possession are checked separately, and a process can be user ID zero while still being denied an operation purely because it lacks the matching capability.

How Docker builds a container’s capability set at startup

When a container starts, Docker’s daemon computes the container’s final capability set by starting from its internal default list, then removing any capabilities named in a drop instruction, then adding back any capabilities named in an add instruction, and finally passing that resolved list down to the container runtime, which configures it into the new process’s permitted, effective, and bounding sets before the container’s first command ever executes. Crucially, the bounding set — the absolute ceiling on which capabilities a process or any of its descendants can ever hold, even if it tries to re-acquire them later — is set at this same moment, which is what prevents a dropped capability from being silently regained later in the container’s life through ordinary means.

sequenceDiagram
    participant U as docker run --cap-drop ALL --cap-add NET_BIND_SERVICE
    participant D as Docker Daemon
    participant R as Runtime (runc)
    participant P as Container Process

    U->>D: requested drop/add list
    D->>D: default set - drops + adds = final set
    D->>R: final capability set
    R->>P: configure permitted/effective/bounding sets
    Note over P: process starts already
holding only NET_BIND_SERVICE
Fig. 2 — The final capability set is resolved before the container’s first process ever begins executing

Why the bounding set matters more than it first appears

Without the bounding set acting as a hard ceiling, a sufficiently clever process could potentially find ways to re-acquire a capability that was only removed from its immediate effective set — for instance by re-executing a file with certain permission bits set, a classic privilege-escalation technique on traditional Linux systems. Because Docker configures the bounding set itself at container creation, a capability that has been dropped is not just temporarily inactive; it becomes genuinely unreachable for the remaining lifetime of that container, closing off this entire category of escalation regardless of what the process inside subsequently attempts.

Chapter Four

DData Flow & Lifecycle

Tracing the lifecycle of a container’s capability configuration from the moment it is specified to the moment it governs an actual system call.

1

Configuration Specified

An operator or a Compose file specifies which capabilities to drop, which to add back, or requests dropping every capability as the starting baseline.

2

Final Set Resolved

The Docker daemon computes the resulting capability list by applying the requested drops and adds on top of its internal default set.

3

Runtime Configures the Process

The container runtime configures the new process’s permitted, effective, and bounding capability sets before the container’s entrypoint command begins executing.

4

Steady-State Enforcement

For the remainder of the container’s life, every privileged system call made by any process inside it is checked against this fixed capability set at the moment it is attempted.

5

Child Processes Inherit the Ceiling

Any new process spawned inside the container inherits capabilities governed by the same bounding set, meaning it can never exceed what the container was originally configured with.

6

Container Exits

The capability configuration, like the rest of the container’s runtime state, ceases to exist along with the container; a new container started from the same image begins the process again from its configured defaults.

“Dropping a capability doesn’t just refuse a request in the moment — it removes the permission from the process’s ceiling for the rest of its life, closing off later attempts to reclaim it.”

What happens when a dropped capability is needed

If a process inside a container attempts an operation requiring a capability that has been dropped, the kernel returns a standard permission-denied error at the exact point of that system call — the same error an ordinary, unprivileged Linux process would receive attempting the same operation. This is not a Docker-specific error message; it surfaces exactly as it would on any Linux system, which means troubleshooting a “mysteriously failing” container after capabilities have been tightened often means recognizing a permission-denied failure as a capability issue rather than a filesystem or application-configuration issue.

Chapter Five

EAdvantages, Disadvantages & Trade-offs

Capabilities dropping is close to a pure security win, but it is not entirely free, and understanding its limits keeps expectations realistic.

Advantages

  • Meaningfully shrinks what a compromised or malicious container process can do, even while running as root inside its own namespace
  • Closes off entire categories of privilege-escalation and container-escape techniques that rely on specific dangerous capabilities
  • Costs no runtime performance — capability checks are a normal, lightweight part of every Linux system call
  • Works alongside, and strengthens, every other container hardening measure rather than replacing any of them
  • Can be applied incrementally, starting from the default set and dropping capabilities one at a time as they are verified unnecessary

Disadvantages

  • Determining exactly which capabilities a given application genuinely needs requires careful testing, not guesswork
  • Overly aggressive dropping can cause confusing, hard-to-diagnose permission failures deep inside third-party application code
  • Does not protect against vulnerabilities that do not require any capability at all, such as many application-layer logic flaws
  • Some legacy or poorly designed images assume full default capabilities and may not function correctly once capabilities are restricted
  • Provides no protection against a kernel vulnerability that bypasses the capability check mechanism itself

Defense in depth, not a silver bullet

Capabilities dropping should be understood as one strong layer within a broader defense-in-depth strategy, not a complete container security solution on its own. It meaningfully reduces the blast radius of a compromised container, particularly against known classes of container-escape techniques that rely on specific dangerous capabilities like CAP_SYS_ADMIN or CAP_SYS_MODULE, but it does nothing to prevent, for example, an application-level SQL injection vulnerability or a leaked credential from being misused within whatever access that credential legitimately grants. The honest framing is that capabilities dropping shrinks the kernel-level attack surface available to an attacker who has already gained code execution inside a container — a genuinely valuable and cheap improvement, but only one piece of a complete security posture.

i
The “Drop All, Add Back” Pattern

The strongest and most common practical pattern is to drop every capability as a starting point and then explicitly add back only the small handful an application has been verified to actually require, rather than starting from Docker’s default set and trying to guess which of the fourteen default capabilities are safe to remove. This inverts the default from “permissive unless proven dangerous” to “restrictive unless proven necessary,” which is the more defensible security posture.

Chapter Six

FSecurity

Because capabilities dropping is fundamentally a security control, this chapter looks specifically at the dangerous capabilities that matter most and the attack classes they close off.

The most dangerous capabilities to know by name

CapabilityWhat It GrantsWhy It’s High-Risk in a Container
CAP_SYS_ADMINA broad grab-bag of administrative operations, including many mount-related actionsFrequently the single capability most associated with known container-escape techniques; considered the closest thing to “root-lite”
CAP_SYS_MODULELoad and unload kernel modulesAllows loading arbitrary code directly into the host kernel, a near-total compromise if reachable from inside a container
CAP_SYS_PTRACEInspect and control other processes’ memory and executionCan allow a container process to read secrets out of another process’s memory, including processes outside expected boundaries
CAP_NET_RAWCreate raw network socketsEnables packet crafting and certain network-based attacks like spoofing that ordinary sockets do not permit
CAP_DAC_OVERRIDEBypass normal file read/write/execute permission checksDefeats the purpose of ordinary file permission restrictions within the container’s filesystem
Security Analogy

If CAP_SYS_ADMIN is the master key that opens almost every door, dropping it before handing an untrusted contractor the rest of the keyring is like removing the one key that happens to also open the building’s electrical control room — the contractor can still do their actual job perfectly well with the remaining keys, but the single most consequential door is simply never available to them, regardless of any other mistake made elsewhere in the security setup.

How dropping interacts with running as non-root

The strongest, most widely recommended container hardening combination pairs a non-root user with an aggressively minimized capability set — dropping everything and adding back only what is verified necessary. These two measures address different attacker scenarios: running as non-root limits damage even if every capability were somehow still present, because many privileged operations additionally require an effective user ID of zero on top of the relevant capability; minimizing capabilities limits damage even in the rarer case where a process does hold or briefly regains elevated user privilege. Neither substitutes fully for the other, which is why security guidance consistently recommends applying both together rather than treating them as alternative options.

ADR-032: Drop-All-Add-Back as the Organization-Wide DefaultAdopted
Context

A security review found that the majority of internally built container images ran with Docker’s full default capability set despite very few of them ever exercising most of those default privileges.

Decision

All internally maintained base images were updated to drop every capability by default, with individual teams required to explicitly document and add back any specific capability their application genuinely needed.

Consequence

A handful of legacy applications required a short remediation effort to identify and re-add the one or two capabilities they actually relied on, but the organization’s baseline container attack surface was reduced fleet-wide with no ongoing performance cost.

Chapter Seven

GMonitoring, Logging & Metrics

Because capability checks happen deep in the kernel, visibility into how they are behaving requires purpose-built observability, not general container metrics.

Detecting denied capability attempts

When a process attempts an operation requiring a capability it does not have, the kernel’s audit subsystem, where enabled, can record the specific denied attempt, including which capability was missing and which process made the request. This is the single most useful signal for two very different purposes: catching a legitimate application that needs a capability it wasn’t granted, and detecting a compromised or malicious process probing for capabilities it should never have needed in the first place, both of which produce the same underlying kernel-level denial event but warrant very different responses.

Expected Signal

Occasional Denials During Rollout

A short-lived spike in capability denial events immediately after tightening a container’s capability set usually indicates a legitimate, previously-unnoticed dependency that needs to be added back explicitly.

Concerning Signal

Denials for High-Risk Capabilities in Steady State

Repeated attempts to use a capability like CAP_SYS_ADMIN or CAP_SYS_MODULE from a container that has been stable in production for a long time is a stronger indicator worth treating as a potential security event rather than routine noise.

Baseline Signal

Zero Denials, Zero Excess Capabilities

A well-tuned container in a mature state shows no capability denials at all, because its granted set has already been trimmed to exactly what the application uses.

Auditing what capabilities a running container actually holds

Beyond watching for denials, periodic auditing of the capability set each running container is actually configured with — comparing it against the organization’s documented minimal-necessary set for that image — catches configuration drift, where a container is redeployed at some point with a looser capability configuration than intended, whether through a copy-pasted deployment manifest or a well-meaning but overly broad troubleshooting change that was never reverted.

!
Silent Regression Risk

Because a container with excess capabilities usually runs perfectly normally — the extra capabilities simply sit unused — configuration drift toward a looser capability set produces no functional symptom at all, only a security regression. Regular auditing, not just monitoring for failures, is necessary to catch this class of problem.

Chapter Eight

HDesign Patterns & Anti-patterns

Recurring approaches that make capability management sustainable across many images and teams, and the recurring mistakes that erode its value.

Pattern

Drop-All-Add-Back Baseline

Every image starts from zero capabilities, with each one added back individually and documented against the specific feature that requires it, making the capability list a form of living security documentation.

Pattern

Privileged Helper, Unprivileged Application

A short-lived, narrowly scoped helper process or init container performs the one operation genuinely requiring an elevated capability (such as binding a privileged port), then the main long-running application process continues with that capability dropped entirely.

Pattern

Per-Image Capability Manifest

Teams maintain an explicit, reviewed list of exactly which capabilities each image requires and why, checked as part of code review whenever a new capability is requested.

Anti-pattern

Adding Capabilities to Silence an Error

Adding back a capability purely because an application throws a permission error, without first diagnosing whether the underlying operation is actually necessary or could be redesigned to avoid needing it at all.

Anti-pattern

Copying a Broad Capability Set Across Unrelated Images

Reusing one image’s capability configuration for a different application with entirely different needs, granting privileges the new application never actually exercises.

Pattern in practice: the privileged-port problem

A common real-world case is a web application that wants to bind directly to port 80, which ordinarily requires the CAP_NET_BIND_SERVICE capability. Rather than running the entire application with a broad default capability set just to satisfy this one requirement, the recommended pattern is to drop every capability except that single one, letting the application bind its privileged port while remaining otherwise as unprivileged as an ordinary non-root process for everything else it does.

Chapter Nine

IBest Practices & Common Mistakes

Concrete guidance for rolling out capability restrictions without breaking production workloads along the way.

Do

Test in a staging environment first

Roll out a tightened capability set in a non-production environment under realistic load before applying it to production, since some capability dependencies only surface under specific, infrequent code paths.

Do

Add back capabilities one at a time

When troubleshooting a permission failure after dropping all capabilities, add back the single most likely candidate rather than restoring several at once, to build an accurate, minimal picture of what the application truly needs.

Do

Document why each retained capability exists

Record the specific feature or code path that requires each capability an image keeps, so future maintainers can evaluate whether it is still necessary rather than treating it as untouchable.

Avoid

Reaching for privileged mode as a shortcut

Running a container in fully privileged mode — which effectively grants every capability plus additional device access — to resolve a single permission error is a drastic overcorrection almost never actually required.

Avoid

Assuming third-party images are already minimal

Many popular public images run with Docker’s full default capability set out of the box; explicitly reviewing and tightening capabilities for third-party images is just as important as for internally built ones.

Avoid

Treating capability review as a one-time exercise

Application code changes over time, and a capability that was genuinely required at one point may no longer be needed after a refactor, making periodic re-review worthwhile.

Checklist Before finalizing a container’s capability configuration, confirm: (1) the image was tested with every capability dropped and only verified-necessary ones added back, (2) each retained capability is documented against a specific requirement, (3) the configuration is paired with a non-root user wherever feasible, and (4) audit logging or monitoring is in place to catch future configuration drift.

Chapter Ten

JReal-World & Industry Examples

Capabilities dropping is a foundational recommendation across nearly every major container security guideline and platform available today.

Industry Guidance

CIS Docker Benchmark

Widely referenced security benchmarks for container deployment explicitly recommend dropping all default capabilities and adding back only what is required, treating it as a baseline hardening expectation rather than an advanced technique.

Orchestration

Kubernetes Security Contexts

Kubernetes exposes its own capability drop and add controls directly through pod and container security context specifications, applying the same underlying Linux capability mechanism this article describes at the orchestration layer.

Compliance

Regulated Industry Deployments

Organizations in finance, healthcare, and other regulated sectors frequently mandate minimal capability sets as part of formal container hardening standards required for audit and compliance purposes.

Public Image Ecosystem

Hardened Base Image Projects

Security-focused base image projects have gained popularity specifically because they ship with minimized default capabilities and non-root users out of the box, reducing the hardening work required of every downstream image that builds on them.

Top
RECOMMENDATION IN CONTAINER SECURITY BENCHMARKS
1
FLAG TO DROP EVERY CAPABILITY AT ONCE
0
RUNTIME PERFORMANCE COST

Chapter Eleven

KFrequently Asked Questions

Q1Is dropping capabilities the same thing as running a container as a non-root user?
No. They are separate, complementary controls. Capabilities limit what root-level privileges a process holds; running as non-root additionally removes the special treatment the kernel gives to user ID zero. The strongest configuration combines both.
Q2Can a container regain a capability that was dropped at startup?
Not through ordinary means. Because Docker configures the container’s bounding set at creation time, a dropped capability is removed from the absolute ceiling for that container’s lifetime, not just temporarily suppressed.
Q3Does dropping capabilities slow down a container’s performance?
No. Capability checks are a lightweight, standard part of how the Linux kernel already evaluates privileged operations, so restricting which capabilities a process holds carries no measurable runtime performance cost.
Q4What happens if I drop a capability my application actually needs?
The specific operation requiring that capability will fail with a standard permission-denied error at the exact moment it is attempted, which is why testing in a non-production environment before rolling out a tightened capability set matters.
Q5Is running a container in privileged mode the same as granting all capabilities?
Privileged mode grants all capabilities plus significantly broader access beyond capabilities alone, including direct device access and relaxed security module enforcement, making it a considerably larger grant of trust than simply adding every capability back individually.
Q6Do all container images need the same set of dropped or added capabilities?
No. The correct capability set depends entirely on what a specific application actually does, which is why a documented, per-image capability manifest is more reliable than reusing one configuration across unrelated images.

Chapter Twelve

LSummary and Key Takeaways

Capabilities dropping takes root’s historically monolithic power and cuts it down to exactly what each container genuinely needs.

Key Takeaways

  • Linux capabilities split root’s traditional all-or-nothing privilege into roughly forty independently grantable units, checked separately by the kernel for each privileged operation.
  • Docker grants every container a curated default set of around fourteen capabilities, which already exceeds what most ordinary application containers actually use.
  • Dropped capabilities are removed from the container’s bounding set at creation time, making the restriction genuinely permanent for that container’s lifetime, not merely a temporary suppression.
  • The strongest and most widely recommended pattern is dropping every capability and explicitly adding back only the ones a specific application has been verified to require.
  • Capabilities dropping is one layer of defense-in-depth — it shrinks the damage a compromised container process can do, but does not replace non-root users, seccomp filtering, or application-level security.
  • This practice is a baseline recommendation across major container security benchmarks and is directly exposed by orchestration platforms like Kubernetes, making it one of the highest-leverage, lowest-cost hardening steps available to any team running containers.