How to fix AppArmor profile error in Kubernetes

KubernetesADVANCEDHIGHUpdated Dec 6, 2025· 125 views

The "AppArmor profile error" occurs when a Kubernetes Pod references an AppArmor profile that is not loaded on the node. This error is caused by missing profile definitions, profile loading failures, or API version mismatches between how the profile is specified and what the cluster supports.

Quick fix

Fixes AppArmor profile error

kubectl version --short
  1. Check Kubernetes version and AppArmor API format
  2. Verify AppArmor is enabled on the node
  3. Check if the specific profile exists on the node
  4. Create a test AppArmor profile if missing
kubernetes — error
kubectl version --short
AppArmor profile error

What this error means

When Kubernetes attempts to enforce an AppArmor security profile on a container, it requires that profile to be pre-loaded on the node's kernel. If the profile name doesn't exist or hasn't been loaded, the kubelet rejects the Pod and prevents it from starting. AppArmor is a Linux kernel security module that restricts programs' capabilities at the OS level. Kubernetes integrates with AppArmor to confine container processes, but unlike other security mechanisms, AppArmor profiles are not automatically deployed—they must exist on every node where Pods using them will run. The error manifests differently depending on your Kubernetes version: prior to v1.30, AppArmor was specified via Pod annotations and would fail with "Cannot enforce AppArmor"; in v1.30+, it's specified in securityContext fields using the appArmorProfile API.

