aa-remove-unknown clears AppArmor profiles that stay loaded in the kernel after their file vanishes from /etc/apparmor.d/. Delete a profile by hand, remove a package, or roll back a deployment, and you can end up with exactly that: a ghost policy with nothing on disk behind it. Budget about ten minutes for a careful review, not a quick run. You need the AppArmor utilities package, a shell, and root for the inventory and removal steps.
This guide covers the installed AppArmor package version 4.0.1really4.0.1-0ubuntu0.24.04.7; the local manual identifies the utility itself as AppArmor 4.0.1. The command changes loaded kernel policy. It does not touch anything under /etc/apparmor.d/.
/etc/apparmor.d/, and profile names currently loaded into the kernel.$ command -v aa-remove-unknown
/usr/sbin/aa-remove-unknown
$ aa-remove-unknown --help
usage: /usr/sbin/aa-remove-unknown [options]
Remove profiles unknown to the system
Options:
-h, --help Show this help message and exit
-n Dry run; don't remove profiles
Checkpoint: stop here if you expected this to delete obsolete files, disable AppArmor altogether, or edit policy text. It does none of those things.
The -n option only prints what would be removed, so start there every time:
$ sudo aa-remove-unknown -n
Would remove 'example//null-/usr/bin/whoami'
Would remove 'example'
That output is the manual's sample, not a forecast of yours: what you see depends entirely on the profiles loaded on your host. Empty output is a good result too, it just means nothing loaded is missing from disk.
An unprivileged run cannot read /sys/kernel/security/apparmor/profiles and exits with an error. That is a permissions failure, not proof of a clean system. Use sudo, then check the exit status rather than trusting the absence of text on screen:
$ sudo aa-remove-unknown -n
$ status=$?
$ printf 'dry-run status: %s\n' "$status"
dry-run status: 0
A successful dry run finishes without an AppArmor filesystem error. If it complains that it cannot read the loaded-profile list, stop and fix that before you go anywhere near removal.
Before touching kernel state, chase down each candidate. Start by listing what actually exists on disk:
$ sudo find /etc/apparmor.d -maxdepth 1 -type f -printf '%f\n' | sort
$ sudo aa-remove-unknown -n
$ dpkg-query -S /etc/apparmor.d/PROFILE_FILE
Swap PROFILE_FILE for a real filename from the directory. If a package owns it, this becomes a packaging or service-management decision, not a job for this tool: the comparison is mechanical and has no idea whether a profile is still needed.
Safety warning: removal can weaken confinement for a process that is still running or due to start again. Do not wave a name through just because its file is hard to find. Resolve ownership and lifecycle first.
Once every flagged name is understood and the maintenance window allows a policy change, drop -n:
$ sudo aa-remove-unknown
Removing 'example//null-/usr/bin/whoami'
Removing 'example'
It reports each profile it removes. There is no flag to narrow this to one profile: it always acts on the full comparison. That is exactly why steps 2 and 3 matter so much.
Checkpoint: save this output with your change record. If it lists a profile you never approved, stop making further changes and preserve the evidence.
There is no undo flag. The command never touches /etc/apparmor.d/, so recovery means restoring or locating the intended profile file, not recreating one from memory:
$ sudo apparmor_parser --replace /etc/apparmor.d/PROFILE_FILE
--replace as replacing an AppArmor definition.Permission denied: the utility could not read the AppArmor profiles interface. Rerun with the privilege your host requires and confirm AppArmor's securityfs is mounted. Do not read this error as a clean inventory.
Unexpected names: stop before removing anything. Match the name against a profile file and its owning service or package; namespace-qualified names rarely look like the filename you expect.
Parser or profile errors: fix the profile source first. A broken policy inventory is no basis for unloading anything. Rerun the dry run once the source is fixed.
No output: normal, and it means nothing loaded is unknown, but only when the command also exits successfully. Capture the exit status immediately if you're scripting this.
aa-remove-unknown and AppArmor version.-n only once you'd cleared it.