How to Add Linux Capabilities to a Container Using Apple Container

You can add Linux capabilities to a container using the --cap-add flag (e.g., container run --cap-add NET_ADMIN alpine) or remove them with --cap-drop, with processing occurring in that specific order.

Apple Container provides granular control over the Linux kernel capabilities available to your containers, moving beyond the traditional "all-or-nothing" permission model. By default, containers run with a minimal, hardened capability set defined in the runtime configuration. This guide explains how to leverage the --cap-add and --cap-drop flags to customize security permissions for specific use cases.

Understanding Linux Capabilities in Apple Container

Linux capabilities are kernel-level permissions that grant specific privileges to processes without requiring full root access. Traditional Linux processes either have all capabilities (running as root) or none, but Apple Container adopts a minimal default approach for enhanced security.

The container runtime initializes with a curated subset of capabilities optimized for general workloads. When you need additional privileges—such as CAP_NET_ADMIN for network administration or CAP_SYS_ADMIN for system-level operations—you can surgically add only what is necessary rather than running a fully privileged container.

CLI Flags for Capability Management

The Apple Container CLI exposes two primary flags for capability manipulation, implemented in Sources/Services/ContainerAPIService/Client/Flags.swift through the capAdd and capDrop string array properties.

Adding Capabilities with --cap-add

Use --cap-add to grant additional capabilities to the container's effective set. The flag accepts capability names with or without the CAP_ prefix and is case-insensitive.


# Add a specific capability

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

# Add multiple capabilities

container run --cap-add NET_RAW --cap-add SYS_ADMIN alpine sh

# Grant all capabilities (privileged mode)

container run --cap-add ALL alpine sh -c "ip link set lo down && echo ok"

Removing Capabilities with --cap-drop

Use --cap-drop to remove capabilities from the default set or from any previously added ones. This is useful for creating even more restrictive environments than the default.


# Drop a specific capability from the default set

container run --cap-drop CHOWN alpine chown 100 /tmp

# Remove all capabilities for maximum restriction

container run --cap-drop ALL alpine id

Understanding Processing Order

Capabilities are dropped before they are added. This processing order means that --cap-drop ALL --cap-add ALL results in a container with all capabilities enabled, because the drop operation occurs first, then the add operation restores full access. This behavior is documented in the Control Linux capabilities section of the project documentation.

Implementation Architecture

The capability configuration flows from the CLI through to the Linux kernel via the container runtime service. In Sources/Services/ContainerAPIService/Client/Flags.swift, the flags are defined as Swift @Option properties:

@Option(
    name: .customLong("cap-add"),
    help: .init("Add a Linux capability (e.g. CAP_NET_RAW, or ALL)", valueName: "cap")
)
public var capAdd: [String] = []

@Option(
    name: .customLong("cap-drop"),
    help: .init("Drop a Linux capability (e.g. CAP_NET_RAW, or ALL)", valueName: "cap")
)
public var capDrop: [String] = []

These values are passed to the runtime when executing container run or container create commands. The Sources/Services/RuntimeLinux/Server/RuntimeService.swift file configures the underlying Linux kernel's capability Linux Security Module (LSM) through kernel command-line arguments (lsm=lockdown,capability,…), ensuring the kernel enforces the specified capability boundaries.

Practical Examples

Combine drops and adds to create a least-privilege container that has only the specific permissions it needs:


# Drop all capabilities, then add only the specific ones required

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

# Network administration container with default set minus CHOWN

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

These flags work identically with both container run (immediate execution) and container create (pre-creation setup), as verified by the integration tests in Tests/IntegrationTests/Run/TestCLIRunCapabilities.swift.

Summary

  • Default security: Apple Container starts containers with a minimal, hardened capability set rather than granting all root privileges.
  • Granular control: Use --cap-add to grant specific kernel capabilities and --cap-drop to remove them.
  • Processing order: Drops are processed before adds, allowing patterns like --cap-drop ALL --cap-add NET_ADMIN for least-privilege networking.
  • Flexibility: Capability names are case-insensitive and work with or without the CAP_ prefix.
  • Integration: Flags are parsed in Flags.swift and enforced by the LSM configuration in RuntimeService.swift.

Frequently Asked Questions

What is the default capability set for containers?

According to the Apple Container source code, containers start with a curated list of capabilities chosen specifically for security hardening. This minimal set excludes most administrative capabilities like CAP_SYS_ADMIN or CAP_NET_ADMIN, requiring explicit opt-in through --cap-add when these permissions are necessary.

Can I use --cap-add with container create?

Yes, the --cap-add and --cap-drop flags work with both container run and container create commands. The capability configuration is stored when creating the container and applied when the container subsequently starts.

Is the capability name case-sensitive?

No, capability names are case-insensitive in Apple Container. You can specify NET_ADMIN, net_admin, Cap_Net_Admin, or use the full kernel name CAP_NET_ADMIN—all resolve to the same capability.

How does --cap-drop ALL --cap-add ALL behave?

This combination results in a container with all capabilities enabled. Because drops are processed before adds, the sequence first removes all capabilities, then immediately adds them all back. This is functionally equivalent to running a privileged container and is supported by the processing order logic implemented in the runtime.

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 →