Skip to main content
Version: v24.1

Kubernetes (kind)

This guide walks you through installing a highly available Dgraph cluster on Kubernetes using kind (Kubernetes in Docker). kind is ideal for local development and testing, as it creates multi-node Kubernetes clusters that closely simulate production environments.

Architecture and Key Benefits

Cluster Architecture

This setup creates a highly available Dgraph cluster with:

  • 1 control plane node - Manages the Kubernetes cluster
  • 3 worker nodes - Run Dgraph workloads
  • 3 Dgraph Zero pods - One per worker node (cluster coordination)
  • 3 Dgraph Alpha pods - One per worker node (data storage and queries)

Each worker node runs exactly one Zero and one Alpha pod, ensuring high availability while maximizing resource efficiency.

Storage Architecture

For local development, data is mapped from your host machine to the kind cluster:

Host Machine (Mac/Linux)          kind Worker Nodes          Dgraph Pods
───────────────────────── ────────────────── ────────────
$HOME/dgraph-data/ /dgraph-data/ /dgraph/
├── alpha-0/ ──────────────────> ├── alpha-0/ ─────────> alpha-0 pod
├── alpha-1/ ──────────────────> ├── alpha-1/ ─────────> alpha-1 pod
└── alpha-2/ ──────────────────> └── alpha-2/ ─────────> alpha-2 pod

This allows you to directly inspect and experiment with Dgraph's data files, including the p folders (posting lists).

Key Benefits

  • High Availability: Each Zero and Alpha pod runs on a different node, ensuring fault tolerance
  • Resource Efficiency: Uses only 3 nodes instead of 6, with one Zero and one Alpha per node
  • Production-Like: Multi-node setup closely simulates real Kubernetes environments
  • Local Development: Perfect for testing and development without cloud costs
  • Data Persistence: Persistent storage ensures data survives pod restarts
  • Direct Data Access: Local disk mapping allows direct inspection and experimentation with data files

Prerequisites

Before you begin, ensure you have the following tools installed:

  • Docker - Running and accessible
  • kubectl - Kubernetes command-line tool
  • Helm - Kubernetes package manager
  • kind - Kubernetes in Docker

Install Prerequisites

On macOS, you can install these tools using Homebrew:

brew install kind kubectl helm

Verify your installations:

docker --version
kubectl version --client
helm version
kind --version

Step 1: Create Local Data Directories

Create directories on your host machine for each Alpha pod's data:

mkdir -p $HOME/dgraph-data/alpha-0
mkdir -p $HOME/dgraph-data/alpha-1
mkdir -p $HOME/dgraph-data/alpha-2

Note: Use absolute paths (e.g., /Users/your-username/dgraph-data/alpha-0). Shell shortcuts like ~ do not work in Kubernetes YAML.

Step 2: Create kind Cluster with Volume Mounts

Create a kind cluster configuration that mounts your local data directory into all worker nodes.

Create a file named kind-config.yaml:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
extraMounts:
- hostPath: /Users/your-username/dgraph-data # Replace with your absolute path
containerPath: /dgraph-data
- role: worker
extraMounts:
- hostPath: /Users/your-username/dgraph-data # Replace with your absolute path
containerPath: /dgraph-data
- role: worker
extraMounts:
- hostPath: /Users/your-username/dgraph-data # Replace with your absolute path
containerPath: /dgraph-data

Important: Replace /Users/your-username/dgraph-data with your actual absolute path. You can use:

echo $HOME/dgraph-data

Then create the cluster:

kind create cluster --config kind-config.yaml

What happens during cluster creation:

  • Docker containers are created for each node (1 control plane + 3 workers)
  • Kubernetes control plane components are set up in the control plane node
  • Worker nodes join the cluster and become ready
  • Your local dgraph-data directory is mounted into each worker node at /dgraph-data
  • A kubectl context named kind-kind is automatically created and set as default

Verify Cluster Creation

Verify that your cluster is running correctly:

kubectl get nodes

Expected output:

NAME                 STATUS   ROLES           AGE   VERSION
kind-control-plane Ready control-plane 2m v1.27.x
kind-worker Ready <none> 2m v1.27.x
kind-worker2 Ready <none> 2m v1.27.x
kind-worker3 Ready <none> 2m v1.27.x

All nodes should show STATUS: Ready.

Step 3: Create StorageClass

Create a StorageClass for local persistent volumes:

kubectl apply -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
EOF

Verify the StorageClass was created:

kubectl get storageclass

Step 4: Create PersistentVolumes

Create PersistentVolumes that map to your local directories. Each PV explicitly binds to a specific PVC using claimRef:

kubectl apply -f - <<EOF
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: dgraph-dgraph-alpha-pv-0
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
claimRef:
namespace: default
name: datadir-dgraph-dgraph-alpha-0
hostPath:
path: /dgraph-data/alpha-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: dgraph-dgraph-alpha-pv-1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
claimRef:
namespace: default
name: datadir-dgraph-dgraph-alpha-1
hostPath:
path: /dgraph-data/alpha-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: dgraph-dgraph-alpha-pv-2
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
claimRef:
namespace: default
name: datadir-dgraph-dgraph-alpha-2
hostPath:
path: /dgraph-data/alpha-2
EOF

How Pod-to-Folder Binding Works:

The binding is ensured through a predictable chain:

  1. StatefulSet Pod Naming: Pods are named dgraph-dgraph-alpha-0, -1, -2
  2. StatefulSet PVC Creation: The volumeClaimTemplate creates PVCs: datadir-dgraph-dgraph-alpha-0, -1, -2
  3. PV claimRef Binding: Each PV's claimRef explicitly names the PVC it should bind to
  4. Complete Chain: alpha-0 pod → PVC datadir-dgraph-dgraph-alpha-0 → PV dgraph-dgraph-alpha-pv-0 → /dgraph-data/alpha-0

The claimRef creates an explicit, one-to-one binding ensuring alpha-0 always uses the alpha-0 folder.

Verify the PVs were created:

kubectl get pv

You should see three PVs in Available status.

Step 5: Add Dgraph Helm Repository

Add the official Dgraph Helm chart repository:

helm repo add dgraph https://charts.dgraph.io
helm repo update

Verify the repository was added:

helm repo list

Step 6: Create Helm Values File

Create a Helm values file for high availability configuration:

zero:
replicaCount: 3
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values: ["zero"]
topologyKey: kubernetes.io/hostname
persistence:
enabled: true
storageClass: local-storage

alpha:
replicaCount: 3
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values: ["alpha"]
topologyKey: kubernetes.io/hostname
persistence:
enabled: true
storageClass: local-storage

Save this as dgraph-ha-values.yaml.

Configuration explanation:

  • replicaCount: 3 - Creates 3 replicas of Zero and Alpha pods
  • podAntiAffinity - Ensures no two Zero pods (or Alpha pods) are scheduled on the same node
  • topologyKey: kubernetes.io/hostname - Uses node hostname as the topology domain, spreading pods across nodes
  • persistence.enabled: true - Enables persistent storage for data durability
  • storageClass: local-storage - Uses the pre-created StorageClass for local volumes

Step 7: Install Dgraph

Install the Dgraph Helm chart with your custom values:

helm install dgraph dgraph/dgraph -f dgraph-ha-values.yaml

Wait for all pods to be ready:

kubectl wait --for=condition=Ready pod -l component=alpha --timeout=300s
kubectl wait --for=condition=Ready pod -l component=zero --timeout=300s

Step 8: Verify Installation

Check Pod Status

Verify that all Dgraph pods are running and distributed across nodes:

kubectl get pods -o wide

Expected output:

NAME               READY   STATUS    NODE            AGE
dgraph-alpha-0 1/1 Running kind-worker 2m
dgraph-alpha-1 1/1 Running kind-worker2 2m
dgraph-alpha-2 1/1 Running kind-worker3 2m
dgraph-zero-0 1/1 Running kind-worker 2m
dgraph-zero-1 1/1 Running kind-worker2 2m
dgraph-zero-2 1/1 Running kind-worker3 2m

Each of your 3 worker nodes should have exactly one Zero pod and one Alpha pod.

Verify PVC to PV Bindings

Check that each PVC is bound to the correct PV:

kubectl get pvc
kubectl get pv

Each PVC should show STATUS: Bound and the VOLUME column should match a PV name.

Verify Data Directories

Check that data directories are accessible on your host:

ls -la $HOME/dgraph-data/alpha-0/
ls -la $HOME/dgraph-data/alpha-1/
ls -la $HOME/dgraph-data/alpha-2/

After Dgraph starts writing data, you'll see:

  • p/ - Posting lists directory (contains the actual graph data)
  • w/ - Write-ahead log (WAL) directory

Understanding StatefulSets

Dgraph Alpha and Zero use StatefulSets (not Deployments) because they require:

  • Stable pod identities: Pods are named predictably (e.g., dgraph-dgraph-alpha-0, dgraph-dgraph-alpha-1)
  • Persistent storage: Each pod gets its own PersistentVolumeClaim (PVC)
  • Ordered deployment: Pods are created and terminated in a specific order

Important StatefulSet characteristics:

  • StatefulSets do not allow most spec changes after creation (e.g., volumeClaimTemplates)
  • Each pod gets a unique PVC based on the pod's index (e.g., datadir-dgraph-dgraph-alpha-0)
  • Pods maintain their identity even after restarts

Step 9: Access Dgraph

Port Forward to Alpha Service

Access Dgraph's HTTP endpoint:

kubectl port-forward svc/dgraph-alpha-public 8080:8080

In another terminal, access the gRPC endpoint:

kubectl port-forward svc/dgraph-alpha-public 9080:9080

Access Ratel UI (if enabled)

If Ratel is included in your Helm chart, port-forward to access the UI:

kubectl port-forward svc/dgraph-ratel 8000:8000

Then open http://localhost:8000 in your browser and enter http://localhost:8080 as the Dgraph connection string.

Test the Connection

Test that Dgraph is responding:

curl http://localhost:8080/health | jq

You should see cluster health information.

Data Directory Mapping Reference

Alpha PodHost Path (Mac)Container Path (kind node)PV PathPVC Name
alpha-0$HOME/dgraph-data/alpha-0/dgraph-data/alpha-0/dgraph-data/alpha-0datadir-dgraph-dgraph-alpha-0
alpha-1$HOME/dgraph-data/alpha-1/dgraph-data/alpha-1/dgraph-data/alpha-1datadir-dgraph-dgraph-alpha-1
alpha-2$HOME/dgraph-data/alpha-2/dgraph-data/alpha-2/dgraph-data/alpha-2datadir-dgraph-dgraph-alpha-2

Important Notes:

  • All worker nodes share the same mount point (/dgraph-data), but each PV uses a unique subdirectory
  • Data persists even if you stop/start the kind cluster
  • To completely remove data, delete the host directories: rm -rf $HOME/dgraph-data/alpha-*
  • The p folders contain BadgerDB files that represent your graph data
  • Use absolute paths for hostPath in PVs (shell shortcuts like ~ do not work in YAML)

Managing the Cluster

Stop the Cluster

To stop the cluster without deleting it (preserves data and configuration):

docker stop $(docker ps -q --filter "name=kind")

Start the Cluster

To start the cluster again:

docker start $(docker ps -a -q --filter "name=kind")

After starting, verify nodes are ready:

kubectl get nodes

All nodes should show STATUS: Ready after a few moments.

View Helm Release

Check your Helm release status:

helm list

View Default Helm Values

To see all available configuration options:

helm show values dgraph/dgraph

Clean Up

Uninstall Dgraph

To remove Dgraph from your cluster:

helm uninstall dgraph

Delete Persistent Volume Claims

Helm does not delete PVCs by default to prevent data loss. Manually delete them if needed:

kubectl delete pvc --selector="app.kubernetes.io/instance=dgraph"

Delete PersistentVolumes

Delete the PVs:

kubectl delete pv dgraph-dgraph-alpha-pv-0 dgraph-dgraph-alpha-pv-1 dgraph-dgraph-alpha-pv-2

Delete the kind Cluster

To completely remove the kind cluster:

kind delete cluster

This deletes all nodes, but your data directories on the host remain. To remove them:

rm -rf $HOME/dgraph-data/alpha-*

Troubleshooting

Pods Not Starting

If pods are stuck in Pending or CrashLoopBackOff:

Check pod logs:

kubectl logs dgraph-dgraph-alpha-0

Check pod events:

kubectl describe pod dgraph-dgraph-alpha-0

Check node resources:

kubectl top nodes

Pods Not Distributed Across Nodes

If pods are scheduled on the same node:

Verify anti-affinity rules:

kubectl get pod dgraph-dgraph-alpha-0 -o yaml | grep -A 10 affinity

Check node labels:

kubectl get nodes --show-labels

Debugging Storage Issues

If data isn't appearing in your host directory:

1. Verify the PV's hostPath is correct:

kubectl get pv dgraph-dgraph-alpha-pv-0 -o jsonpath='{.spec.hostPath.path}'

Ensure paths are absolute (e.g., /Users/username/dgraph-data/alpha-0, not ~/dgraph-data/alpha-0).

2. Check PVC to PV binding:

kubectl get pvc,pv

PVCs should show STATUS: Bound and the VOLUME column should match a PV name.

3. Verify pod volume mounts:

kubectl describe pod dgraph-dgraph-alpha-0 | grep -A 10 "Mounts"

Confirm the mount path matches your configuration (typically /dgraph).

4. Test writing to the mount path:

kubectl exec -it dgraph-dgraph-alpha-0 -- touch /dgraph/test-file

Then verify the file appears in your host directory:

ls -la $HOME/dgraph-data/alpha-0/test-file

5. Verify the binding chain:

# Check which PVC the pod is using
kubectl get pod dgraph-dgraph-alpha-0 -o jsonpath='{.spec.volumes[?(@.name=="datadir")].persistentVolumeClaim.claimName}'

# Check which PV the PVC is bound to
kubectl get pvc datadir-dgraph-dgraph-alpha-0 -o jsonpath='{.spec.volumeName}'

