If you haven't already, check out my Getting started with Podman guide.
When we run containers in podman, there will be instances where we need to persist data outside of the container lifetime. This is achieved with volumes.
A volume is simply a file or folder that is mounted to a specific path in the container and any data written there will not be lost when the container is removed.
With podman and generally docker there are two common types of volumes:
A named volume, which is a volume managed and created by podman. It already has all the correct permissions allocated and is usually the default option.
A host mounted volume, which is a file or folder not created or managed by podman that you mount into your containers. You will have to ensure that the
correct ownership and permissions are set otherwise your container will throw an access denied
error. More on that later.
Here is the most useful / used volume commands with podman:
podman volume --help # show all volume commands with descriptions
podman volume ls # list all your volumes
podman volume inspect {name | id} # inspect the details of that volume
podman volume create {name} # create a volume, name is optional
podman volume rm {name | id} # remove (delete) a volume
podman volume prune # prune (delete) all volumes NOT in use
An awesome quality of life (QOL) feature of podman, is that we don't need to create our named volumes before we run our containers, podman will do that for us. All we need to do is specify the named volume and where to mount it along with the appropriate SELinux label if you are using SELinux.
Here is an example command of this in action:
podman run \
--name my-pgsql \ # container name
-v pg-data:/var/lib/postgresql/data:z \ # named volume mount
-p 5432:5432 \ # port mapping
-e POSTGRES_PASSWORD=secret \ # pgsql env var
postgres:latest
Using the -v
flag we specified the named volume pg-data
and mounted it to /var/lib/postgresql/data
path within the container
along with the z
SELinux label.
Here is an example of a host mounted volume:
podman run \
--name my-nginx \ # container name
-v ~/my-nginx-site:/usr/share/nginx/html:ro,Z \ # host mounted volume
-p 8080:80 \ # port mapping
nginx:latest
In this example we also used the -v
flag, but instead of mounting a named volume we used a path on the host and mounted that
to the /usr/share/nginx/html
path inside of the container with the appropriate SELinux labels.
When using host mounted volumes
it's inevitable that you will run into an access denied error. This can also occur with named volumes
but it's super rare.
The reason our containers receive an access denied
error is usually due to one or more of the following:
Let's cover how we can tackle these different scenarios:
Before we can tackle those issues we need to understand a key Linux kernel feature that enables container isolation: user namespaces
.
Think of a user namespace as a bubble where the user and group IDs (UID and GID) are mapped to different IDs within that bubble.
This means our containers believe they are running as normal users -- such as nginx
with UID 101
-- but on the host, this
is mapped to a different UID like 10100
which typically has very limited privileges:
This is important to understand because when containers interact with the file system (reading or writing to volumes), the kernel
uses the host UID for permission checks. If that host UID does not have sufficient privileges or the correct ownership you will get an
access denied
error.
NOTE: Before we continue, make sure you understand how Linux handles file and folder ownership, including how
permissions work for the user, group and others (using r
, w
and x
).
To make our lives easier with user namespaces, the podman team has given us the podman unshare command.
It executes any command you give it within the container user namespace, allowing us to use linux commands like chown
(change owner)
to change ownership of files or folders without worrying about what the container user id maps back to on host as the user namespace handles that
for us.
Let's see this in action in an example where we grant nginx
container user test file that we mounted:
# ownership before, using `ls -l`:
-rw-rw-r-- 1 jason jason 0 May 27 18:55 myfile.txt
# change ownership of file
podman unshare chown nginx myfile.txt
# ownership after, using `ls -l`
-rw-rw-r-- 1 10100 jason 0 May 27 18:55 myfile.txt
We can also recursively change ownership of a folder and its contents using the -R
flag:
podman unshare chown -R nginx my-dir
This is pretty simple as we don't need to think about user namespaces, we can simply use the linux chmod
command to assign read, write or
execute permissions depending on our needs.
Here is an example of adding write permission to our myfile.txt
for user from our earlier example.
# permissions before using `ls -l`:
-r--rw-r-- 1 10100 jason 0 May 27 18:55 myfile.txt
# adding write (w) permission for user with chmod:
chmod u+w myfile.txt
# permissions after using `ls -l`:
-rw-rw-r-- 1 10100 jason 0 May 27 18:55 myfile.txt
Unfortunately, I'm not too familiar with SELinux labels, but I'm sure if you are using it that you know how to manage or assign labels.
Here is the official documentation.
When I become more familiar with it, I will update this section.
We covered the basics of volumes and how to triage those pesky access denied errors from rootless containers.
However, my guide does not cover everything and in case you missed it, here are some helpful documentation links:
The next step on your podman journey would be Learning How to Use Networks in Podman.
Thanks for reading and I hoped this helped you on your podman journey!