appArmorProfile field is stable and should be used instead of the legacy annotation-based approach.
Prerequisites

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, runaa-status to confirm AppArmor is enabled and the expected profiles are present:
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:securityContext under the Pod spec. Use type: Localhost and localhostProfile for profiles that are loaded locally on nodes:
type: Localhostindicates 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 iscontainer.apparmor.security.beta.kubernetes.io/<container-name>.
appArmorProfile fields
Create and test the Pod
- Save the stable manifest above to a file (for example
ubuntu-sleeper.yaml). - Create the Pod and confirm it starts:
- Inspect pod logs to confirm the container started correctly:
- Exec into the container and verify the deny-write behavior (if using a deny-write profile):
Troubleshooting
- Confirm the
localhostProfilename 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:
dmesgor 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.