Skip to content

Instantly share code, notes, and snippets.

@jeremy-code
Created April 2, 2025 18:04
Show Gist options
  • Save jeremy-code/cdf1d9f7aedb36d086dc47cce5119ad0 to your computer and use it in GitHub Desktop.
Save jeremy-code/cdf1d9f7aedb36d086dc47cce5119ad0 to your computer and use it in GitHub Desktop.
Docker Compose Configs Permissions

TL;DR: I think config files by default are root:root (UID 0, GID 0) and user and group readable (0440)

Here's the Docker documentation: Services top-level elements > Attributes > configs. Of note is:

uid and gid: The numeric uid or gid that owns the mounted config file within the service's task containers. Default value when not specified is USER.

mode: The permissions for the file that is mounted within the service's task containers, in octal notation. Default value is world-readable (0444). Writable bit must be ignored. The executable bit can be set.

In Configs top-level elements, it is stated

By default, the config:

  • Is owned by the user running the container command but can be overridden by service configuration.
  • Has world-readable permissions (mode 0444), unless the service is configured to override this.

Consider the followind docker-compose.yml:

services:
  busybox:
    container_name: busybox
    image: "busybox:latest"
    restart: unless-stopped
    stdin_open: true # docker run -i
    tty: true # docker run -t
    user: "1000:1000"
    configs:
      - my_config

configs:
  my_config:
    content: "Hello world!"

Then, running these commands:

docker compose attach busybox
~ $ ls -la my_config 
-r--r-----    1 root     root            31 Apr  2 17:38 my_config
~ $ id
uid=1000 gid=1000 groups=1000
  • The config is not world readable (0444) it's user and group readable (0440).
  • The UID and GID of the config is root:root while the user is 1000:1000

At first in regards to the UID and GID, I thought the documentation was just phrased weirdly and that it meant the USER command in the original DockerFile is what determines the uid/gid (i.e. the user yaml key will not affect it).

However:

services:
  distroless:
    container_name: distroless
    image: "gcr.io/distroless/base-debian12:debug-nonroot"
    restart: always
    stdin_open: true # docker run -i
    tty: true # docker run -t
    user: "65532:65532" # Note, this is the default USER, so this can be omitted
    configs:
      - my_config

configs:
  my_config:
    content: "Hello world!"
docker compose attach distroless
~ $ ls -la /my_config 
-r--r-----    1 root     root            31 Apr  2 17:56 /my_config
~ $ ls -ln /my_config 
-r--r-----    1 0        0               31 Apr  2 17:56 /my_config
~ $ id
uid=65532(nonroot) gid=65532(nonroot) groups=65532(nonroot)
~ $ cat /etc/passwd
root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin

Note that this image specifically has a nonroot user, but the config file is still owned by root.

@jeremy-code
Copy link
Author

I intend to request changes to the documentation with a more detailed review but I wanted to put this out there in case anyone else was getting frustrated with the wonky permissions

@aequitas
Copy link

aequitas commented Apr 3, 2025

Thanks for putting this out there. I think I'm running into the same issue.

I've resorted to manually setting mode: 0444 on the configs for now.

edit: looks like there is already some progress reverting the issue: docker/compose#12666 docker/compose#12658 docker/compose#12656

@jeremy-code
Copy link
Author

Thanks for putting this out there. I think I'm running into the same issue.

I've resorted to manually setting mode: 0444 on the configs for now.

edit: looks like there is already some progress reverting the issue: docker/compose#12666 docker/compose#12658 docker/compose#12656

Oh nice I didn't see that — looks like the only affected version is v2.34.0 and it should be fixed on the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment