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.
2
Answers
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.
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
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 runtimeuser:
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-defaultuser:
.The issue you’re facing is related to the way Docker handles file ownership and permissions when mounting volumes. When you mount a volume, Docker creates the top-level directory on the host with the root user’s ownership and permissions. This is why the
/app/portal
directory inside the container is owned by the root user.Solution 1
To resolve this issue, you can use the
chown
command inside the container to change the ownership of the/app/portal
directory after the volume is mounted. Here’s how you can do it:fix-permissions.sh
, with the following content:docker-entrypoint.sh
script, add a line to execute thefix-permissions.sh
script before starting your application:With this approach, the
fix-permissions.sh
script will run before your application starts, and it will recursively change the ownership of the/app/portal
directory (including thelogs
subdirectory) to theappuser
user.Solution 2
You can use the
user
option in your Docker Compose file to specify the user and group for the mounted volume. However, this approach requires the user and group IDs to exist on both the host and the container, which may not always be the case.In this example, the
uid=1000
andgid=1000
options ensure that the mounted volume is owned by the user with UID 1000 and the group with GID 1000, respectively.Conclusion
Both approaches should solve the issue and ensure that Spring Boot has the necessary permissions to create directories and files within the
/app/portal
directory.