A Kubernetes operator for managing 389 Directory Server instances.
It automates deployment, configuration, and lifecycle management of 389DS via a DirectoryService custom resource.
The ds-operator provides a declarative way to run 389 Directory Server on Kubernetes.
Define a DirectoryService CR and the operator handles:
- StatefulSet management -- ordered pod startup with stable network identities
- Persistent storage -- automatic PVC provisioning for the
/datavolume - Suffix (database backend) creation -- configure LDAP suffixes declaratively
- Directory Manager credentials -- auto-generated or user-provided via Secret reference
- Service exposure -- headless Service for pod DNS and ClusterIP Service for LDAP/LDAPS
- Version-aware upgrades -- health-gated StatefulSet rollouts with rollback
apiVersion: dirsrv.operator.port389.org/v1alpha1
kind: DirectoryService
metadata:
name: my-ds
spec:
image: quay.io/389ds/dirsrv:3.1.0
version: 3.1.0
replicas: 2
suffixes:
- name: userroot
dn: "dc=example,dc=com"
storage:
size: "10Gi"
ports:
ldap: 3389
ldaps: 3636- Go 1.24+
- Docker or Podman
- kubectl v1.28+
- Access to a Kubernetes v1.28+ cluster
# Clone and set up pre-commit hooks
git clone https://github.com/389ds/ds-operator.git
cd ds-operator
make setup-hooksBuild and push the operator image:
make docker-build docker-push IMG=ghcr.io/389ds/ds-operator:v0.0.1Install the CRDs into the cluster:
make installDeploy the operator to the cluster:
make deploy IMG=ghcr.io/389ds/ds-operator:v0.0.1NOTE: If you encounter RBAC errors, you may need to grant yourself cluster-admin privileges or be logged in as admin.
Create a DirectoryService instance:
kubectl apply -f - <<EOF
apiVersion: dirsrv.operator.port389.org/v1alpha1
kind: DirectoryService
metadata:
name: example-ds
spec:
image: quay.io/389ds/dirsrv:3.1.0
version: 3.1.0
replicas: 1
suffixes:
- name: userroot
dn: "dc=example,dc=com"
EOFCheck the status:
kubectl get dirsrvChange spec.version and spec.image together. The version must use semantic
version format. Patch and minor upgrades are supported. Major upgrades and
minor or major downgrades require exact transition approval.
kubectl patch dirsrv example-ds --type merge -p \
'{"spec":{"version":"3.1.1","image":"quay.io/389ds/dirsrv:3.1.1"}}'For example, approve 3.1.0 to 4.0.0, then update the version and image:
kubectl annotate dirsrv example-ds \
'dirsrv.operator.port389.org/approved-transition=3.1.0->4.0.0' --overwrite
kubectl annotate dirsrv example-ds \
'dirsrv.operator.port389.org/backup-confirmed=3.1.0->4.0.0' --overwrite
kubectl patch dirsrv example-ds --type merge -p \
'{"spec":{"version":"4.0.0","image":"quay.io/389ds/dirsrv:4.0.0"}}'The approval must match current->target exactly. Remove it after the
transition if future major changes should remain blocked.
Data-compatible downgrades can be enabled with exact transition approval:
# 4.x -> 3.x
kubectl annotate dirsrv example-ds \
'dirsrv.operator.port389.org/approved-transition=4.0.0->3.1.0' --overwrite
kubectl annotate dirsrv example-ds \
'dirsrv.operator.port389.org/backup-confirmed=4.0.0->3.1.0' --overwrite
# 3.2.x -> 3.1.x
kubectl annotate dirsrv example-ds \
'dirsrv.operator.port389.org/approved-transition=3.2.0->3.1.0' --overwrite
kubectl annotate dirsrv example-ds \
'dirsrv.operator.port389.org/backup-confirmed=3.2.0->3.1.0' --overwritePatch downgrades are allowed without approval. The operator rejects image and version mismatches before changing the StatefulSet. Major changes and minor downgrades also require matching backup confirmation.
The operator updates one StatefulSet pod at a time and waits for the updated replica to become ready. Inspect progress with:
kubectl get dirsrv example-ds -o yaml
kubectl describe dirsrv example-dsStatus includes currentVersion, targetVersion, update counts, conditions,
and recent upgrade history. Upgrade lifecycle events are available with
kubectl get events.
If an upgrade fails, the operator restores the last known good image and keeps PVCs. A manual rollback can target a recorded version:
kubectl annotate dirsrv example-ds \
dirsrv.operator.port389.org/rollback-to=3.1.0 --overwriteAfter automatic rollback, change or remove the rejected target before retrying. Single-replica deployments cannot provide zero-downtime upgrades.
Delete the DirectoryService instances:
kubectl delete directoryservices --allDelete the CRDs from the cluster:
make uninstallUndeploy the operator from the cluster:
make undeploy# Unit tests (with envtest)
make test
# Linting
make lint
# End-to-end tests (requires Kind; use Podman with KIND_EXPERIMENTAL_PROVIDER=podman)
make test-e2e
# With Podman
CONTAINER_TOOL=podman KIND_EXPERIMENTAL_PROVIDER=podman make test-e2e- Build the installer for the image built and published in the registry:
make build-installer IMG=ghcr.io/389ds/ds-operator:v0.0.1This generates dist/install.yaml containing all resources needed to deploy the operator.
- Install using the generated manifest:
kubectl apply -f https://raw.githubusercontent.com/389ds/ds-operator/main/dist/install.yaml- Fork the repository
- Set up pre-commit hooks:
make setup-hooks - Create a feature branch
- Make changes and ensure all checks pass:
make test lint - Submit a pull request
Run make help for all available targets.
More information can be found via the Kubebuilder Documentation.
Copyright 2026 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.