# Check which folder the PV maps to
kubectl get pv dgraph-dgraph-alpha-pv-0 -o jsonpath='{.spec.hostPath.path}'

Common Pitfalls and Solutions

CrashLoopBackOff

Causes:

  • Misconfigured volumes or missing PVs
  • Missing dependencies (Dgraph Alpha requires Zero to be running)
  • Resource limits too low (OOM kills)
  • Incorrect storage class configuration

Solutions:

# Check pod logs for specific errors
kubectl logs dgraph-dgraph-alpha-0

# Check resource usage
kubectl top pod dgraph-dgraph-alpha-0

# Verify Zero is running first
kubectl get pods | grep zero

# Check events
kubectl describe pod dgraph-dgraph-alpha-0

PVCs/PVs Stuck in Terminating

Cause: Finalizers preventing deletion or bound PVCs

Solution - Force delete PVC:

kubectl patch pvc <pvc-name> --type json -p '[{"op": "remove", "path": "/metadata/finalizers"}]'

Solution - Force delete PV:

kubectl patch pv <pv-name> --type json -p '[{"op": "remove", "path": "/metadata/finalizers"}]'

Silent Storage Failures

Symptom: PVCs remain in Pending state without obvious errors

Causes:

  • Missing StorageClass
  • No available PVs matching the PVC requirements
  • StorageClass volumeBindingMode set incorrectly

Solutions:

# Check if StorageClass exists
kubectl get storageclass

# Check PVC status and events
kubectl describe pvc datadir-dgraph-dgraph-alpha-0

# Verify PVs are available
kubectl get pv

# Ensure StorageClass name matches in Helm values
helm get values dgraph | grep storageClass

StatefulSet Won't Update

Cause: StatefulSets don't allow changes to volumeClaimTemplates after creation

Solution: Delete and recreate the StatefulSet (data in PVCs will persist):

# Delete StatefulSet (pods will be recreated)
kubectl delete statefulset dgraph-dgraph-alpha

# Or use Helm upgrade with new values
helm upgrade dgraph dgraph/dgraph -f new-values.yaml

Accessing Pods for Debugging

Access pod shell:

kubectl exec -it dgraph-dgraph-alpha-0 -- /bin/sh

Check logs:

# Current logs
kubectl logs dgraph-dgraph-alpha-0

# Follow logs
kubectl logs -f dgraph-dgraph-alpha-0

# Previous container logs (if pod restarted)
kubectl logs dgraph-dgraph-alpha-0 --previous

Check volume mounts:

kubectl describe pod dgraph-dgraph-alpha-0 | grep -A 10 "Mounts"

List files in data directory:

kubectl exec -it dgraph-dgraph-alpha-0 -- ls -la /dgraph
kubectl exec -it dgraph-dgraph-alpha-0 -- ls -la /dgraph/p

Best Practices

Storage Configuration

  • Use absolute paths for hostPath volumes in PVs (e.g., /Users/username/dgraph-data/alpha-0, not ~/dgraph-data/alpha-0)
  • Pre-create StorageClass and PVs before installing the Helm chart
  • Test volume mounts by writing a file manually to verify the mount works
  • Use Retain reclaim policy on PVs to prevent accidental data loss during cleanup

Resource Management

  • Set appropriate resource requests and limits to avoid OOM crashes:
alpha:
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"

Namespace Isolation

  • Use namespaces to isolate releases and simplify cleanup:
kubectl create namespace dgraph
helm install dgraph dgraph/dgraph -n dgraph -f dgraph-ha-values.yaml

Dependencies

  • Ensure Zero is running before Alpha starts. Dgraph Alpha depends on Zero for cluster coordination.

Useful Commands Reference

TaskCommand
Check pod statuskubectl get pods
Check pod status with nodeskubectl get pods -o wide
Access pod shellkubectl exec -it dgraph-dgraph-alpha-0 -- /bin/sh
Check pod logskubectl logs dgraph-dgraph-alpha-0
Follow pod logskubectl logs -f dgraph-dgraph-alpha-0
Check volume mountskubectl describe pod dgraph-dgraph-alpha-0 | grep -A 10 "Mounts"
Check PVC/PV bindingkubectl get pvc,pv
Check StorageClasskubectl get storageclass
Port-forward servicekubectl port-forward svc/dgraph-alpha-public 8080:8080
Delete StatefulSetkubectl delete statefulset dgraph-dgraph-alpha
Force delete PVkubectl patch pv <pv-name> --type json -p '[{"op": "remove", "path": "/metadata/finalizers"}]'
Check pod eventskubectl describe pod dgraph-dgraph-alpha-0
Check node resourceskubectl top nodes
Check pod resourceskubectl top pod dgraph-dgraph-alpha-0

Next Steps