AppArmor (Application Armor) is a Mandatory Access Control (MAC) system implemented as a major LSM (Linux Security Module), developed originally by Immunix and now maintained by Canonical. It is the default MAC system on Ubuntu, Debian, and their derivatives, and the default container confinement mechanism for containerd and Docker on those distributions. Where SELinux assigns security labels to every object on the system and enforces policy based on label interactions, AppArmor takes a fundamentally different approach: it confines programs by filesystem path. A profile for nginx lists the specific file paths that nginx is allowed to read, write, and execute, the network operations it may perform, and the Linux capabilities it may use — anything not listed is denied. No relabelling of the filesystem is required and no extended attributes are set: AppArmor’s confinement decisions are made purely from the path of the file being accessed and the identity of the confined process. This path-based model makes AppArmor profiles far simpler to read, write, and audit than SELinux policy, and eliminates the mislabelled-file failure mode that is the most common SELinux operational problem.
An AppArmor profile is a text file, typically stored in /etc/apparmor.d/, that specifies confinement rules for a single program identified by its executable path. Profile syntax covers: file rules (path patterns, optionally using globbing, with a permission set — r read, w write, x execute, m memory-map, k lock, l link — for example /var/log/nginx/** rw); capability rules (which Linux capabilities the process may use, e.g. capability net_bind_service); network rules (address families and socket types permitted, e.g. network inet tcp); signal rules (which signals the confined process may send and to which domains); and mount rules (which filesystem mounts are permitted). Profile transitions allow a confined process to execute a child process in a different profile, or to transition to a sub-profile for fine-grained control over helper programs. Profiles operate in one of two modes: enforce (violations are blocked and logged to the kernel audit subsystem, visible via dmesg and journalctl) and complain (violations are logged but not blocked, used to profile new applications and tune profiles before enforcement). AppArmor profiles are loaded into the kernel via apparmor_parser and the status of all loaded profiles is visible via aa-status.
In Kubernetes and container environments, AppArmor profiles must be pre-loaded on each node — they are not distributed with the workload manifest — and are referenced in a pod spec via securityContext.appArmorProfile (Kubernetes 1.30+, previously via annotation). Container runtimes ship a built-in docker-default / cri-containerd.apparmor.d profile that blocks the most dangerous operations (writes to /proc/sys, mount, ptrace of processes outside the container, several dangerous capabilities) while permitting everything a typical containerised application needs; this profile is applied automatically unless overridden. Custom profiles on Kubernetes nodes are typically deployed via a DaemonSet or the Security Profiles Operator. The fundamental limitation of AppArmor’s path-based model is bind mounts and overlayfs: a file accessed via a different path than the profile expects — common with container volume mounts and overlay filesystem upper layers — may not match the profile’s path rules and will either be incorrectly allowed or denied. SELinux does not have this problem because its labels are stored on the inode, not derived from the path. In practice, this means AppArmor profiles for containers require careful attention to the paths the container runtime constructs inside the overlay filesystem (/run/containerd/..., overlay upper dirs) to avoid both over-permission and spurious denials. BPF LSM can be stacked alongside AppArmor on the same system to add programmatic per-workload policy on top of AppArmor’s profile-based confinement without replacing it.
