Persistent data is one of the most important concerns when running containers, since a container’s writable layer is destroyed when the container is removed. Docker provides three storage mechanisms to address this: named volumes managed by Docker itself, bind mounts that link a host directory directly into a container, and tmpfs mounts for ephemeral in-memory storage. On RHEL 8 with SELinux enforcing, bind mounts require an additional label flag to prevent permission-denied errors. This tutorial covers all three approaches along with the management commands you need to keep your storage tidy.

Prerequisites

  • Docker CE installed and running on RHEL 8
  • Root or sudo access, or membership in the docker group
  • Basic familiarity with docker run syntax
  • SELinux in enforcing mode (the default on RHEL 8 — check with getenforce)

Step 1 — Create and Inspect Named Volumes

Named volumes are stored under /var/lib/docker/volumes and managed entirely by Docker. Create one, list all volumes, and inspect its metadata before attaching it to a container.

docker volume create myappdata
docker volume ls
docker volume inspect myappdata

The inspect output shows the full host path under Mountpoint. Named volumes are the preferred approach for database files and application state because Docker handles directory creation and ownership automatically.

Step 2 — Mount a Named Volume in a Container

Attach the volume to a container using the -v volumename:/container/path syntax. Data written to that path inside the container is stored in the volume and survives container removal.

docker run -d --name appserver 
  -v myappdata:/app/data 
  nginx:alpine

docker exec appserver sh -c "echo 'hello volume' > /app/data/test.txt"
docker rm -f appserver
docker run --rm -v myappdata:/app/data alpine cat /app/data/test.txt

The second run command confirms the file survived the removal of the first container.

Step 3 — Use Bind Mounts with SELinux Labels

A bind mount maps a specific host directory into the container. On RHEL 8, SELinux blocks container processes from reading host directories unless you append the :z or :Z label. Use :z to share the directory among multiple containers and :Z to make it private to a single container.

mkdir -p /srv/webroot
echo "

Hello from bind mount

" > /srv/webroot/index.html # :z relabels for shared access (safe for multiple containers) docker run -d --name webserver -v /srv/webroot:/usr/share/nginx/html:z -p 8080:80 nginx:alpine

Without the :z or :Z suffix, NGINX would fail to read the files and serve a 403 error even though the bind mount path is correct.

Step 4 — Copy Files With docker cp

Use docker cp to transfer individual files between the host and a running or stopped container without needing a mounted volume.

# Copy a file from host into the container
docker cp /srv/webroot/index.html webserver:/usr/share/nginx/html/index.html

# Copy a file from the container back to the host
docker cp webserver:/etc/nginx/nginx.conf /tmp/nginx.conf

Step 5 — Use a tmpfs Mount for Ephemeral Data

A tmpfs mount stores data in host RAM and is never written to disk, making it suitable for secrets, session tokens, or any scratch data that must not persist.

docker run -d --name ephemeral-app 
  --tmpfs /run/secrets:rw,noexec,nosuid,size=64m 
  nginx:alpine

The mount options noexec and nosuid follow the principle of least privilege by preventing execution of binaries from the secrets directory.

Step 6 — Clean Up Unused Volumes

Anonymous volumes (created automatically for containers without an explicit name) accumulate over time. Remove a specific volume or prune all unused ones at once.

docker volume rm myappdata
docker volume prune

docker volume prune only removes volumes not currently attached to any container (running or stopped). Always check docker volume ls before pruning in a shared environment to avoid deleting data in use by stopped containers you intend to restart.

Conclusion

You now know how to use all three Docker storage types on RHEL 8: named volumes for managed persistent data, bind mounts with SELinux :z/:Z labels for host-directory access, and tmpfs mounts for sensitive ephemeral data. The SELinux labeling requirement is a RHEL 8 specific detail that trips up many users migrating from Debian-based systems, so building it into your run commands and Compose files from the start saves significant debugging time later. Regular volume pruning keeps disk usage under control on long-running development hosts.

Next steps: How to Configure Docker Networking on RHEL 8, How to Manage Docker Images and Containers on RHEL 8, and How to Install Docker Compose on RHEL 8.