Run OpenDJ in Kubernetes
This recipe runs one directory server with persistent storage in a local Minikube cluster. It requires minikube, kubectl, and a default StorageClass that supports dynamic volume provisioning. It does not configure replication; increasing the replica count alone does not create a replicated directory.
minikube start --cpus=4 --memory=8g
kubectl create namespace opendj-demo
kubectl -n opendj-demo create secret generic opendj-root \
--from-literal=password='DemoPassword123!'
Create the Service and StatefulSet
Save the following as opendj.yaml. The root password comes from the Secret. A PersistentVolumeClaim keeps the instance under /opt/opendj/data, the same location described in Running OpenDJ in Docker. fsGroup lets the non-root server write to the mounted volume on storage that supports Kubernetes volume ownership management.
apiVersion: v1
kind: Service
metadata:
name: opendj
spec:
clusterIP: None
selector:
app: opendj
ports:
- name: ldap
port: 1389
- name: ldaps
port: 1636
- name: admin
port: 4444
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: opendj
spec:
serviceName: opendj
replicas: 1
selector:
matchLabels:
app: opendj
template:
metadata:
labels:
app: opendj
spec:
terminationGracePeriodSeconds: 60
securityContext:
fsGroup: 1001
containers:
- name: opendj
image: openidentityplatform/opendj:5.2.0
env:
- name: ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: opendj-root
key: password
- name: ADD_BASE_ENTRY
value: --addBaseEntry
ports:
- containerPort: 1389
- containerPort: 1636
- containerPort: 4444
readinessProbe:
exec:
command: [/opt/opendj/healthcheck.sh]
periodSeconds: 10
timeoutSeconds: 30
volumeMounts:
- name: data
mountPath: /opt/opendj/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
kubectl -n opendj-demo apply -f opendj.yaml
kubectl -n opendj-demo rollout status statefulset/opendj --timeout=600s
kubectl -n opendj-demo get pods,pvc
Wait for the pod to be Ready. The readiness probe checks completed bootstrap and LDAP availability; a listening TCP port alone is insufficient during first startup. If the claim stays Pending, check the StorageClass. If the pod does not become Ready, inspect kubectl -n opendj-demo logs opendj-0 and kubectl -n opendj-demo describe pod opendj-0.
Verify the Directory and Persistence
kubectl -n opendj-demo exec opendj-0 -- /opt/opendj/bin/ldapsearch \
--hostname localhost --port 1389 \
--bindDN 'cn=Directory Manager' --bindPassword 'DemoPassword123!' \
--baseDN dc=example,dc=com --searchScope base '(objectClass=*)' dn
Expect dn: dc=example,dc=com. Entries added to this instance remain on the claim when the pod is replaced. To verify, add a test entry, delete only the pod with kubectl -n opendj-demo delete pod opendj-0, wait for the replacement to become Ready, and search for the entry again. Do not delete the claim during this check.
Deleting the StatefulSet normally retains its claim. Deleting the demonstration namespace with kubectl delete namespace opendj-demo also deletes its claims and can delete the backing storage, depending on its reclaim policy.