Debian ships with AppArmor enabled by default, but some hardening and compliance workflows still expect SELinux and its tighter policy controls around services, files, and network ports. To install SELinux on Debian, you do more than add packages: you switch the active MAC workflow, relabel the filesystem, and test services before moving into enforcing mode.
The same package set works on Debian 13, 12, and 11 on server and desktop installs alike. After activation, you can tune the operating mode, label custom web content, inspect denials, and roll back to AppArmor cleanly if the host is not ready for permanent SELinux enforcement.
Install SELinux on Debian
Switching from AppArmor to SELinux changes how Debian enforces mandatory access controls, so the install flow starts with AppArmor, then moves into package installation, boot activation, and a first reboot for relabeling.
Disable AppArmor Before Installing SELinux on Debian
Check AppArmor first so you know whether the service is still active on this host.
sudo systemctl status apparmor
When AppArmor is active, the status output includes lines like these.
apparmor.service - Load AppArmor profiles
Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; preset: enabled)
Active: active (exited) since ...
Disabling AppArmor removes its profile enforcement until you enable it again. If this Debian system relies on custom AppArmor profiles today, back them up before you switch to SELinux.
Disable AppArmor before you activate SELinux on Debian.
sudo systemctl disable apparmor --now
Confirm that AppArmor is no longer active before you move on.
sudo systemctl status apparmor --no-pager
apparmor.service - Load AppArmor profiles
Loaded: loaded (/lib/systemd/system/apparmor.service; disabled; preset: enabled)
Active: inactive (dead)
Install SELinux Packages on Debian
Refresh the package index, then install the base SELinux tools, the default policy, and the Python helpers used by semanage and audit2allow.
sudo apt update
If your account does not already have sudo access, add a user to sudoers on Debian before you continue.
sudo apt install -y policycoreutils selinux-utils selinux-basics selinux-policy-default auditd policycoreutils-python-utils
The package set is the same on Debian 13, 12, and 11. policycoreutils-python-utils provides semanage and audit2allow, while auditd records AVC denials for later troubleshooting.
Check that the expected SELinux command files are present after the package install.
dpkg -L policycoreutils policycoreutils-python-utils | grep -E '/sestatus$|/semanage$'
/usr/bin/sestatus /usr/sbin/semanage
On some minimal Debian installs, /usr/sbin may not be in a regular user’s PATH. If semanage returns command not found, rerun it with sudo semanage or the full /usr/sbin/semanage path.
Activate SELinux on Debian
Run the activation script once so Debian adds the SELinux boot parameters and prepares the relabel on the next restart.
sudo selinux-activate
Successful output resembles:
Activating SE Linux Generating grub configuration file ... Found linux image: /boot/vmlinuz-6.x.x-amd64 Found initrd image: /boot/initrd.img-6.x.x-amd64 done SE Linux is activated. You may need to reboot now.
Restart after the script finishes. The first SELinux boot can take several minutes because Debian relabels the filesystem before it hands control back to the system.
sudo reboot

