When I added Elasticsearch to the Cloudapps Docker Compose stack, the container refused to start. Elasticsearch runs as a non-root user - UID 1000, GID 0 - and the data directory, mounted as a Docker volume, was owned by root.
Years ago Elasticsearch still had a system property es.insecure.allow.root to bypass this, but they removed it - rightfully so, even if the discussion was controversial. Security over convenience. But it means the deployment needs to prepare volume ownership before the container starts.
Manually fixing permissions on the host works once, but breaks the next time someone provisions the server from scratch.
Preparing volumes with depends_on
A throwaway init container can fix ownership before the actual service starts. Docker Compose's depends_on with condition: service_completed_successfully makes this declarative:
volumes:
elasticsearch-data:
driver: local
driver_opts:
type: none
o: bind
device: /srv/data/elasticsearch
services:
elasticsearch-volume-init:
image: alpine
volumes:
- elasticsearch-data:/tmp/change-ownership
command: chown --recursive 1000:0 /tmp/change-ownership
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2
depends_on:
elasticsearch-volume-init:
condition: service_completed_successfully
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
# ... Elasticsearch config (memory, security, etc.)
Yaml
The elasticsearch-volume-init service runs chown on the mounted volume and exits. The elasticsearch service only starts after the init container succeeds. If permissions are already correct, the chown is a no-op - it runs on every docker compose up and needs no external provisioning scripts.
The pattern works for any non-root container that writes to a mounted volume. Match the UID and GID to whatever user the target container runs as.
Credit to Pratik Chowdhury for the original idea.