Dissecting the three SELinux modes — enforcing, permissive, and disabled — along with how to switch between modes safely via runtime, kernel parameters, and /etc/selinux/config configuration. Concluded with the practice of reading AVC logs to diagnose denials using ausearch.

In episode 2 we built the basic SELinux vocabulary: domain, type, security context, and architecture. Now it's time to put it into practice at the terminal. This episode will cover the three SELinux modes, how to switch between them safely and with awareness of the consequences, and basic troubleshooting — reading and diagnosing your first denials.
This is the episode that will save you most often in the real world. The majority of "why can't my application read this file?" questions on production servers have the answer right here: the wrong mode or denials that were never read.
SELinux has three modes that determine how the policy is treated:
| Mode | Behavior | AVC Log |
|---|---|---|
| Enforcing | Policy is enforced — forbidden accesses are blocked | Recorded |
| Permissive | Policy is not enforced — forbidden accesses are allowed | Recorded |
| Disabled | Policy is not loaded — SELinux is completely off | Not recorded |
The easiest way to understand it: permissive is a rehearsal without consequences — the system records what would be blocked if it were enforcing, but still lets everything run. That's why permissive is such a valuable debugging tool: you can see all denials without cutting off application access.
In disabled mode, the kernel doesn't load the policy at all — all SELinux features are off. Keep in mind: this mode is not "more relaxed than permissive", but rather severs all protection and can't be changed without a reboot.
Warning
Never make disabled your routine solution. Disabling SELinux on a production server removes the entire MAC layer you've learned about — and some applications and compliance scripts actually detect this and refuse to run. Keep SELinux enforcing; if there's a problem, use permissive only while debugging.
There are three mechanisms for switching modes, and you must understand the differences — this is often a point of comprehension testing.
setenforce changes the mode without a reboot, but only lasts until the system is restarted:
sudo setenforce 0
sudo setenforce 1setenforce 0 switches to permissive, setenforce 1 returns to enforcing. This command also accepts the Permissive and Enforcing arguments. Important note: from disabled mode, setenforce can't turn SELinux back on — the policy hasn't been loaded into the kernel.
The mode can be set at boot via the kernel parameter enforcing=0 (or selinux=0 to disable entirely). This mechanism is useful for systems that can't boot because of a problematic policy, for example via the GRUB prompt. The effect lasts until the next boot.
To change the mode permanently, edit the /etc/selinux/config file:
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targetedThe SELINUX= line determines the default mode at boot. Note: changing to disabled only takes effect after a reboot — likewise going from disabled back to enforcing. Changes to or from permissive, however, take effect at runtime too. So if you change to permissive, the mode changes immediately without a restart.
A quick summary of the differences:
| Mechanism | Takes Effect | Needs Reboot | Example |
|---|---|---|---|
setenforce 0 | Runtime | No | Temporary debugging |
enforcing=0 (kernel param) | Boot | Yes (at next boot) | System can't boot |
SELINUX=disabled | Permanent | Yes | Disabling entirely |
Two commands for checking the system's current state:
getenforceThe output is a single word: Enforcing, Permissive, or Disabled. For more complete details, use sestatus, which we got to know in episode 0 — it shows the current mode, the mode from the config file, and the loaded policy. The difference between the two: getenforce is fast and short for scripting, sestatus is complete for diagnosis.
SELinux denials are recorded in the audit log. If the auditd service is running (we enabled it in episode 0), the logs are in /var/log/audit/audit.log. If not, the messages appear in journald and can be viewed with journalctl.
To find all AVC denials, run ausearch -m avc -ts recent:
sudo ausearch -m avc -ts recentThe output consists of AVC lines like the one we saw in episode 1. Here's the reading pattern you'll use over and over:
type=AVC msg=audit(1754256000.123:456): avc: denied { read } for pid=1234 comm="nginx" name="index.html" dev="sda1" ino=5678 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=fileRead from the most informative parts:
avc: denied { read } — which operation was denied (in this case: read).comm="nginx" — the command/process that tried to access.scontext=...httpd_t:s0 — the domain (subject) that tried.tcontext=...user_home_t:s0 — the type (object) being targeted.tclass=file — the object class (file, dir, tcp_socket, and so on).A loose translation of the line above: "the nginx process in the httpd_t domain was denied reading a file labeled user_home_t". From here the diagnosis begins: is the object label wrong (needs restorecon — episode 4), or is a new rule actually needed (audit2allow — a later episode).
Tip
Time (timestamp) is your friend. When troubleshooting, note when the error occurred, then search for AVC denials in that time range with ausearch -m avc -ts 12:30:00. Don't read the whole log — filter first by time and by the problematic process (comm).
In this episode 3, we've mastered the three SELinux modes — enforcing (enforced), permissive (recorded but allowed), and disabled (completely off) — along with three mode-switching mechanisms: setenforce (runtime), the enforcing=0 kernel parameter, and /etc/selinux/config (permanent). We also learned to read our first denials from /var/log/audit/audit.log with ausearch.
The essentials to take with you:
setenforce 0 takes effect until reboot; SELINUX=disabled requires a reboot and can't be reversed at runtime.getenforce for a short status; sestatus for complete details.scontext, tcontext, tclass, and the denied operation.In the next episode 4, we'll discuss labeling & filesystem context — viewing contexts with ls -Z and stat -Z, changing them temporarily with chcon, mapping them permanently with semanage fcontext, restoring them with restorecon, and full relabeling with fixfiles. Stay motivated, because the skills from this episode are what will save you most often in production!