Verify SELinux Status on Debian
After the host comes back, confirm that SELinux is enabled and still in permissive mode.
sudo sestatus
SELinux status: enabled SELinuxfs mount: /sys/fs/selinux Loaded policy name: default Current mode: permissive Mode from config file: permissive
Permissive mode is expected on the first boot. That gives you a safe place to collect denials before you move into enforcement.
Configure SELinux on Debian
SELinux has three operating modes, and the right one depends on how far you are through testing.
- Enforcing: blocks and logs policy violations. Use this after the host and its services are labeled correctly.
- Permissive: logs denials without blocking them. This is the safest place to start after the first reboot.
- Disabled: turns SELinux off entirely. Re-enabling it later requires another relabel cycle.
Edit the SELinux Configuration File on Debian
Open the main SELinux configuration file before you choose the permanent mode.
sudo nano /etc/selinux/config
Set SELINUX to the mode you want Debian to use after the next reboot.
Use enforcing mode once your services run cleanly under permissive mode.
SELINUX=enforcing
Keep permissive mode if you still need to watch denials without blocking traffic.
SELINUX=permissive
Use disabled only when you are removing SELinux or intentionally backing out of the migration.
SELINUX=disabled
Apply SELinux Mode Changes on Debian
Restart Debian after you save the configuration so the new mode is loaded at boot.
sudo reboot
Verify SELinux Mode After Reboot on Debian
Check the active mode after the host comes back.
sudo getenforce
When enforcing mode is active, the command returns:
Enforcing
When permissive mode is active, the command returns:
Permissive
Configure SELinux Policy Settings on Debian
Most Debian systems only need two policy lines beyond the main mode setting in /etc/selinux/config.
SETLOCALDEFS=0keeps Debian on the policy-provided file contexts unless you deliberately add local overrides withsemanage.SELINUXTYPE=defaultloads Debian’s standard policy package. Usemlsonly when your environment truly requires multi-level security labels.
SETLOCALDEFS=0
SELINUXTYPE=default
Debian uses
defaultas the standard SELinux policy name. Do not change that line totargeted; that Red Hat naming does not exist in Debian’s policy packages.
Configure SELinux for Web Servers on Debian
Custom document roots and non-standard ports are common SELinux adjustments after you install Apache on Debian or install Nginx on Debian.
Check the HTTP ports that SELinux already allows before you add a new one.
sudo semanage port -l | grep http_port_t
http_port_t tcp 80, 443, 488, 8008, 8009, 8443, 8448
Add port 8080 to the HTTP port type if your service listens there.
sudo semanage port -a -t http_port_t -p tcp 8080
If the command returns
Port tcp/8080 already defined, Debian already has a label on that port. Modify the existing label instead of adding a second one:sudo semanage port -m -t http_port_t -p tcp 8080.
Check the list again so you can confirm that 8080 now appears under http_port_t.
sudo semanage port -l | grep http_port_t
http_port_t tcp 80, 443, 488, 8008, 8009, 8080, 8443, 8448
Use a file-context rule when the web content lives outside Debian’s default document root.
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"
Restore the context on existing files after you add the rule.
sudo restorecon -Rv /srv/www
Relabeled /srv/www from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
Troubleshoot SELinux on Debian
Most SELinux problems on Debian come down to missing labels, denied network access, or services that were moved outside their default paths. If the host also needs perimeter controls, install UFW on Debian and install Fail2Ban on Debian so the firewall and intrusion-response side stays in step with the policy work.
Restore Default SELinux File Contexts on Debian
Reset labels first when a service breaks after you move files or copy content into a path that SELinux already knows how to manage.
sudo restorecon -Rv /var/www/html
When files need relabeling, the output resembles:
Relabeled /var/www/html from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
If no files need changes, restorecon finishes without printing anything.
Test SELinux with Permissive Mode on Debian
Switch to permissive mode temporarily when you need to confirm that SELinux is the source of a denial.
sudo setenforce 0
Verify the mode change before you retest the application.
sudo getenforce
Permissive
If the application works in permissive mode, inspect the audit logs and then switch back to enforcing once you have fixed the policy issue.
sudo setenforce 1
Check the mode again after you re-enable enforcement.
sudo getenforce
Enforcing
Review SELinux Audit Logs on Debian
Start with auditd because Debian writes SELinux denials into /var/log/audit/audit.log.
sudo systemctl status auditd --no-pager
auditd.service - Security Auditing Service
Loaded: loaded (/lib/systemd/system/auditd.service; enabled; preset: enabled)
Active: active (running) since ...
Query the recent AVC records once the service is running.
sudo ausearch -m AVC,USER_AVC -ts recent
Use a narrower log search when you want a quick look at the most recent denials. For more pattern-matching options, use grep command in Linux as a reference while you work through larger audit logs.
sudo grep 'denied' /var/log/audit/audit.log | tail -20
An AVC denial entry looks like this.
type=AVC msg=audit(1234567890.123:456): avc: denied { read } for pid=1234 comm="httpd" name="index.html" dev="sda1" ino=123456 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file permissive=0
The process name, requested action, and source and target contexts usually tell you whether the fix belongs in a file label, a boolean, or a custom policy module.
Create Custom SELinux Policy Modules on Debian
Generate a local module only after you understand the denial and decide that the access should be allowed permanently.
sudo grep 'denied' /var/log/audit/audit.log | audit2allow -M mycustommodule
sudo semodule -i mycustommodule.pp
Only the grep part needs sudo because the audit log is root-readable. audit2allow writes the module files into your current working directory, and semodule loads the finished policy with root privileges.
Verify that Debian loaded the module after you install it.
sudo semodule -l | grep mycustommodule
mycustommodule
Review the generated .te file before you load it on production systems so you are not granting broader access than the service actually needs.
Manage SELinux Booleans on Debian
Booleans are the quickest way to toggle common policy behaviors without writing a custom module.
sudo getsebool -a | grep httpd
httpd_can_network_connect --> off httpd_can_network_connect_db --> off httpd_can_sendmail --> off httpd_enable_cgi --> on
Enable the boolean you need with the -P flag so the change survives reboots.
sudo setsebool -P httpd_can_network_connect 1
Check the result after you change it.
sudo getsebool httpd_can_network_connect
httpd_can_network_connect --> on
Remove SELinux from Debian
Back the system out of SELinux in three stages: disable enforcement, remove the SELinux boot parameters, and purge the packages before you bring AppArmor back.
Disable SELinux on Debian Before Removal
Move SELinux into permissive mode first so you are not removing packages while policy enforcement is still active.
sudo setenforce 0
Edit the main configuration file and change the permanent mode to disabled.
sudo nano /etc/selinux/config
SELINUX=disabled
Remove SELinux Kernel Parameters on Debian
Use Debian’s deactivation helper instead of editing GRUB by hand.
sudo selinux-activate disable
Successful output resembles:
Deactivating SE Linux Generating grub configuration file ... done SE Linux is deactivated. You may need to reboot now.
selinux-activate disableremoves the SELinux boot parameters from GRUB. That is more reliable than editing/etc/default/grubby hand and hoping nothing gets left behind.
Remove SELinux Packages on Debian
Purge the SELinux packages after the configuration and boot parameters are in place.
sudo apt remove --purge -y selinux-basics selinux-policy-default policycoreutils policycoreutils-python-utils selinux-utils auditd
Clean up any dependencies that were pulled in only for SELinux administration.
sudo apt autoremove --purge -y
Verify SELinux Removal on Debian
Re-enable AppArmor before the reboot if you want Debian’s default MAC layer back immediately.
sudo systemctl enable apparmor
Restart once the cleanup is finished.
sudo reboot
After Debian comes back, refresh APT metadata and confirm that the SELinux packages are no longer installed.
sudo apt update
apt-cache policy selinux-basics selinux-utils policycoreutils
selinux-basics: Installed: (none) Candidate: 0.x.x selinux-utils: Installed: (none) Candidate: 3.x.x policycoreutils: Installed: (none) Candidate: 3.x.x
The candidate version numbers vary by Debian release. The important removal check is that each package shows Installed: (none).
If you re-enabled AppArmor, confirm that the service is active again after the reboot.
sudo systemctl status apparmor --no-pager
apparmor.service - Load AppArmor profiles
Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; preset: enabled)
Active: active (exited) since ...
SELinux on Debian FAQ
No. Debian uses AppArmor by default, and SELinux is an optional hardening framework that you install and activate separately. If you switch to SELinux, disable AppArmor first so you are not managing two different policy workflows on the same host.
Yes. Debian 13, 12, and 11 use the same package names and the same activation flow. Package versions differ by release, but the install, reboot, verification, and rollback commands stay the same.
The semanage command comes from policycoreutils-python-utils. If it is missing, install or reinstall that package with sudo apt install policycoreutils-python-utils. On minimal Debian installs, you may also need to run the command with sudo because it lives in /usr/sbin.
Yes. Set SELINUX=disabled in /etc/selinux/config, run sudo selinux-activate disable, purge the SELinux packages, and reboot. If you want Debian’s default MAC layer back immediately, re-enable AppArmor before that final reboot with sudo systemctl enable apparmor.
Conclusion
SELinux is installed on Debian and ready for permissive testing or a move to enforcing mode once your labels and booleans match the services you run. To keep the host hardened after the initial policy work, configure unattended upgrades on Debian for routine patches and install Fail2Ban on Debian if the server also needs automated response to brute-force traffic.
#Install Package Dependencies
sudo apt install selinux-basics selinux-policy-default selinux-policy-src selinux-policydoc auditd locate
Thanks for the suggestion, hmidani. You were right that
auditdis essential for troubleshooting. The article didn’t include it when you commented in April. The guide has been updated to includeauditdalong withpolicycoreutils-python-utils.The updated command is:
Thanks for helping improve the guide.