How to Manage Linux Capabilities in Apple Container: `--cap-add` and `--cap-drop` Explained

Use --cap-add to grant specific Linux capabilities and --cap-drop to remove them from the default set, with both flags supporting case-insensitive names, optional CAP_ prefixes, and the special ALL token to toggle the complete capability set.

The apple/container repository provides a lightweight container runtime that executes workloads inside virtual machines with a reduced Linux capability set for security hardening. Understanding how to manage Linux capabilities using --cap-add and --cap-drop commands allows you to implement precise least-privilege access controls while ensuring your applications have the specific kernel permissions they require.

Understanding the Default Capability Set

By default, containers run with a restricted subset of Linux capabilities rather than the full root privilege set. The default configuration includes capabilities such as CAP_NET_RAW and CAP_SETUID, but excludes many administrative privileges. You can view the complete default list in the project documentation at docs/how-to.md (lines 70-76).

This reduced set ensures that containers operate with minimal privileges, following the principle of least privilege. When your workload requires additional kernel permissions—such as network administration or process ownership changes—you must explicitly grant those capabilities using the command-line interface.

Command-Line Syntax and Normalization

The container runtime exposes two primary flags for capability management, defined in Sources/Services/ContainerAPIService/Client/Flags.swift (lines 31-41):

  • --cap-add <cap>: Adds a capability to the container's effective set
  • --cap-drop <cap>: Removes a capability from the default set

Both flags accept capability names in a flexible format. The parser in Sources/Services/ContainerAPIService/Client/Parser.swift (lines 1009-1024) automatically normalizes input by converting strings to uppercase and prepending CAP_ if missing. For example, net_admin, NET_ADMIN, and CAP_NET_ADMIN are all valid representations of the same capability.

The parser validates each capability against the CapabilityName enum (lines 1019-1022 in Parser.swift). If you provide an invalid capability name, the command aborts immediately with an "invalid argument" error.

Processing Order and the ALL Token

Capability modifications follow a specific processing sequence that affects the final permission set. According to the documentation in docs/how-to.md (lines 98-100), the runtime processes all --cap-drop entries first, then applies all --cap-add entries. This ordering means that adds override drops when both flags target the same capability or use the ALL token.

The special token ALL represents the complete capability set:

  • --cap-add ALL: Grants every available Linux capability
  • --cap-drop ALL: Removes all capabilities, starting from an empty set

Because drops are processed before adds, the combination --cap-drop ALL --cap-add ALL results in a container with full capabilities, not an empty set.

Practical Usage Examples

Granting Additional Capabilities

Add specific capabilities when your application requires elevated privileges beyond the defaults:


# Add network administration capability

container run --cap-add NET_ADMIN alpine ip link set lo down

# Add multiple capabilities for user/group management

container run --cap-add SETUID --cap-add SETGID alpine id

Removing Default Capabilities

Drop specific capabilities to further restrict container permissions:


# Remove the chown capability to prevent ownership changes

container run --cap-drop CHOWN alpine sh -c 'touch /tmp/file'

Implementing Least Privilege

Start with no capabilities and add only what is necessary:


# Drop all defaults, then add back only specific requirements

container run --cap-drop ALL --cap-add SETUID --cap-add SETGID alpine id

Using Deny-List Approach

Grant all capabilities except specific restricted ones:


# Give all capabilities except network administration

container run --cap-add ALL --cap-drop NET_ADMIN alpine sh

Understanding Order Effects

Demonstrate how processing order affects the final capability set:


# Results in ALL capabilities because add is processed after drop

container run --cap-drop ALL --cap-add ALL alpine sh

Implementation Details

The capability management system spans several key source files:

Summary

  • The apple/container runtime runs workloads in lightweight VMs with a reduced default capability set including CAP_NET_RAW and CAP_SETUID.
  • Use --cap-add to grant capabilities and --cap-drop to remove them; both support case-insensitive names with optional CAP_ prefixes.
  • The parser validates capabilities against the CapabilityName enum in Parser.swift and aborts on invalid input.
  • Processing order matters: All --cap-drop entries are processed before --cap-add entries, meaning additions override removals.
  • The ALL token represents the complete capability set and can be used with either flag to grant or remove every capability.

Frequently Asked Questions

What capabilities are included in the default set?

The default capability set includes commonly required permissions such as CAP_NET_RAW for raw socket access and CAP_SETUID for changing user IDs, but excludes administrative capabilities like CAP_NET_ADMIN or CAP_SYS_ADMIN. The complete list is documented in docs/how-to.md lines 70-76 of the apple/container repository.

Can I use lowercase when specifying capability names?

Yes. The parser automatically normalizes capability names to uppercase and adds the CAP_ prefix if you omit it. You can specify net_admin, NET_ADMIN, or CAP_NET_ADMIN interchangeably, and the parser in Sources/Services/ContainerAPIService/Client/Parser.swift will handle them identically.

What happens if I combine --cap-add and --cap-drop in the same command?

The runtime processes all --cap-drop entries first, then applies all --cap-add entries. This means that if you specify --cap-drop ALL --cap-add NET_ADMIN, the container starts with no capabilities, then gains only CAP_NET_ADMIN. Conversely, --cap-drop ALL --cap-add ALL results in a container with all capabilities because the final add operation restores the full set.

How do I run a container with absolutely no capabilities?

Use the ALL token with --cap-drop to start from an empty capability set. The command container run --cap-drop ALL alpine sh creates a container with no Linux capabilities, providing the maximum security isolation. You can then selectively add back specific capabilities using --cap-add if your workload requires them.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →