How to Configure SELinux for Database Servers on Fedora

Enable the container_use_devices SELinux boolean to allow database containers to access host devices and data volumes on Fedora.

You spin up a database and SELinux blocks it

You launch a PostgreSQL or MySQL container on Fedora, mount your data volume, and the service crashes immediately. The logs show a permission denied error. You check the host filesystem and the Unix permissions look correct. You run getenforce and see Enforcing. SELinux is stepping in. You could flip it to permissive, but that leaves your database exposed to lateral movement and privilege escalation. You need the right context labels and booleans so the database runs securely without bypassing the security model.

What is actually happening

SELinux does not rely on traditional Unix permissions alone. It uses Mandatory Access Control to restrict what processes can touch, regardless of file ownership or group settings. A database process runs under a specific domain, like postgresql_t or mysqld_t. That domain has a strict allow list of file types it can read, write, or execute. When you mount a host directory into a container or run a database directly, the filesystem often carries a generic label like default_t or var_t. The database domain sees that label, checks its policy, and denies access. The denial is not a bug. It is the policy working exactly as designed.

Containers add a second layer. Podman and Docker run workloads in isolated namespaces. By default, SELinux labels the container filesystem with container_file_t. If your database needs to access host devices like /dev/shm for shared memory, or a raw block device for storage, the container policy blocks it. You have two paths to fix this. You can adjust the boolean that governs container device access, or you can relabel the mounted data directory so the database domain recognizes it as valid storage. Both approaches keep SELinux enforcing while giving the database the access it requires.

Convention aside: Fedora ships with strict default contexts for a reason. The container_var_lib_t type exists specifically for database and application data that lives outside the container image. It grants read and write access to containerized services while blocking execution and preventing the container from reading sensitive host files.

Check the audit log before you guess. Raw AVC denials tell you exactly which domain is blocked and which file type it is trying to touch.

The fix for native and containerized databases

Start by identifying what SELinux is actually blocking. Run the audit log search to see recent access vector cache denials.

sudo ausearch -m avc -ts recent | grep denied
# WHY: Filters the audit log for recent AVC denials. The -m avc flag targets mandatory access control events. The grep isolates the denied lines so you can spot the missing permission.

If the output shows your database process trying to read or write a file labeled default_t or unlabeled_t, the fix is a context relabel. Do not use chcon for this. chcon applies a temporary label that gets overwritten the next time the filesystem is restored or the package updates. Use semanage to write the rule to the SELinux policy store.

sudo semanage fcontext -a -t container_var_lib_t "/mnt/db-data(/.*)?"
# WHY: Adds a persistent file context rule. The -a flag appends to the policy. The -t flag sets the target type. The regex matches the directory and everything inside it.
sudo restorecon -Rv /mnt/db-data
# WHY: Applies the new rule to the existing files. The -R flag recurses into subdirectories. The -v flag prints each file as it gets relabeled so you can verify the change.

If your database runs inside a container and needs host device access, the context relabel alone will not help. You need to toggle the container boolean.

sudo setsebool -P container_use_devices 1
# WHY: Enables the boolean permanently. The -P flag writes the change to the policy store so it survives reboots. The value 1 turns the boolean on.

Convention aside: Fedora ships with container_use_devices disabled by default. This is intentional. Allowing containers to access host devices breaks the isolation boundary. Only enable it when your database explicitly requires /dev/shm or a custom character device, and only if you trust the container image.

Apply the rule to the policy store, not the inode. Runtime changes vanish on reboot.

Verify it worked

Restart the database service or container. Watch the logs for the startup sequence. If SELinux is still blocking something, the denial will appear immediately. Check the setroubleshoot service for a human-readable summary of the event.

sudo journalctl -t setroubleshoot -n 10
# WHY: Queries the journal for SELinux alert messages. The -n 10 flag shows the ten most recent entries. setroubleshoot translates raw audit denials into plain English recommendations.

Confirm the process is running under the correct domain.

ps -Z | grep -E "postgres|mysql"
# WHY: Lists running processes with their SELinux security context. The -Z flag prints the domain and type. Matching the database name confirms the right domain is active.

If the logs show clean startup and no AVC messages, the context and boolean are correct. Run a quick query against the database to confirm read and write operations succeed. The filesystem labels should now match the database domain expectations. Reboot before you debug. Half the time the symptom is gone.

Common pitfalls and what the error looks like

The most common mistake is using chcon instead of semanage. chcon modifies the inode label directly. Package managers and filesystem restore tools ignore chcon changes. The next dnf upgrade or restorecon run will wipe your manual labels and break the database again. Stick to semanage fcontext for any directory outside the default package paths.

Another frequent error is forgetting the -P flag on setsebool. Without it, the boolean change only applies to the current runtime session. A reboot resets it to the default value, and your container loses device access. The error message in the container logs will look like Permission denied or open /dev/shm: operation not permitted. The host audit log will show avc: denied { read write } for pid=... comm="postgres" name="shm".

Do not edit files in /usr/lib/selinux/. Those files ship with the selinux-policy package. Any manual edits get overwritten on the next security update. All user modifications belong in /etc/selinux/ or in the semanage policy store. Trust the package manager. Manual file edits drift, snapshots stay.

Container mount options also trip up administrators. Passing :Z to a volume mount tells the container runtime to relabel the content exclusively for that container. Passing :z shares the label across multiple containers. Using neither leaves the host label intact, which triggers the AVC denial you are trying to fix. Pick the mount suffix that matches your isolation requirements.

Read the audit log first. Guessing the wrong context wastes hours.

When to use this vs alternatives

Use semanage fcontext when you need persistent file labels for custom data directories or mounted volumes. Use setsebool -P when a container or service requires a policy toggle that the base distribution disables by default. Use chcon only for temporary debugging sessions where you plan to revert the change before rebooting. Use audit2allow when you are building a custom policy module for a proprietary application that does not ship with a Fedora SELinux type. Use getsebool when you need to verify the current state of a boolean before changing it. Use ls -Z when you want to inspect the current labels on a directory tree without modifying anything. Use podman run --security-opt label=level:s0:c100,c200 when you need to isolate multiple database containers from sharing the same SELinux category. Use dnf install selinux-policy-devel when you plan to compile custom policy modules from source.

Where to go next