skip to Main Content

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


  1. 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:.

    Login or Signup to reply.
  2. 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:

    1. Create a new script file, e.g., fix-permissions.sh, with the following content:
    #!/bin/sh
    chown -R appuser:appuser /app/portal
    
    1. Add the script to your Docker image by adding the following line to your Dockerfile:
    COPY fix-permissions.sh /usr/local/bin/
    
    1. In your docker-entrypoint.sh script, add a line to execute the fix-permissions.sh script before starting your application:
    #!/bin/sh
    /usr/local/bin/fix-permissions.sh
    # Start your application
    
    1. Rebuild your Docker image with the updated Dockerfile and scripts.

    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 the logs subdirectory) to the appuser 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.

    services:
      portal:
        image: myapp:latest
        volumes:
          - /data:/app/portal/logs:rw,uid=1000,gid=1000
        user: "1000:1000"
    

    In this example, the uid=1000 and gid=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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search