
September 10, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
You deploy a Laravel app on Ubuntu, reload PHP-FPM, and the site returns 500 with nothing useful in the application log. The real culprit is often SELinux blocking httpd from reading a new directory under /var/www. SELinux Explained: Modes and Policies is the reference you need before you disable security or paste random chcon commands. On production servers I maintain with Apache, PHP-FPM, and Git-based deploys, SELinux sits between your file permissions and what processes can actually do. This guide walks through modes, policy types, contexts, booleans, and the audit workflow that fixes denials without opening the whole box. If you run Linux system administration for production web stacks, SELinux literacy saves hours of false starts.
What is SELinux and why does SELinux Explained: Modes and Policies matter on production servers?
SELinux is Mandatory Access Control (MAC) layered on top of standard Unix permissions (DAC). DAC asks: does this user own the file or belong to its group? MAC asks: is this process type allowed to read this file type, regardless of chmod 777?
That distinction matters on shared hosting and VPS boxes where one compromised PHP script must not read another client's uploads or poke at MySQL sockets. I've seen teams spend days tuning Laravel storage permissions while SELinux quietly denied writes to storage/app because the directory kept the wrong context after a manual copy.
SELinux ships enabled on RHEL, AlmaLinux, Rocky Linux, and Fedora. On Ubuntu it is optional but common on hardened images. Your stack—Apache or Nginx fronting PHP-FPM 8.3/8.4, MySQL 8.4 or MariaDB 12.3, Redis 8.10—runs inside SELinux domains whether you notice or not.
Policy is compiled into the kernel. At boot, the loaded policy defines allowed transitions between process domains and object types. When a rule blocks access, the kernel logs an AVC (Access Vector Cache) denial. That log entry is your map to a fix.
SELinux is not a firewall. It does not replace UFW or fail2ban. It complements them by constraining what a breached web process can touch after traffic is already inside. For background on adjacent hardening, see our posts on Kubernetes pod security and network policies and Content Security Policy for Laravel apps.
What are the three SELinux modes and when should you use each?
Runtime mode controls whether SELinux enforces policy, only logs violations, or is fully inactive. You set it in /etc/selinux/config for persistence and with setenforce for immediate changes until reboot.
Enforcing mode
Enforcing is the production default. Denied operations fail with EACCES, and AVC entries land in the audit log. Your app may break after a deploy, but the system stays within policy bounds. Never treat Enforcing as optional on internet-facing boxes.
Permissive mode
Permissive still evaluates every rule and logs would-be denials. Nothing is blocked. Use it briefly during initial hardening or after major path changes. A common mistake is leaving Permissive for months because nobody reads audit logs.
Disabled mode
Disabled unloads SELinux from the kernel. Toggling back to Enforcing later often requires a relabel of the filesystem. Avoid Disabled except during a controlled migration where you accept a full relabel window.
Check current mode:
getenforce
sestatus
# Temporary switch (lost on reboot unless config updated)
sudo setenforce 0 # Permissive
sudo setenforce 1 # Enforcing Persist mode in config:
sudo nano /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted On sister sites I deploy with Deployer 7 and GitLab CI, Enforcing stays on. Post-deploy smoke tests catch context drift before users do. That workflow mirrors what we document for Laravel + Livewire booking platforms on hardened Linux.
| Mode | Blocks access? | Logs AVC? | Typical use |
|---|---|---|---|
| Enforcing | Yes | Yes | Production, staging mirrors prod |
| Permissive | No | Yes | Short troubleshooting windows |
| Disabled | N/A | No | Rare migrations; avoid on web servers |
How do SELinux policies, contexts, and booleans work together?
Policy is the rulebook. Context labels on processes and files are the variables. Booleans are on/off switches that enable optional rule sets without recompiling the whole policy.
Policy types you will actually see
targeted is the default on RHEL-family systems. Most daemons run in confined domains; unconfined domains exist for admin shells. minimum is a stripped policy for containers or appliances. mls (Multi-Level Security) adds classification levels for government workloads—you will not need it on a WooCommerce or Laravel shop.
Set policy type in /etc/selinux/config via SELINUXTYPE=targeted. Changing type requires reboot and often relabel.
Security contexts: user:role:type:level
The type field drives type enforcement. A file might be system_u:object_r:httpd_sys_content_t:s0. PHP-FPM under Apache often runs as system_u:system_r:httpd_t:s0. Policy allows or denies pairs like httpd_t → httpd_sys_content_t : read.
View labels:
ls -Z /var/www/html/public
ps -eZ | grep php-fpm Booleans: surgical policy toggles
Booleans flip predefined rule bundles. Examples for web stacks:
httpd_can_network_connect— allow Apache/httpd outbound TCP (curl, remote APIs, SMTP)httpd_can_network_connect_db— allow database socket/TCP from web domainhttpd_unified— treat PHP-FPM and httpd as one domain on some setupshttpd_read_user_content— read user home dirs (usually keep off)
getsebool -a | grep httpd
sudo setsebool -P httpd_can_network_connect on
sudo setsebool -P httpd_can_network_connect_db on Permanent file context uses file context mappings, not one-off chcon:
# See defined contexts for web roots
sudo semanage fcontext -l | grep /var/www
# Assign persistent label to Laravel storage
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/myapp/storage(/.*)?"
sudo restorecon -Rv /var/www/myapp/storage Official references: the Red Hat Using SELinux guide and the SELinux Project security context notebook document context syntax and policy modules.
Application-level authorization in Laravel uses a different "policy" concept—gates and Eloquent policies. Our Laravel policies and gates guide covers that layer; SELinux protects the OS underneath.
How do you troubleshoot SELinux denials on a Laravel or PHP-FPM stack?
Start with symptoms: 403 on static assets, 500 on uploads, queue workers that cannot write logs, or PHP unable to reach Redis on a Unix socket. Confirm SELinux is Enforcing before you chase Laravel config.
Step 1: Read AVC denials
sudo ausearch -m avc -ts recent
sudo grep avc /var/log/audit/audit.log | tail -20
# Friendly summary tool (install policycoreutils-python-utils)
sudo dnf install -y setroubleshoot-server
sudo sealert -a /var/log/audit/audit.log An AVC line names scontext (source type), tcontext (target type), and tclass (permission class like file or dir).
Step 2: Pick the smallest fix
- Boolean if the denial matches a known toggle (network connect, DB connect).
- File context if a deploy copied files without labels—use
semanage fcontext+restorecon. - Port context if you bind a custom port:
semanage port -a -t http_port_t -p tcp 8080. - Local policy module only when no boolean or fcontext fits—audit the module before loading.
# Generate a local module from recent denials (review before install!)
sudo ausearch -c 'php-fpm' --raw | audit2allow -M myapp_php
sudo semodule -i myapp_php.pp
# List loaded modules
sudo semodule -l | head Step 3: Common Laravel paths on Enforcing systems
After a Deployer release swap, run restorecon -Rv on storage and bootstrap/cache. Symlinked releases do not always inherit correct labels from shared directories.
Queue workers started from cron need the same domain as the web user. A stale cron path pointing at an old release is a deployment bug; SELinux may expose it first.
Nginx + PHP-FPM on RHEL uses nginx_t and php-fpm domains—booleans differ slightly from Apache. Match fixes to your actual ps -eZ output.
For deeper primer material, read SELinux basics for administrators. Network segmentation in containers overlaps conceptually with Kubernetes network policies explained.
When auditing regex-heavy semanage rules or log parsers, a local regex tester beats guessing escape sequences in production scripts.
How do SELinux modes and policies compare to AppArmor and standard permissions?
DAC (chmod/chown) remains necessary but insufficient. Two apps running as www-data share the same DAC identity—SELinux separates them by domain if policy defines distinct types.
AppArmor on Ubuntu attaches path-based profiles. SELinux uses label-based type enforcement. AppArmor profiles read like file path lists; SELinux scales better on complex multi-service hosts but has a steeper learning curve.
| Mechanism | Granularity | Default on | Typical fix style |
|---|---|---|---|
| DAC (chmod) | User/group/mode | Everywhere | chown, chmod |
| SELinux MAC | Type + role + optional MLS | RHEL, Alma, Rocky, Fedora | Booleans, fcontext, modules |
| AppArmor | Path profiles | Ubuntu (optional) | aa-complain, profile edits |
Do not run both MAC systems in enforcing mode on the same box without a deliberate design. Pick one stack-appropriate framework and document exceptions.
Hardening does not end at the kernel. Pair MAC with ongoing server maintenance, patched PHP runtimes, and pre-release testing on staging that mirrors production SELinux mode.
On greenfield web development projects, define deploy paths and context rules before launch. Retrofitting SELinux after years of Permissive is painful—every unlabeled upload directory becomes an audit exercise.
Hosting choices matter too. Shared panels that disable SELinux globally trade your isolation for their convenience. Prefer VPS or dedicated instances where you control /etc/selinux/config. See domain registration and hosting options with that constraint in mind.
For enterprise modules—payment callbacks, document portals, CRM integrations—enterprise application development should include a MAC checklist alongside Laravel authorization and API rate limits (API rate limiting guide).
Policy-as-code in cloud pipelines (multi-cloud governance, Kyverno vs OPA Gatekeeper) extends the same mindset: explicit rules, logged violations, minimal exceptions. SELinux is the on-box analogue.
When you need a break from audit logs, unrelated but useful: the password generator for service accounts that pair with locked-down SELinux domains—not shared human passwords reused across sudo and deploy keys.
More about production Linux work: about Kokil Thapa, full services list, and client reviews mentioning secure deployments.
Key Takeaways
- Run Enforcing on production web servers; use Permissive only for short, logged diagnosis windows.
- targeted policy plus correct type contexts—not blanket
chmod 777—is how Laravel storage and cache directories stay writable and safe. - Fix denials in order: boolean → fcontext/restorecon → port label → audited custom module.
- After every Deployer or CI deploy, run
restoreconon release paths and confirm queue/cron domains match the web stack. - SELinux MAC complements DAC, firewalls, and app-level auth—it does not replace Laravel policies or CSP headers.
- Document every
setsebool -Pandsemanage fcontextchange so the next maintainer does not "fix" Enforcing by disabling it.
People Also Ask
Should I disable SELinux on a Laravel production server?
No. Disabling removes MAC protection and often forces a full filesystem relabel if you re-enable later. Fix AVC denials with booleans and file contexts instead. Production stacks on RHEL-family OS images are designed to run Enforcing.
What is the difference between setenforce and /etc/selinux/config?
setenforce 0|1 changes mode immediately until reboot. /etc/selinux/config sets the boot default. Use both: test with setenforce, then persist Enforcing in config once the app passes smoke tests.
Why does restorecon fix uploads after deployment but chcon does not stick?
chcon changes the label on disk until the next relabel or policy-driven reset. semanage fcontext registers a permanent pattern, and restorecon applies it. Deploy scripts should call restorecon, not manual chcon on every release.
Is SELinux the same as a firewall?
No. Firewalls filter network packets. SELinux controls what a process may do after it is already running—file reads, socket connects, capability use—based on domain types. Use UFW or cloud security groups together with SELinux, not as substitutes.
Ship hardened Linux stacks with confidence
SELinux Explained: Modes and Policies boils down to one habit: treat AVC lines as first-class deploy signals, not noise. Enforcing mode, targeted policy, labeled paths, and documented booleans keep PHP-FPM and Laravel apps working without flattening server security. I've applied this on shared EC2 hosts running multiple legal-tech and eCommerce properties—Enforcing stays on, and fixes stay in Git-backed runbooks.
Need Enforcing-safe deploys, post-migration relabels, or a staging box that mirrors production MAC settings? Contact us for Linux hardening and Laravel hosting support, or browse production portals running on maintained infrastructure.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