Typical symptoms

  • Pod status: Failed with error message "Cannot enforce AppArmor: profile [...] is not loaded"
  • Pod stuck in Pending state and never reaches Running
  • Events show: "AppArmor profile [...] is not available on this host"
  • Nodes show different AppArmor status (some have profile, some don't)
  • Older manifests with container.apparmor.security.beta.kubernetes.io annotations fail in Kubernetes v1.30+
  • Upgrade to Kubernetes v1.30 breaks existing AppArmor Pod specifications
  • Custom AppArmor profiles work on some nodes but not others in heterogeneous clusters

Common causes

  1. AppArmor profile not loaded on the node: profiles must be pre-installed in /etc/apparmor.d/ and loaded via `apparmor_parser`
  2. Profile loading failed during node initialization: script errors, missing profile files, or syntax errors in the profile definition
  3. Profile name typo in Pod spec: AppArmor profile names are case-sensitive and must match exactly
  4. Kubernetes v1.30 API change: AppArmor annotation syntax changed from `container.apparmor.security.beta.kubernetes.io/...` to `securityContext.appArmorProfile`
  5. Profile only loaded on subset of nodes: cluster uses node pool setup where profiles aren't distributed uniformly
  6. AppArmor disabled on the node: AppArmor module not loaded in kernel or disabled via kernel boot parameters
  7. Conflicting profile definitions: multiple AppArmor policies loaded with overlapping names causing profile rejection
  8. Trying to use a profile defined only in container runtime instead of system AppArmor

How to fix "AppArmor profile error"

1Check Kubernetes version and AppArmor API format

First, identify which AppArmor specification method your cluster uses:

bash
kubectl version --short

Kubernetes v1.30+ uses securityContext.appArmorProfile:

yaml
containers:
- name: app
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: my-profile

Pre-v1.30 used Pod annotations (now deprecated):

yaml
metadata:
  annotations:
    container.apparmor.security.beta.kubernetes.io/app: localhost/my-profile

If upgrading from v1.29 to v1.30+, update your manifests to use the new securityContext format.

2Verify AppArmor is enabled on the node

SSH into the node and check if AppArmor is active:

bash
sudo systemctl status apparmor
sudo aa-status  # Lists loaded profiles

If AppArmor is not running:

bash
sudo systemctl start apparmor
sudo systemctl enable apparmor  # Enable on boot

Verify the module is loaded in the kernel:

bash
ls -la /sys/kernel/security/apparmor/

If AppArmor is not available, enable it via kernel boot parameters (requires node restart):

bash
# Edit GRUB config (Ubuntu/Debian)
sudo nano /etc/default/grub
# Find GRUB_CMDLINE_LINUX and add apparmor=1 security=apparmor
# Run: sudo update-grub && sudo reboot
3Check if the specific profile exists on the node

View all loaded AppArmor profiles on the node:

bash
sudo aa-status | grep -i profile
sudo cat /sys/kernel/security/apparmor/profiles

Check if your profile file exists:

bash
sudo ls -la /etc/apparmor.d/
sudo cat /etc/apparmor.d/my-profile  # View the profile definition

If the profile file exists but isn't loaded, manually load it:

bash
sudo apparmor_parser -r /etc/apparmor.d/my-profile
sudo aa-status | grep my-profile

If it fails to load, there's a syntax error in the profile. Check logs:

bash
sudo dmesg | tail -20
sudo journalctl -xe | grep -i apparmor
4Create a test AppArmor profile if missing

If the profile doesn't exist, create a basic one. On each node:

bash
sudo cat > /etc/apparmor.d/k8s-test-profile << 'EOF'
#include <tunables/global>

profile k8s-test-profile flags=(attach_disconnected) {
  #include <abstractions/base>
  
  /bin/sh rix,
  /bin/bash rix,
  /bin/ls rix,
  /bin/cat rix,
  /bin/echo rix,
  /dev/null rw,
  /dev/zero rw,
  /dev/full rw,
  /dev/random r,
  /dev/urandom r,
}
EOF

Load it:

bash
sudo apparmor_parser -r /etc/apparmor.d/k8s-test-profile
sudo aa-status | grep k8s-test-profile

Now reference it in your Pod:

yaml
containers:
- name: app
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: k8s-test-profile
5Use a DaemonSet to distribute profiles to all nodes

For cluster-wide consistency, deploy a DaemonSet that loads profiles on every node. Create a script directory with your profiles:

bash
# profiles/k8s-apparmor-profile
#include <tunables/global>
profile k8s-apparmor-profile flags=(attach_disconnected) {
  #include <abstractions/base>
  ...
}

Create a DaemonSet:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: apparmor-loader
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: apparmor-loader
  template:
    metadata:
      labels:
        name: apparmor-loader
    spec:
      hostNetwork: true
      hostPID: true
      containers:
      - name: loader
        image: ubuntu:22.04
        securityContext:
          privileged: true
        volumeMounts:
        - name: profiles
          mountPath: /etc/apparmor.d
          readOnly: true
        - name: sys
          mountPath: /sys
        command:
        - /bin/bash
        - -c
        - |
          apt-get update && apt-get install -y apparmor-utils
          for profile in /etc/apparmor.d/*; do
            apparmor_parser -r "$profile" || echo "Failed to load $profile"
          done
          sleep 1000000
      volumes:
      - name: profiles
        configMap:
          name: apparmor-profiles
      - name: sys
        hostPath:
          path: /sys
6Verify Pod can reference the profile

After ensuring the profile exists on the node, deploy a test Pod:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: apparmor-test
spec:
  containers:
  - name: app
    image: busybox
    securityContext:
      appArmorProfile:
        type: Localhost
        localhostProfile: k8s-test-profile
    command: ["sleep", "3600"]

Apply and check status:

bash
kubectl apply -f apparmor-test.yaml
kubectl describe pod apparmor-test
kubectl logs apparmor-test  # Check for errors

If successful, the Pod will run with the AppArmor profile enforced. If it fails, check events:

bash
kubectl get events --sort-by='.lastTimestamp' | grep apparmor
7Debug with system logs if profile still fails

If the Pod still can't load the profile, check system logs on the node:

bash
# SSH into the node
ssh <node-ip>

# Check kubelet logs
sudo journalctl -u kubelet -n 50 | grep -i apparmor

# Check AppArmor denied messages
sudo dmesg | grep -i apparmor
sudo tail -f /var/log/audit/audit.log | grep apparmor

# Check if profile syntax is invalid
sudo apparmor_parser -d /etc/apparmor.d/my-profile 2>&1

Common issues:
- Profile has syntax errors: Fix the profile definition
- Profile name in Pod doesn't match loaded profile: Ensure exact spelling match
- AppArmor module not loaded: Load via sudo modprobe apparmor
- SELinux conflicts (on some distros): Disable SELinux or set to permissive mode

8Use RuntimeDefault as fallback if custom profiles unavailable

If you can't get custom profiles working, use the runtime's default profile:

yaml
containers:
- name: app
  securityContext:
    appArmorProfile:
      type: RuntimeDefault

Or in older annotation format:

yaml
metadata:
  annotations:
    container.apparmor.security.beta.kubernetes.io/app: runtime/default

RuntimeDefault applies the container runtime's built-in security policy (usually permissive). This is less restrictive than custom profiles but still provides baseline security. Once your cluster infrastructure is set up to distribute custom profiles via DaemonSet, you can switch to Localhost profiles.

Advanced notes

AppArmor profile management in Kubernetes requires cluster-level coordination. For production clusters, use a profile distribution tool like the Kubernetes Security Profiles Operator (kube-spo) which simplifies profile deployment. If your nodes run different distributions (Ubuntu, Debian, RHEL), profile syntax may vary slightly—test thoroughly. Rootless Kubernetes clusters may have different AppArmor constraints due to unprivileged namespace limitations. In multi-cluster setups, maintain profile consistency via IaC tools (Terraform, Ansible) to avoid cross-cluster AppArmor mismatches. For CI/CD pipelines, ensure your container build process doesn't strip required AppArmor capabilities from the image. AppArmor profiles are node-local and not portable—the same profile must be loaded identically on every node where Pods using it will run. Consider using Pod node affinity to ensure Pods with AppArmor requirements run only on nodes with those profiles loaded.

Frequently asked questions

What does "AppArmor profile error" mean?
When Kubernetes attempts to enforce an AppArmor security profile on a container, it requires that profile to be pre-loaded on the node's kernel. If the profile name doesn't exist or hasn't been loaded, the kubelet rejects the Pod and prevents it from starting. AppArmor is a Linux kernel security module that restricts programs' capabilities at the OS level.
How do I fix "AppArmor profile error"?
The "AppArmor profile error" occurs when a Kubernetes Pod references an AppArmor profile that is not loaded on the node. This error is caused by missing profile definitions, profile loading failures, or API version mismatches between how the profile is specified and what the cluster supports.
What causes "AppArmor profile error"?
AppArmor profile not loaded on the node: profiles must be pre-installed in /etc/apparmor.d/ and loaded via apparmor_parser Profile loading failed during node initialization: script errors, missing profile files, or syntax errors in the profile definition Profile name typo in Pod spec: AppArmor profile names are case-sensitive and must match exactly
Is the "AppArmor profile error" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading