Skip to content
Go back

Docker Volume Mounts: Essential Flags

· Updated:
By SumGuy 6 min read
Docker Volume Mounts: Essential Flags

Each Docker mount point flag serves specific scenarios that can enhance the functionality, security, and efficiency of containerized applications. By understanding and applying these flags appropriately, developers and system administrators can optimize their Docker environments to suit various operational needs, from simple web hosting to complex, distributed applications in a microservices architecture.

1. Read-Only (:ro)

Scenario: Hosting Static Websites Suppose you are deploying a static website using an Nginx Docker container. The website’s files (HTML, CSS, JavaScript) do not need to be modified by the container once deployed.

Example:

docker run -d -p 80:80 -v /host/webdata:/usr/share/nginx/html:ro nginx

Explanation:

2. Read-Write (:rw)

Scenario: Application Logging Imagine you have an application that needs to write logs persistently so that logs are retained even after the container restarts or is removed.

Example:

docker run -d -v /host/applogs:/app/logs:rw myapp

Explanation:

3. Shared (:shared)

Scenario: Development Environment In a development environment, multiple containers might need to access and modify a common set of files, such as source code or resources.

Example:

docker run -d -v /host/project:/workspace:shared coder1
docker run -d -v /host/project:/workspace:shared coder2

Explanation:

4. RShared (:rshared)

Scenario: Kubernetes Volume Propagation In a Kubernetes cluster, you might need to ensure that any mounts created by a container in a pod are visible to other pods, possibly on different nodes.

Example:

docker run -d -v /host/data:/data:rshared global-data-provider

Explanation:

5. :z and :Z

These flags are used to automatically adjust the SELinux labels of the host files to allow containers to read and write them.

Example:

docker run -v /host/path:/container/path:z nginx

6. --mount Option

While not a flag, the --mount syntax provides more explicit and verbose options compared to the -v or --volume syntax. It supports different types of mounts (like volume, bind, or tmpfs), and allows for additional settings such as readonly.

Example:

docker run --mount type=bind,source=/host/path,target=/container/path,readonly nginx

7. --tmpfs

This option mounts a temporary file storage in the container’s filesystem. It can be used to store temporary application data without persisting it to disk.

Example:

docker run --tmpfs /tmp nginx

8. --volumes-from

This flag allows one container to use the volume of another container. It’s useful for sharing data between containers, especially in complex applications where multiple services need access to the same data.

Example:

docker run --volumes-from other-container nginx

9. :nocopy

This modifier can be used with the --mount option for volumes. It tells Docker not to copy data from a container path to a new volume, which can be useful when the default behavior of copying data is not desired.

Example:

docker run --mount type=volume,source=myvolume,target=/app,data:nocopy myapp

The Ownership Gotcha That’ll Ruin Your Night

Here’s the thing nobody tells you until you’re staring at a Permission denied error at 11 PM: Docker doesn’t care about your host filesystem permissions when it creates a named volume. When you spin up a container with a named volume for the first time, Docker initializes that volume directory as root:root by default — even if your app inside the container runs as a non-root user.

This bites you hardest with database containers or any image that explicitly switches to an unprivileged user (Postgres runs as postgres, Redis as redis, and so on). You mount a volume, the container starts, and the app immediately crashes because it can’t write to its own data directory.

The fix depends on the situation. For named volumes, the cleanest approach is to let the container’s entrypoint handle ownership — most official images already do this. But for bind mounts, you’re responsible:

Terminal window
# Check what UID the container process runs as
docker run --rm postgres id
# Pre-create the host dir with the right owner
sudo mkdir -p /data/postgres
sudo chown 999:999 /data/postgres # 999 is the postgres UID in the official image
# Now mount it
docker run -d \
-v /data/postgres:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres

If you don’t know the UID ahead of time, you can also override it at container start with --user, but check the image docs first — some entrypoints break if you do that.

For Compose setups, a volumes: block with a driver_opts entry or an init container that runs chown is the production-grade solution. Ugly? A little. But it beats debugging permission errors during a deploy.

compose.yml
services:
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
volumes:
pgdata:

With named volumes like the example above, Postgres’s entrypoint handles ownership automatically — no manual chown needed. That’s one strong reason to prefer named volumes over bind mounts for stateful services unless you have a specific reason to inspect the files directly on the host.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Docker Resource Limits: Stop Letting Containers Eat Your RAM
Next Post
Dockerfile: Differences Between COPY and ADD

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts