I created a user with uid 1000 in the Dockerfile and created a directory /app, changing the owner of /app to uid 1000. Spring Boot also starts using the user with uid 1000.
RUN adduser --uid 1000 -D appuser
RUN mkdir /app
RUN chown -R appuser:appuser /app
USER 1000
ENTRYPOINT [ "./docker-entrypoint.sh" ]
When Spring Boot starts, it creates the directory /app/portal/logs and writes logs to the logs directory. Before using Docker Compose to mount the logs to a directory on the host with a volume, the entire path /app/portal/logs had uid 1000 as the owner, and there was no issue.
However, when I created a volume to mount /app/portal/logs to /data on the host, the owner of the app and logs directories inside the container remained uid 1000, but the owner of the intermediate directory, portal, became root.
services:
portal:
image: myapp:latest
volumes:
- /data:/app/portal/logs
user: "1000:1000"
/app $ ls -al / | grep app
drwxr-xr-x 1 appuser appuser 83 Jun 7 16:13 app
/app $ ls -al /app | grep portal
drwxr-xr-x 3 root root 18 Jun 7 16:13 portal
/app $ ls -al /app/portal/
drwxr-xr-x 3 root root 18 Jun 7 16:13 .
drwxr-xr-x 1 appuser appuser 83 Jun 7 16:13 ..
drwxr-xr-x 2 appuser appuser 4096 Jun 7 14:54 logs
This causes Spring Boot to have insufficient permissions when creating other directories in /app/portal. How can I change the owner of the entire path created by the volume to uid 1000?
I tried adding user: "1000:100" in the Docker Compose YAML, but it didn't work. Pre-creating /app/portal and changing the owner of the entire path to uid 1000 in the Dockerfile beforehand can solve this issue, but it seems a bit odd to do so.
Docker is automatically creating the directory for you, with a set of permissions that don't match what you want. You probably need to either mount that parent directory as a volume or create it in the Dockerfile, or both; mounting the parent will make the permissions easier.
volumes:
- /data:/app/portal
# ^^^^^^^^^^^ for the entire writable directory
user: 1000:1000 # matching the numeric owner for this directory on this system
Docker needs to create the container filesystem before it can start the process. You clarify in a comment that the /app/portal
directory isn't in the image. But, your Compose file mounts content onto /app/portal/logs
. That means Docker needs to create the mount point, which also means it needs to create the directory that holds it.
At that point Docker needs to choose an owner and permissions. "Owned by root" is one of several reasonable choices it could make, but it happens to not work for your application.
I mentioned that you can create the directory in the image too. That's probably a good idea
RUN adduser -D appuser
RUN mkdir /app/portal && chown appuser:appuser /app/portal
In your current Dockerfile, doing this will avoid the permission problem: the parent directory will exist and its owner in the image happens to match the user:
in the Compose file. In general, though, you can't guarantee that the runtime user:
will exactly match what's built into the image. In this last fragment I've removed the specific numeric user ID from the Dockerfile; but that means you will always need to mount a volume over the entire writable directory if you're going to use a non-default user:
.