UID/GID mapping in OCI container

Hey, I’m having some difficulty mounting a file from a custom volume into a Keycloak OCI container. The UID/GID of the file in the container don’t match the container’s user, causing permission issues. I’ve seen a couple of related topics on here and GitHub but haven’t been able to figure out a solution.

For context: I’m on IncusOS, using the default project and local storage pool. The Keycloak container user has UID 1000.

Steps to reproduce:

# Create container (custom entrypoint for debugging)
incus remote add quay https://quay.io --protocol oci --public
incus init quay:keycloak/keycloak kc --config oci.entrypoint=bash
# Create and attach volume
incus storage volume create local kc-config
echo 'kc config' | incus storage volume file push - local kc-config/keycloak.conf
incus storage volume attach local kc-config/keycloak.conf kc /opt/keycloak/conf/keycloak.conf
# Check keycloak.conf in container
incus start kc
incus exec kc -- ls --numeric-uid-gid /opt/keycloak/conf/keycloak.conf

Output:

-rw-rw---- 1 501 20 10 Jan 25 15:10 /opt/keycloak/conf/keycloak.conf

So the file is mounted with UID/GID 501/20 and mode 0660, which seems random to me. How can I get the permissions/mode to something like 1000/1000/0755?

Following other posts, I’ve tried playing with the options security.shifted=true initial.uid=1000 initial.gid=1000 initial.mode=0755 on the volume and did not see any difference.

Volume config (incus storage volume show local kc-config):

config:
  volatile.idmap.last: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":1000000000},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":1000000000}]'
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":1000000000},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":1000000000}]'
description: ""
name: kc-config
type: custom
used_by:
- /1.0/instances/kc
location: none
content_type: filesystem
project: default
created_at: 2026-01-25T15:10:17.03105566Z

Instance config (incus config show kc):

architecture: x86_64
config:
  environment.HOME: /opt/keycloak
  environment.KC_RUN_IN_CONTAINER: "true"
  environment.LANG: en_US.UTF-8
  environment.PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  environment.TERM: xterm
  image.architecture: x86_64
  image.description: quay.io/keycloak/keycloak (OCI)
  image.id: keycloak/keycloak
  image.type: oci
  oci.cwd: /
  oci.entrypoint: bash
  oci.gid: "0"
  oci.uid: "1000"
  volatile.base_image: 2a18f117c9f2dcac6430451d3a5117dbf4849a05619fa9a505e666519cd3c1d7
  volatile.cloud-init.instance-id: e75e5a35-66cb-4e7c-9504-3a7d2a614888
  volatile.container.oci: "true"
  volatile.eth0.host_name: vetha073ba1f
  volatile.eth0.hwaddr: 10:66:6a:58:70:d5
  volatile.idmap.base: "0"
  volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":1000000000},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":1000000000}]'
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":1000000000},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":1000000000}]'
  volatile.last_state.idmap: '[]'
  volatile.last_state.power: RUNNING
  volatile.uuid: 0b810538-8260-40fd-990d-e99cf3f65f4b
  volatile.uuid.generation: 0b810538-8260-40fd-990d-e99cf3f65f4b
devices:
  kc-config/keycloak.conf:
    path: /opt/keycloak/conf/keycloak.conf
    pool: local
    source: kc-config/keycloak.conf
    type: disk
ephemeral: false
profiles:
- default
stateful: false
description: ""

Thanks in advance :slight_smile:

Hmm, with what you’re describing, Incus would have mounted the file as 0:0, not 501:20, so chances are that there’s something in the container that did a chown on that file.

Can you maybe run ps fauxww in the container to see what user the various things are running as in there and if 501:20 may be meaningful somehow?

Hmm, there’s no ps in the Keycloak image, so I tried replicating the issue with docker.io/debian and I actually get the same behavior — keycloak.conf is mounted as 501:20. I get this output from ps fauxww (after installing procps in the container):

root@debian:/# ps fauxww
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0 6535060 19764 ?       Ss   16:21   0:00 init
root          21  0.0  0.0   4336  2304 pts/0    Ss   16:21   0:00 bash
root         149  0.0  0.0   6400  2832 pts/0    R+   16:23   0:00  \_ ps fauxww
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus storage volume create local kc-config
Storage volume kc-config created
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ echo 'kc config' | incus storage volume file push - local kc-config/keycloak.conf
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus create images:debian/13 test --target sateda
Creating test
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus config device add test kc disk pool=local source=kc-config/keycloak.conf path=/mnt/keycloak.conf
Device kc added to test
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus start test
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus exec test bash
root@test:~# ls -lh /mnt/
total 4.5K
-rw------- 1 1000 1000 10 Jan 26 00:03 keycloak.conf
root@test:~# incus ^C
root@test:~# 
exit
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus stop test
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus storage volume file delete local kc-config/keycloak.conf
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ echo 'kc config' | incus storage volume file push - local kc-config/keycloak.conf --uid=1234 --gid=1234
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus start test
stgraber@castiana:~ (incus:s-shf-cluster/stgraber-dev)$ incus exec test bash
root@test:~# ls -lh /mnt/
total 4.5K
-rw------- 1 1234 1234 10 Jan 26 00:05 keycloak.conf
root@test:~# 

Also on IncusOS.

echo 'kc config' | incus storage volume file push - local kc-config/keycloak.conf --uid=1234 --gid=1234

Thanks, I’d missed the uid/gid flags in the push command. When I set those to 1000, keycloak.conf shows up with the correct ownership in the Keycloak container and the permission issues are resolved.

Is that the expected way to do this? I’m surprised because I’d expect that files in a storage volume should have their UID/GID mapped automatically when attached. Otherwise I don’t see how you would, say, mount one file to two containers where each runs with a different UID.