I’m struggling while setting up Hashicorp Vault on Docker using Docker Compose in a dedicated Debian 13.3 VM.
Docker engine version is 29.1.4, docker compose is 2.26.1-4 , Vault image is 1.21.
By default, Vault image is using uid:gid 100:100, however this one is already existing on my Debian VM. To avoid any conflict, I created a vault-system user and group where uid:gid are 1001:1001.
At the moment, here’s how I configured permissions on my project folder. All folders are empty except tls, which contains certificate and private key :
root@my-server:/opt/vault-infra# ll . && ll tls/
total 20K
drwxr-x--- 2 vault-system vault-system 4,0K 16 janv. 09:48 config
drwx------ 2 vault-system vault-system 4,0K 16 janv. 09:23 data
-rw-r--r-- 1 vault-system vault-system 547 16 janv. 09:50 docker-compose.yml
drwxr-x--- 2 vault-system vault-system 4,0K 13 janv. 17:44 logs
drwx------ 2 vault-system vault-system 4,0K 15 janv. 15:37 tls
total 8,0K
-rw-r--r-- 1 vault-system vault-system 2,7K 15 janv. 14:22 tls.crt
-rw------- 1 vault-system vault-system 3,2K 15 janv. 14:22 tls.key
- Test 1 : Setup using a simple Docker Compose file and configuration - the default user is the one in defined in official vault image so
uid:gidis100:100by default.
#docker-compose.yml :
services:
vault:
image: hashicorp/vault:1.21
cap_add:
- IPC_LOCK
volumes:
- /opt/vault-infra/tls:/vault/tls:ro
- /opt/vault-infra/data:/vault/data
- /opt/vault-infra/config:/vault/config
environment:
VAULT_LOCAL_CONFIG: |
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/vault/tls/tls.crt"
tls_key_file = "/vault/tls/tls.key"
}
storage "file" {
path = "/vault/data"
}
command: server
#################################
root@my-server:/opt/vault-infra# docker compose up -d
WARN[0000] No services to build
[+] up 1/1
✔ Container vault-infra-vault-1 Recreated 0.2s
root@my-server:/opt/vault-infra# docker logs vault-infra-vault-1
WARNING! Unable to read storage migration status.
2026-01-16T09:23:37.346Z [INFO] proxy environment: http_proxy="" https_proxy="" no_proxy=""
2026-01-16T09:23:37.346Z [WARN] storage migration check error: error="open /vault/data/core/_migration: permission denied"
=> Test 1 result: Vault can’t be set up as there’s a permission denied which is expected as uid:gid is the default one.
Also at this moment, I noticed that config folder seems to have been changed to uid 100 (which is dhcpcd in my VM) and local.json config has been created.
root@my-server:/opt/vault-infra# ls -l
total 20
drwxr-x--- 2 dhcpcd 1000 4096 16 janv. 10:23 config
drwx------ 2 vault-system vault-system 4096 16 janv. 09:23 data
-rw-r--r-- 1 vault-system vault-system 526 16 janv. 10:23 docker-compose.yml
drwxr-x--- 2 vault-system vault-system 4096 13 janv. 17:44 logs
drwx------ 2 vault-system vault-system 4096 15 janv. 15:37 tls
root@my-server:/opt/vault-infra# tree
./
├── config/
│ └── local.json
├── data/
├── logs/
├── tls/
│ ├── tls.crt
│ └── tls.key
└── docker-compose.yml
- Test 2 : Setup using the same Docker Compose file but user’s
uid:gidis updated to1001:1001.
#docker-compose.yml :
services:
vault:
image: hashicorp/vault:1.21
user: "1001:1001"
cap_add:
- IPC_LOCK
volumes:
- /opt/vault-infra/tls:/vault/tls:ro
- /opt/vault-infra/data:/vault/data
- /opt/vault-infra/config:/vault/config
environment:
VAULT_LOCAL_CONFIG: |
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/vault/tls/tls.crt"
tls_key_file = "/vault/tls/tls.key"
}
storage "file" {
path = "/vault/data"
}
command: server
##################################
root@my-server:/opt/vault-infra# docker compose up -d
WARN[0000] No services to build
[+] up 2/2
✔ Network vault-infra_default Created 0.0s
✔ Container vault-infra-vault-1 Created 0.1s
root@my-server:/opt/vault-infra# docker logs vault-infra-vault-1
chown: /vault/config/local.json: Operation not permitted
chown: /vault/config: Operation not permitted
chown: /vault/config: Operation not permitted
Could not chown /vault/config (may not have appropriate permissions)
unable to set CAP_SETFCAP effective capability: Operation not permitted
root@my-server:/opt/vault-infra# ls -l
total 20
drwxr-x--- 2 vault-system vault-system 4096 16 janv. 10:26 config
drwx------ 2 vault-system vault-system 4096 16 janv. 09:23 data
-rw-r--r-- 1 vault-system vault-system 548 16 janv. 10:26 docker-compose.yml
drwxr-x--- 2 vault-system vault-system 4096 13 janv. 17:44 logs
drwx------ 2 vault-system vault-system 4096 15 janv. 15:37 tls
root@my-server:/opt/vault-infra# tree
./
├── config/
│ └── local.json
├── data/
├── logs/
├── tls/
│ ├── tls.crt
│ └── tls.key
└── docker-compose.yml
=> Test 2 result : We can see there’s still permissions issue, the image is kind of trying to set chown operations that failed, and local.json file has been created again.
- Test 3 : Using
group_addin docker compose, it means that default user (100:100) will be used, and will be add to group 1001
#docker-compose.yml :
services:
vault:
image: hashicorp/vault:1.21
group_add:
- "1001"
cap_add:
- IPC_LOCK
volumes:
- /opt/vault-infra/tls:/vault/tls:ro
- /opt/vault-infra/data:/vault/data
- /opt/vault-infra/config:/vault/config
environment:
VAULT_LOCAL_CONFIG: |
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/vault/tls/tls.crt"
tls_key_file = "/vault/tls/tls.key"
}
storage "file" {
path = "/vault/data"
}
command: server
##############################
root@my-server:/opt/vault-infra# docker compose up -d
WARN[0000] No services to build
[+] up 2/2
✔ Network vault-infra_default Created 0.0s
✔ Container vault-infra-vault-1 Created 0.1s
root@my-server:/opt/vault-infra# docker logs vault-infra-vault-1
2026-01-16T09:28:48.624Z [INFO] proxy environment: http_proxy="" https_proxy="" no_proxy=""
2026-01-16T09:28:48.624Z [WARN] storage migration check error: error="open /vault/data/core/_migration: permission denied"
WARNING! Unable to read storage migration status.
2026-01-16T09:28:50.624Z [WARN] storage migration check error: error="open /vault/data/core/_migration: permission denied"
2026-01-16T09:28:52.624Z [WARN] storage migration check error: error="open /vault/data/core/_migration: permission denied"
root@my-server:/opt/vault-infra# ls -l
total 20
drwxr-x--- 2 dhcpcd 1000 4096 16 janv. 10:28 config
drwx------ 2 vault-system vault-system 4096 16 janv. 09:23 data
-rw-r--r-- 1 vault-system vault-system 556 16 janv. 10:28 docker-compose.yml
drwxr-x--- 2 vault-system vault-system 4096 13 janv. 17:44 logs
drwx------ 2 vault-system vault-system 4096 15 janv. 15:37 tls
root@my-server:/opt/vault-infra# tree
./
├── config/
│ └── local.json
├── data/
├── logs/
├── tls/
│ ├── tls.crt
│ └── tls.key
└── docker-compose.yml
5 directories, 4 files
=> Test 3 result : config folder owner has changed to uid 100 (dhcpcd) , local.json has been created again and there’s permission issue on data folder.
- Test 4 Set up my container using a one-liner Docker
root@my-server:/opt/vault-infra# docker run -it --rm --user 100:100 -v ./tls/:/vault/tls -v ./data/:/vault/data hashicorp/vault:1.21 sh
/ $ ls -l /vault/
total 20
drwxr-xr-x 2 vault vault 4096 Jan 6 16:40 config
drwx------ 2 1001 1001 4096 Jan 16 08:23 data
drwxr-xr-x 2 vault vault 4096 Jan 6 16:40 file
drwxr-xr-x 2 vault vault 4096 Jan 6 16:40 logs
drwx------ 2 1001 1001 4096 Jan 15 14:37 tls
/ $ ls -l /vault/tls/
ls: can't open '/vault/tls/': Permission denied
total 0
/ $ ls -l /vault/data
ls: can't open '/vault/data': Permission denied
total 0
=> Test 4 result : result is expected as user 100:100 don’t have any permissions on tls and data folders.
- Test 5 : using a one-liner Docker and using uid:gid 1001:1001
root@my-server:/opt/vault-infra# docker run -it --rm --user 1001:1001 -v ./tls/:/vault/tls -v ./data/:/vault/data hashicorp/vault:1.21 sh
~ $ ls -l /vault/
total 20
drwxr-xr-x 2 vault vault 4096 Jan 6 16:40 config
drwx------ 2 1001 1001 4096 Jan 16 08:23 data
drwxr-xr-x 2 vault vault 4096 Jan 6 16:40 file
drwxr-xr-x 2 vault vault 4096 Jan 6 16:40 logs
drwx------ 2 1001 1001 4096 Jan 15 14:37 tls
~ $ ls -l /vault/config/
total 0
~ $ ls -l /vault/data/
total 0
~ $ ls -l /vault/tls/
total 8
-rw-r--r-- 1 1001 1001 2756 Jan 16 09:26 tls.crt
-rw------- 1 1001 1001 3272 Jan 16 09:26 tls.key
~ $ cat /vault/tls/tls.crt
-----BEGIN CERTIFICATE-----
MIIHxzCCBa+gAwIBAgITNwAAAGJx2Gx25yK6/gAAAAAAYjANBgkqhkiG9w0BAQsF
ADB4MQswCQYDVQQGEwJGUjEMMAoGA1UECBMDQWluMRgwFgYDVQQHEw9Cb3VyZyBl
=> Test 5 result : result is expected as UID:GID is updated => it works (note, it also works if GID only is updated)
My question is : how can I handle permissions and correct user in order to make Vault up without cutting security corners ?