Skip to main content
This guide shows how to apply AppArmor profiles to containers in Kubernetes. AppArmor restricts container behaviors at the kernel level, improving security by preventing unwanted file writes, execution, or other actions. As of Kubernetes v1.31 the Pod-level appArmorProfile field is stable and should be used instead of the legacy annotation-based approach.

Prerequisites

A slide titled "AppArmor in Kubernetes" showing four rounded items listing requirements. They read: "K8 Version > 1.31", "AppArmor Kernel module enabled", "AppArmor Profile loaded in the Kernel", and "Container Runtime should be supported."
Before scheduling pods that reference a local AppArmor profile, make sure the profile is loaded on every candidate node. You can load profiles using apparmor_parser or by placing profiles under /etc/apparmor.d/ and reloading AppArmor on the node.

Verify AppArmor is active and the profile is loaded

On each node, run aa-status to confirm AppArmor is enabled and the expected profiles are present:
If the profile you plan to use (for example apparmor-deny-write) does not appear in the loaded profiles list, load it on the node first.

How AppArmor profiles are attached to containers

AppArmor profiles are applied per-container. Historically (beta-era) you attached a profile via an annotation on the Pod. That legacy approach looked like this:
Modern (stable) usage: specify AppArmor via the Pod’s securityContext under the Pod spec. Use type: Localhost and localhostProfile for profiles that are loaded locally on nodes:
Notes:
  • type: Localhost indicates the profile is a local profile that must be loaded on each node.
  • When using the legacy annotation, the value is localhost/<profile-name> and the annotation key is container.apparmor.security.beta.kubernetes.io/<container-name>.

appArmorProfile fields

Create and test the Pod

  1. Save the stable manifest above to a file (for example ubuntu-sleeper.yaml).
  2. Create the Pod and confirm it starts:
Expected output:
  1. Inspect pod logs to confirm the container started correctly:
  1. Exec into the container and verify the deny-write behavior (if using a deny-write profile):
A “Permission denied” error indicates the AppArmor profile is being enforced on the node where the Pod is running.

Troubleshooting

  • Confirm the localhostProfile name exactly matches the profile loaded on each node.
  • Ensure the AppArmor kernel module is enabled on the node and the container runtime supports AppArmor enforcement.
  • Inspect node logs for denials: dmesg or the node’s syslog often show AppArmor denial messages with details about why an operation was blocked.
  • If a pod does not start or behaves unexpectedly, re-check the pod’s node placement and verify the profile exists on that specific node.

Watch Video