Elasticsearch
Helm Chart
AccessDenied Exception
Kubernetes
Troubleshooting

Elasticsearch helm chart gives AccessDenied exception

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

The AccessDenied exception when deploying Elasticsearch via a Helm chart is almost always a file permission issue on the persistent volume. Elasticsearch needs to read and write to its data directory, but the container runs as a non-root user (UID 1000 by default) while the persistent volume is often created with root-only permissions. The fix is to configure an initContainer that sets the correct ownership, or adjust the securityContext in the Helm values.

The Error

 
java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/nodes

Or in the pod logs:

 
ERROR: [1] bootstrap checks failed
[1]: [AccessDeniedException[/usr/share/elasticsearch/data/nodes]]

The Elasticsearch process cannot write to its data directory because the filesystem permissions do not allow the Elasticsearch user (UID 1000) to access it.

Root Cause

  1. Elasticsearch runs as user elasticsearch (UID 1000, GID 1000) by default
  2. Persistent volumes provisioned by cloud providers (EBS, GCE PD, Azure Disk) are often created with root:root ownership
  3. The Elasticsearch container cannot write to a root-owned directory as UID 1000

Fix 1: initContainer to Set Permissions (Most Common)

Add an initContainer that runs as root and changes the data directory ownership before Elasticsearch starts:

yaml
1# values.yaml for elastic/elasticsearch Helm chart
2extraInitContainers:
3  - name: fix-permissions
4    image: busybox
5    command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
6    securityContext:
7      runAsUser: 0       # Run as root
8      privileged: true
9    volumeMounts:
10      - name: elasticsearch-master
11        mountPath: /usr/share/elasticsearch/data
bash
helm install elasticsearch elastic/elasticsearch -f values.yaml

Fix 2: Set fsGroup in Security Context

The fsGroup setting tells Kubernetes to change the group ownership of all files in mounted volumes to the specified GID:

yaml
1# values.yaml
2podSecurityContext:
3  fsGroup: 1000
4  runAsUser: 1000
5
6# Or for the Bitnami chart:
7securityContext:
8  enabled: true
9  fsGroup: 1000
10  runAsUser: 1000

When fsGroup: 1000 is set, Kubernetes recursively changes the group of files on the volume to GID 1000 before the container starts. This is the cleanest solution when your cluster and storage class support it.

Fix 3: Set securityContext on the Container

yaml
1# values.yaml
2securityContext:
3  runAsUser: 1000
4  runAsGroup: 1000
5  runAsNonRoot: true
6  capabilities:
7    drop:
8      - ALL

This ensures the container runs as UID/GID 1000. Combined with fsGroup, this covers most permission scenarios.

Fix 4: Use emptyDir for Testing

For development or testing where persistence is not needed:

yaml
1# values.yaml
2persistence:
3  enabled: false
4
5# OR use emptyDir explicitly
6volumeClaimTemplate:
7  accessModes: ["ReadWriteOnce"]
8  resources:
9    requests:
10      storage: 30Gi

emptyDir volumes are writable by default and do not have the permission issues of persistent volumes. Data is lost when the pod is deleted.

Debugging Steps

bash
1# 1. Check pod status and events
2kubectl describe pod elasticsearch-master-0
3
4# 2. Check logs for the AccessDenied error
5kubectl logs elasticsearch-master-0
6
7# 3. Check the volume permissions by exec-ing into the pod
8kubectl exec -it elasticsearch-master-0 -- ls -la /usr/share/elasticsearch/data
9# If owned by root:root, the fix-permissions initContainer is needed
10
11# 4. Check the init container logs
12kubectl logs elasticsearch-master-0 -c fix-permissions
13
14# 5. Check the PVC and PV status
15kubectl get pvc
16kubectl get pv
17
18# 6. Check the storage class
19kubectl get storageclass

Helm Chart-Specific Configurations

Elastic Official Chart (elastic/elasticsearch)

yaml
1# values.yaml
2extraInitContainers:
3  - name: fix-permissions
4    image: busybox
5    command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
6    securityContext:
7      runAsUser: 0
8    volumeMounts:
9      - name: elasticsearch-master
10        mountPath: /usr/share/elasticsearch/data
11
12podSecurityContext:
13  fsGroup: 1000
14  runAsUser: 1000

Bitnami Chart (bitnami/elasticsearch)

yaml
1# values.yaml
2master:
3  persistence:
4    enabled: true
5    size: 30Gi
6  securityContext:
7    enabled: true
8    fsGroup: 1000
9    runAsUser: 1000
10
11volumePermissions:
12  enabled: true  # Bitnami charts have built-in permission fixing

ECK (Elastic Cloud on Kubernetes)

yaml
1apiVersion: elasticsearch.k8s.elastic.co/v1
2kind: Elasticsearch
3metadata:
4  name: my-cluster
5spec:
6  version: 8.11.0
7  nodeSets:
8    - name: default
9      count: 3
10      podTemplate:
11        spec:
12          initContainers:
13            - name: fix-permissions
14              command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
15              securityContext:
16                runAsUser: 0
17              volumeMounts:
18                - name: elasticsearch-data
19                  mountPath: /usr/share/elasticsearch/data
20      volumeClaimTemplates:
21        - metadata:
22            name: elasticsearch-data
23          spec:
24            accessModes: ["ReadWriteOnce"]
25            resources:
26              requests:
27                storage: 50Gi

Common Pitfalls

  • initContainer volume mount name mismatch: The volumeMounts.name in the initContainer must match the actual volume name defined by the Helm chart. Check the chart's templates to find the correct volume name (e.g., elasticsearch-master for the Elastic chart, data for Bitnami).
  • PodSecurityPolicy blocking privileged initContainers: If your cluster has PodSecurityPolicies (PSPs) or OPA Gatekeeper policies that block privileged containers or root users, the fix-permissions initContainer will fail. Create a PSP exception or use fsGroup instead.
  • Storage class not supporting fsGroup: Some storage classes (notably certain NFS provisioners) do not respect the fsGroup setting. In this case, the initContainer approach is the only reliable solution.
  • Elasticsearch version differences: Elasticsearch 7.x and 8.x use different UIDs. Verify the correct UID by checking the Dockerfile or running kubectl exec -- id inside the pod. Using the wrong UID leaves the permission problem unsolved.
  • Forgetting to set permissions on all data paths: Elasticsearch may use multiple paths (/usr/share/elasticsearch/data, /usr/share/elasticsearch/logs, /usr/share/elasticsearch/config). Ensure all writable paths have correct ownership, especially if you mount separate volumes for logs or snapshots.

Summary

  • The AccessDenied exception is caused by persistent volumes owned by root while Elasticsearch runs as UID 1000
  • Fix with an initContainer that runs chown -R 1000:1000 on the data directory
  • Alternatively, set fsGroup: 1000 in podSecurityContext to let Kubernetes handle ownership
  • Bitnami charts have volumePermissions.enabled: true as a built-in option
  • Always verify volume mount names match between the initContainer and the chart's volume definitions
  • Use kubectl exec -- ls -la to confirm file ownership on the data directory

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.