How to Fix Permission Issues with Docker Volumes
Learn about the simplest way to solve permission issues with Docker volumes when running as a non-root user.
I’ve been using Docker for 8 years now, but I still get tripped by permission issues with Docker volumes because volumes aren’t something I create on a regular basis.
I’m writing this post both as a reminder to myself and for anyone else who might be struggling with the same issue.
Creating and Mounting Volumes
When you use docker volume create myvol to create a named volume, Docker will create a directory in /var/lib/docker/volumes/myvol/_data. This directory will be owned by the root user.
If you mount this volume into a container using the -v myvol:/data flag and run a command that writes to /data inside the container, the user running the command needs to be root as well.
But what if you want to run your app inside the container as a non-root user?
Running without Root
Suppose that your app is running as the appuser user, and it needs read/write access to the /data directory.
When running the container, you used the -v myvol:/data flag to mount the volume.
If you try to write anything to /data inside the container, you’ll get a permission denied error because /data is owned by root and the appuser user doesn’t have write permissions.
Rookie Mistake
At first, I thought that I could just change the ownership of the volume directory to the appuser user in the Dockerfile.
FROM alpine
RUN adduser -D appuser && \
mkdir -p /data && \
chown appuser:appuser /data
USER appuser
CMD ["sh"]
That’s a rookie mistake. The /data directory inside the container is a mount point for the volume, and the ownership of the volume directory is set when the volume is mounted, not when the container is built.
Solution
The solution is to change the ownership of the volume directory to the appuser user after the volume is mounted.
You do that with an entrypoint script:
#!/bin/bash
set -e
# Ensure directory exists with correct ownership
mkdir -p /data
chown -R appuser:appuser /data
if ! gosu appuser bash -c "touch /data/.write_test && rm /data/.write_test"; then
echo "ERROR: Cannot write to /data as appuser user"
echo "Directory permissions:"
ls -la /data
echo "Current user: $(id)"
exit 1
fi
echo "Starting application as appuser user..."
exec gosu replyforge /app/myapp "$@"
The entrypoint runs after the volume is mounted, so it can change the ownership of the volume directory to the appuser user since we’re still running as root at that point. After setting ownership, we run the app as the appuser user using gosu, which is a simple tool to run commands as other users.
In your Dockerfile, make sure to install gosu and copy the entrypoint script into the container and set it as the entrypoint in the Dockerfile:
FROM debian:bookworm-slim
RUN adduser -D appuser && \
apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gosu && \
rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
Notice that the container runs as root initially.
Conclusion
To summarize, just make sure to set the permission after the volume is mounted using an entrypoint script.
Need a Developer Who Gets It Done?
If this post helped solve your problem, imagine what we could build together! I'm a full-stack developer with expertise in Python, Django, Typescript, and modern web technologies. I specialize in turning complex ideas into clean, efficient solutions.