Installing SELinux on Debian replaces AppArmor with mandatory access controls (MAC) that restrict how processes, users, and applications interact with files, devices, and network ports. Unlike discretionary access controls where users manage their own permissions, SELinux enforces system-wide policies that even root cannot bypass without proper authorization.
Common use cases for SELinux on Debian include hardening web servers to contain compromised services, meeting compliance requirements for PCI-DSS or HIPAA environments, and isolating multi-tenant applications. By the end of this guide, you will have SELinux installed and configured on Debian with the default policy active. Additionally, you will understand how to switch between enforcement modes and know how to troubleshoot common policy violations using audit logs and custom modules.
Disable AppArmor Before Installing SELinux
Debian ships with AppArmor as the default Linux Security Module (LSM). However, the kernel can only load one LSM at a time, so you must disable AppArmor before enabling SELinux.
Disabling AppArmor removes its protection from confined applications. If you decide to revert to AppArmor later, you will need to re-enable and reboot. Consider backing up any custom AppArmor profiles before proceeding.
Check if AppArmor is currently active:
sudo systemctl status apparmor
When AppArmor is active, you should see the following output:
● apparmor.service - Load AppArmor profiles
Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; preset: enabled)
Active: active (exited) since ...
Main PID: ... (code=exited, status=0/SUCCESS)
If AppArmor is running, you must deactivate it before installing SELinux. Therefore, use the following command to disable AppArmor:
sudo systemctl disable apparmor --now
Install SELinux Packages
First, update your package index to ensure you install the latest available versions:
sudo apt update
Then, install the core SELinux packages along with the default reference policy:
sudo apt install policycoreutils selinux-utils selinux-basics selinux-policy-default auditd policycoreutils-python-utils
This command installs the SELinux utilities, activation scripts, the default policy (which functions like a targeted policy, confining specific daemons rather than all processes), the audit daemon for logging policy violations, and the Python utilities needed for audit2allow and semanage commands.
Activate SELinux
Run the activation script to configure GRUB and prepare SELinux for the next boot:
sudo selinux-activate
The activation script produces the following output:
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.
Next, reboot to complete the activation:
sudo reboot
The first boot after enabling SELinux takes significantly longer than normal. The system must relabel every file on your filesystem with the correct security context. On a typical installation this can take 5-15 minutes; do not interrupt the process.

Verify SELinux Status
Once the system reboots, verify that SELinux is active:
sestatus
A properly configured system displays the following output:
SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: default Current mode: permissive Mode from config file: permissive Policy MLS status: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 33
SELinux starts in permissive mode by default, which logs policy violations without blocking them. As a result, this allows you to identify potential issues before switching to enforcing mode.
Configure SELinux Mode
Understanding SELinux modes is essential because SELinux operates in three modes that determine how it handles policy violations:
- Enforcing: Actively blocks and logs policy violations. Use this for production systems after testing.
- Permissive: Logs violations without blocking them. Debian defaults to this mode, allowing you to identify issues before enforcing.
- Disabled: SELinux is completely off. Not recommended as re-enabling requires a full filesystem relabel.
Edit the Configuration File
First, open the SELinux configuration file:
sudo nano /etc/selinux/config
Then, set the SELINUX directive to your preferred mode:
To enable enforcing mode (recommended for production after testing):
SELINUX=enforcing
Alternatively, to keep permissive mode (default, logs only):
SELINUX=permissive
If you need to disable SELinux completely (not recommended):
SELINUX=disabled
Apply Configuration Changes
Finally, reboot for the mode change to take effect:
sudo reboot
Verify Mode Change After Reboot
After the system reboots, confirm the mode change took effect:
getenforce
When enforcing mode is active, you will see:
Enforcing
Conversely, permissive mode displays:
Permissive
Configure Policy Settings
The /etc/selinux/config file also contains additional policy settings:
SETLOCALDEFS: Controls whether SELinux uses locally defined file contexts. The default value of 0 uses only the policy-provided contexts, which is appropriate for most systems:
SETLOCALDEFS=0
SELINUXTYPE: Additionally, this directive defines which policy to load. On Debian, the available options are:
default— The standard policy that confines specific daemons and services (recommended)mls— Multi-Level Security for environments requiring classified data handling
The installation process already configures the default policy:
SELINUXTYPE=default
Debian uses
defaultas its policy name, which functions like the “targeted” policy found on Red Hat-based distributions—it confines specific services rather than all processes. Do not change this to “targeted” as that policy type does not exist on Debian.
Configure SELinux for a Web Server
If you run a web server such as Apache or Nginx on Debian, you may need to configure SELinux to allow custom ports. You already have the semanage utility from the initial package installation.
First, check which ports are already allowed for HTTP traffic:
sudo semanage port -l | grep http_port_t
This command shows the default HTTP ports allowed by SELinux:
http_port_t tcp 80, 443, 488, 8008, 8009, 8443, 8448
To add a custom port such as 8080, use the -a flag. If the port is already defined under another type, use -m to modify it instead:
sudo semanage port -a -t http_port_t -p tcp 8080
SELinux assigns port 8080 to
http_cache_port_tby default. On Debian 11 and 12, you will see an error: “Port tcp/8080 already defined.” In this case, use-m(modify) instead of-a(add):sudo semanage port -m -t http_port_t -p tcp 8080. Debian 13 automatically converts add to modify when a port exists.
Confirm that SELinux now allows the port:
sudo semanage port -l | grep http_port_t
After adding the port, the output now includes 8080:
http_port_t tcp 8080, 80, 443, 488, 8008, 8009, 8443, 8448
Similarly, to set a custom file context for a directory (for example, allowing a web server to access content outside the default document root):
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"
Afterward, apply the context to existing files:
sudo restorecon -Rv /srv/www
The command confirms the context change with the following output:
Relabeled /srv/www from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
Troubleshoot Common SELinux Issues
SELinux denials are the most common issue administrators encounter. Consequently, the following techniques help identify and resolve policy violations. For comprehensive firewall configuration alongside SELinux, see our guides on installing UFW on Debian and configuring Fail2Ban on Debian.
Restore Default File Contexts
Incorrect file contexts are a common cause of SELinux denials. If you moved or copied files without preserving contexts, you can restore them with restorecon. For instance, to fix contexts in the default web root:
sudo restorecon -Rv /var/www/html
When restorecon relabels files, it displays output similar to:
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 needed relabeling, the command completes silently with no output.
Test with Permissive Mode
To determine whether SELinux is blocking an application, temporarily switch to permissive mode:
sudo setenforce 0
Then, verify the mode change:
getenforce
The output should confirm permissive mode:
Permissive
Test your application. If it works now, SELinux policies caused the issue—check the audit logs to identify the specific denial. Remember to switch back to enforcing mode once you have completed testing:
sudo setenforce 1
Confirm that enforcing mode is now active:
getenforce
The output should display:
Enforcing
Review SELinux Audit Logs
The audit daemon (auditd) logs all SELinux policy violations to /var/log/audit/audit.log. First, verify the audit service is running:
sudo systemctl status auditd
An active audit service displays the following:
● auditd.service - Security Auditing Service
Loaded: loaded (/lib/systemd/system/auditd.service; enabled; preset: enabled)
Active: active (running) since ...
Once confirmed, view recent denials:
sudo ausearch -m AVC,USER_AVC -ts recent
Alternatively, search the raw log for denied actions:
sudo grep 'denied' /var/log/audit/audit.log | tail -20
Example AVC denial output:
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
Each AVC denial entry shows the process (comm="httpd"), the action attempted ({ read }), and the target resource (name="index.html"). The security contexts (scontext and tcontext) reveal why SELinux denied the access. Subsequently, you can use this information to create targeted policy exceptions.
Create Custom Policy Modules with audit2allow
When standard troubleshooting does not resolve a denial, you can use audit2allow to generate a custom policy module from the audit logs.
For example, to create a custom policy for a specific issue, execute:
sudo grep 'denied' /var/log/audit/audit.log | audit2allow -M mycustommodule
sudo semodule -i mycustommodule.pp
Next, confirm that SELinux loaded the module:
sudo semodule -l | grep mycustommodule
If the module installed successfully, the output displays:
mycustommodule
Manage SELinux Booleans
In addition to custom modules, SELinux booleans provide on/off switches for specific policy features. To list all available booleans and their current state:
sudo getsebool -a | grep httpd
Example output showing HTTP-related booleans:
httpd_can_network_connect --> off httpd_can_network_connect_db --> off httpd_can_sendmail --> off httpd_enable_cgi --> on
Based on the output above, you can enable a specific boolean. For example, to allow Apache to make network connections:
sudo setsebool -P httpd_can_network_connect 1
The -P flag makes the change persist through reboots. To verify the boolean is now enabled:
getsebool httpd_can_network_connect
The output confirms the boolean is active:
httpd_can_network_connect --> on
Remove SELinux from Debian
If you need to revert to AppArmor or remove SELinux entirely, follow the steps below carefully. Improper removal can cause boot issues.
Disable SELinux Before Removal
First, set SELinux to permissive mode temporarily:
sudo setenforce 0
Next, edit the SELinux configuration to disable it permanently:
sudo nano /etc/selinux/config
Change the SELINUX line to:
SELINUX=disabled
Save the file and exit.
Remove SELinux Kernel Parameters
Use the built-in deactivation script to remove SELinux kernel parameters from GRUB:
sudo selinux-activate disable
The deactivation script produces the following output:
Deactivating SE Linux Generating grub configuration file ... done SE Linux is deactivated. You may need to reboot now.
The
selinux-activate disablecommand removes bothselinux=1andsecurity=selinuxkernel parameters from GRUB. This is more reliable than manually editing/etc/default/grub.
Remove SELinux Packages
With the kernel parameters removed, proceed to remove the SELinux packages:
sudo apt remove --purge selinux-basics selinux-policy-default policycoreutils policycoreutils-python-utils selinux-utils auditd
Afterward, remove orphaned dependencies:
sudo apt autoremove
Reboot and Verify Removal
Optionally re-enable AppArmor before rebooting:
sudo systemctl enable apparmor
Reboot to apply changes:
sudo reboot
After reboot, verify SELinux is removed:
sestatus
After you successfully remove SELinux, the output shows:
SELinux status: disabled
Alternatively, if the command is not found, SELinux has been completely removed. Finally, verify AppArmor is active if you re-enabled it:
sudo systemctl status apparmor
Conclusion
You now have SELinux configured on Debian with the default policy providing mandatory access controls. The key techniques covered include switching between enforcing and permissive modes, using semanage to configure port and file contexts, analyzing denials with ausearch, and creating custom policy modules with audit2allow. For production systems, start in permissive mode to identify violations, then switch to enforcing mode once your policies are tuned. Furthermore, consider combining SELinux with automatic security updates to maintain a hardened Debian server.
#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.