Podman Error: 'Permission denied' from an SELinux AVC on a Bind Mount
Fix SELinux AVC denials on Podman bind mounts: use :z and :Z relabel options, read ls -Z and ausearch output, set container_file_t with semanage, and avoid setenforce 0.
- #podman
- #containers
- #troubleshooting
- #errors
Stuck on this Podman error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Exact Error Message
$ podman run --rm -v /srv/appdata:/data:ro docker.io/library/alpine cat /data/config.yaml
cat: can't open '/data/config.yaml': Permission denied
The container-side message is generic; the real error is in the host audit log:
type=AVC msg=audit(1752921883.412:918): avc: denied { read } for pid=48211
comm="cat" name="config.yaml" dev="dm-0" ino=1049234
scontext=system_u:system_r:container_t:s0:c214,c803
tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
What It Means
On SELinux-enforcing systems — RHEL, CentOS Stream, Fedora, Rocky, AlmaLinux — every Podman container process runs in the container_t domain, confined by the container-selinux policy. That policy is deliberately narrow: container_t is allowed to read and write files labelled container_file_t, and almost nothing else on the host filesystem. When you bind-mount a host directory into a container, the files keep whatever label they already had — user_home_t, etc_t, var_lib_t, default_t — and the container’s access is denied at the kernel level, before the file’s Unix permissions are even consulted.
That is why the failure is so confusing: ls -l shows mode 0644 and the right owner, chmod 777 changes nothing, and running the same command outside the container works fine. Discretionary access control (owner/group/mode) and mandatory access control (SELinux) are two independent gates, and the container is failing the second one. Podman’s answer is the :z and :Z mount options, which tell Podman to relabel the source path to container_file_t at mount time. :z applies a shared label usable by any container; :Z applies a private label with a unique MCS category pair (the s0:c214,c803 in the AVC above), so only that one container can read it.
Common Causes
- The bind-mount source has a host label such as
user_home_toretc_tthatcontainer_tmay not read. - The mount was written without
:zor:Z, so no relabelling happened. - The directory was relabelled once, then
restoreconor a package update reset it to the default context. - A container was recreated with
:Z, giving it a new MCS category, while a second container still holds the old one. - The volume lives on a filesystem that does not support security labels at all, such as NFS or some FUSE mounts.
- SELinux booleans like
container_use_cephfsorcontainer_manage_cgroupare off for a workload that needs them.
Diagnostic Commands
First confirm SELinux is actually the cause by checking the mode and the labels on both sides:
getenforce
ls -Z /srv/appdata
ls -Zd /srv/appdata
Search the audit log for the denial, which names the source domain, the target label, and the permission:
sudo ausearch -m AVC,USER_AVC -ts recent
sudo ausearch -m AVC -c cat -ts today -i
If setroubleshoot is installed, sealert turns raw AVCs into a readable explanation with suggested fixes:
sudo sealert -a /var/log/audit/audit.log
Prove the hypothesis without changing anything permanently — a permissive-domain run that succeeds confirms SELinux, and the AVCs are still logged:
podman run --rm --security-opt label=disable -v /srv/appdata:/data:ro \
docker.io/library/alpine cat /data/config.yaml
Inspect what label the container process is actually running with:
podman inspect --format '{{.ProcessLabel}} {{.MountLabel}}' myapp
Step-by-Step Resolution
- Reproduce with the relabel option and confirm it fixes the read. Use
:Zwhen the directory belongs to exactly one container,:zwhen several containers share it:
# private: unique MCS category, one container only
podman run --rm -v /srv/appdata:/data:Z docker.io/library/alpine cat /data/config.yaml
# shared: any container can read it
podman run --rm -v /srv/appdata:/data:z docker.io/library/alpine cat /data/config.yaml
- Check what
:z/:Zdid, and be aware that it is a real, recursivechconon the host path — not a container-scoped illusion:
ls -Z /srv/appdata
# system_u:object_r:container_file_t:s0:c214,c803 config.yaml
- Never point
:zor:Zat a shared system directory. Relabelling/home,/etc,/usr, or/varrecursively rewrites the labels of files the rest of the system depends on and can leave the host unbootable or SSH unusable. Mount a dedicated subdirectory instead:
# dangerous - do not do this
# podman run -v /home:/data:Z ...
# safe
mkdir -p /srv/appdata
podman run --rm -v /srv/appdata:/data:Z docker.io/library/alpine ls /data
- For anything long-lived, make the label permanent with a file-context rule rather than relying on the mount option.
:zsets the label now;semanage fcontextmakes it survive a filesystem relabel:
sudo semanage fcontext -a -t container_file_t "/srv/appdata(/.*)?"
sudo restorecon -Rv /srv/appdata
- If the denial is not about a file label but a capability — cgroup writes, NFS-backed volumes, device access — the fix is a boolean, not a relabel. List and set the relevant one:
getsebool -a | grep ^container_
sudo setsebool -P container_manage_cgroup on
sudo setsebool -P container_use_cephfs on
- If the volume is on a filesystem with no label support (NFS, virtiofs, some FUSE mounts), relabelling is impossible and
:zwill fail. Scope the escape hatch to that one container rather than disabling SELinux globally:
podman run -d --name legacy \
--security-opt label=disable \
-v /mnt/nfs/share:/data \
myapp:latest
Filesystems that do support labels but need a fixed one can use a mount-time context option instead, which is safer than disabling confinement entirely.
Prevention
- Keep container data under a dedicated path such as
/srv/or~/.local/share/containers/and never bind-mount system directories. - Register file contexts with
semanage fcontextin your provisioning code so labels survive package updates and relabels. - Prefer named Podman volumes over host bind mounts — Podman labels them correctly on creation with no extra flags.
- Use
:Zfor single-container data and reserve:zfor genuinely shared paths, so MCS isolation is not thrown away by default. - Audit AVC denials continuously rather than only when something breaks;
ausearch -m AVC -ts todayin a health check catches silent regressions. - Test container changes on an enforcing host, so a denial never first appears in production.
Related Errors
Error: lsetxattr /path: operation not supported— the target filesystem cannot store labels at all; see Podman error: lsetxattr operation not supported.Error: statfs /srv/appdata: no such file or directory— the bind source does not exist, a plain path error rather than a policy denial.Error: cannot connect to the Podman socket— a socket-permission problem, which can also be SELinux-related; see Podman error: cannot connect to Podman socket.avc: denied { write }on a:romount — the policy is fine, the mount is simply read-only.
Frequently Asked Questions
Why doesn’t chmod 777 fix this? Because SELinux is a separate, mandatory gate that runs regardless of Unix permissions. The kernel checks the process’s domain (container_t) against the file’s type label, and if the policy has no rule allowing that pair, the access is denied no matter how permissive the mode bits are.
What is the difference between :z and :Z? :z relabels the path to shared container_file_t with no MCS categories, so every container can access it. :Z adds a unique category pair that only the creating container carries, so no other container can read the data even if it mounts the same path. Use :Z unless you actually need sharing.
Should I just run setenforce 0? No. It disables mandatory access control for the entire host, not just your container, removing the isolation boundary that keeps a compromised container from reading host files — and it silently reverts on reboot, so the “fix” disappears and the problem reappears later with no obvious cause. If you truly need one container unconfined, use --security-opt label=disable on that container only.
Is --security-opt label=disable safe for production? It is a scoped, documented trade-off: that container loses SELinux confinement while the rest of the host keeps it. Acceptable for a label-less NFS mount, not acceptable as a default. Prefer a correct semanage fcontext rule wherever the filesystem supports labels. For more Podman fixes, see the Podman guides.
Fixed it? Get 500 Podman & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.