Single Node
replicas == 1 → Community, always allowed. No license required.
Running a database on Kubernetes means managing StatefulSets, PDBs, ConfigMaps, and scaling policies. ZeptoDB’s operator translates a single ZeptoDBCluster custom resource into a fully configured Helm release — with license gating that prevents unlicensed multi-node deployments at the orchestration layer.
apiVersion: zeptodb.com/v1alpha1kind: ZeptoDBClustermetadata: name: trading-prodspec: replicas: 3 version: "0.9.0" storage: size: 100Gi storageClass: gp3 license: secretName: zeptodb-licensestatus: phase: Running readyReplicas: 3 edition: Enterprise message: "All nodes healthy"kubectl get zdb shows printer columns: name, phase, ready replicas, edition, and age.
Single Node
replicas == 1 → Community, always allowed. No license required.
Multi-Node
replicas > 1 → requires spec.license.secretName pointing to a K8s Secret.
Missing License
CR status set to Failed: “Multi-node requires Enterprise license”.
Separation of Concerns
Operator checks secret existence. Binary does RS256 JWT validation at startup.
The operator is a ~120-line bash script polling every 10 seconds:
┌─────────────────────────────────────────────────┐│ Reconciler Loop ││ ││ 1. kubectl get zeptodb --all-namespaces ││ 2. For each CR: ││ ├── Read spec (replicas, version, storage) ││ ├── License gate check (replicas > 1?) ││ │ ├── No secret → status=Failed, skip ││ │ └── Secret exists → continue ││ ├── helm upgrade --install with --set flags ││ └── Update CR status ││ 3. Detect deletions → helm uninstall ││ 4. Sleep 10s │└─────────────────────────────────────────────────┘| CR Field | Helm Flag |
|---|---|
spec.replicas | --set replicaCount=N |
spec.version | --set image.tag=VERSION |
spec.storage.size | --set persistence.size=SIZE |
spec.storage.storageClass | --set persistence.storageClass=CLASS |
spec.license.secretName | --set extraEnv[0].name=ZEPTODB_LICENSE_KEY,... |
The license secret is mounted as ZEPTODB_LICENSE_KEY env var via Helm’s extraEnv.
| Factor | Bash Operator | Go Operator |
|---|---|---|
| Build dependency | None (kubectl + helm) | Go toolchain + operator-sdk |
| Modifiability | Any engineer can edit | Requires Go + controller-runtime knowledge |
| Reconnection | Simple poll loop, no edge cases | Watch stream needs reconnection handling |
| Backend | Helm chart reuse (StatefulSet, PDB, HPA) | Must reimplement or embed Helm |
Minimal permissions — the operator only reads secrets (for license checks) and manages workloads:
rules: - apiGroups: ["zeptodb.com"] resources: ["zeptodbclusters", "zeptodbclusters/status"] verbs: ["get", "list", "watch", "patch"] - apiGroups: ["apps"] resources: ["statefulsets", "deployments"] verbs: ["get", "list", "create", "update", "delete"] - apiGroups: [""] resources: ["secrets"] verbs: ["get"]Enterprise (3-node): replicas: 3, license: { secretName: zeptodb-license }, storage: { size: 100Gi, storageClass: gp3 }
Community (single-node): replicas: 1, storage: { size: 10Gi } — no license field needed.
| Decision | Rationale |
|---|---|
| No JWT validation in operator | Separation of concerns — operator handles K8s, binary handles crypto |
| Poll interval: 10s | Fast enough for config changes, low API server load |
| Helm as backend | Avoids reimplementing StatefulSet/PDB/HPA logic |
| Single-replica operator | Sufficient for CRD→Helm translation; no leader election needed |