Documentation
Operations
Day-two operational topics: cluster lifecycle, scaling, high availability, failover, backups, and upgrades.
This page covers what you do once a cluster is running. It’s organized around the operational lifecycle: understanding how the operator drives a cluster, scaling it, keeping it highly available, recovering from failures, taking backups, and upgrading the operator and database engine.
For initial install and first-cluster setup, see Getting Started. For full spec references of the resources mentioned here, see Custom Resources Reference.
Cluster Lifecycle
The operator runs a continuous reconciliation loop. Every time the cluster’s spec or state changes — a YAML applied, a pod restarted, a node disappeared — the operator wakes up, compares declared spec to actual state, and takes whatever action is needed to converge them.
Bootstrap phases
When you first apply a DatabaseCluster, the operator drives it through a sequence of phases until it reaches Running. Each phase corresponds to a discrete step in the bootstrap process.
| Phase | What happens | Typical duration |
|---|---|---|
PodsStarting |
StatefulSets created. Pods being scheduled, images pulled, containers started. | 30s – 3 min |
SSHReady |
All pods are responsive over SSH. Trust established between them. | 10 – 30s |
Initializing |
gpinitsystem runs on the coordinator. The catalog is created, segments registered. |
60 – 120s |
Running |
Cluster fully operational. Accepting connections. | — |
Watch progression with:
kubectl get dbc -w
The phase reflects the current activity. If you see a phase persisting for much longer than the typical duration, the operator is likely retrying due to an external problem (image pull failure, storage provisioning issue, etc.) — see Troubleshooting.
Pod restart behavior
When a pod restarts (image upgrade, node drain, OOM kill), the operator’s behavior depends on the pod’s role:
- Coordinator restart with active standby — The operator promotes the standby and updates the Service selector. The original coordinator returns as a new standby once it’s healthy.
- Coordinator restart with no standby — The cluster becomes unavailable until the coordinator pod returns. Brief downtime, typically 15 – 60 seconds.
- Standby restart — No client impact. Standby re-syncs from the primary via WAL streaming.
- Segment restart with mirror present — The mirror is promoted to active. The original primary becomes the new mirror after recovery.
- Segment restart with no mirror — Queries that need that segment fail until the pod returns. Then queries resume.
- Mirror restart — No client impact. Mirror catches up via WAL replication.
The operator handles all of this automatically when spec.recovery.autoRecover: true.
Scaling
Keldon supports horizontal scaling of segments without downtime. Add segments, the operator provisions them and redistributes data automatically.
Scaling up
Increase spec.segments.count:
kubectl patch dbc cluster-demo --type merge \
-p '{"spec":{"segments":{"count":4}}}'
Or edit the resource directly:
kubectl edit dbc cluster-demo
The operator transitions the cluster through:
Running → Scaling → Running
↑
New segment pods provisioned
New segments added to gp_segment_configuration
Data redistributed across the expanded cluster
A scaling operation on a 100GB cluster going from 2 to 4 segments typically takes 5 – 20 minutes depending on storage performance.
Watch progress with
kubectl get dbc -wandkubectl get pods -l keldon.io/cluster=<name>. New segment pods appear with incrementing ordinals:cluster-demo-segment-2,cluster-demo-segment-3.
When scaling up with mirroring enabled, new mirror pods are also provisioned, paired with the new primaries.
Scaling down is not supported
Decreasing
spec.segments.countis not supported in the current version and it’s rejected by the admission webhook. keldon will support scaling down in futur version, for now to shrink a cluster, the correct path is to take a backup, create a new smaller cluster, and restore into it.
The migration path:
# 1. Back up the existing cluster
kubectl apply -f backup-before-shrink.yaml
# 2. Wait for backup completion
kubectl wait --for=condition=Complete --timeout=1h dbb/backup-before-shrink
# 3. Create the smaller target cluster (different name)
kubectl apply -f cluster-shrunken.yaml
# 4. Restore into the new cluster
kubectl apply -f restore-into-shrunken.yaml
# 5. Update applications to point at the new cluster
# 6. Delete the original cluster
High Availability
Keldon provides high availability at two layers: coordinator (via standby) and segments (via mirrors). These are independent — you can enable either or both.
Standby coordinator
The standby coordinator is a hot replica of the primary, kept in sync via PostgreSQL WAL streaming. It runs in its own pod, on a separate StatefulSet, with its own PVC.
spec:
standby:
enabled: true
storage: 10Gi
storageClassName: local-path
Once configured, the standby:
- Streams WAL from the primary coordinator continuously
- Maintains an identical catalog (databases, users, schema definitions)
- Is not writable while in standby mode
- Can be promoted to active coordinator in case of primary failure
Verify the standby is in sync:
kubectl describe dbc <cluster-name>
You should see entries for the primary coordinator (role='p') and standby (role='m'), both with status='u'.
Mirror segments
Mirror segments are hot replicas of primary segments. Each primary segment has exactly one mirror, on a different pod and ideally a different node.
spec:
mirroring: true
When enabled, the operator:
- Provisions one mirror pod per primary segment
- Sets up WAL streaming from primary to mirror
- Updates
gp_segment_configurationto track primary/mirror relationships - Configures Cloudberry’s
gp_fts_probe_intervalfor failure detection
Check segment health:
kubectl describe dbc <cluster-name>
You should see, for each content (segment number), two rows: one with role='p' (primary) and one with role='m' (mirror), both with status='u' (up).
Replicas are NOT backups. A
DROP TABLEreplicates to mirrors and standby instantly. If you need to recover deleted data, you need an out-of-band backup.
Failover and Recovery
Coordinator failover
When the active coordinator pod becomes unavailable, the operator:
- Detects the failure (pod NotReady for > 30 seconds, or unresponsive to health probes)
- Verifies the standby is healthy and caught up
- Runs
gpactivatestandbyon the standby to promote it - Updates the Service selector to point at the promoted pod
- Sets
status.phase = CoordinatorFailover, thenRunningonce promotion completes
Total client-visible downtime is typically 30 – 90 seconds. Existing connections are dropped; clients must reconnect.
Before failover:
cluster-demo-coordinator-0 Active (primary)
cluster-demo-standby-0 Ready (standby)
After failover:
cluster-demo-coordinator-0 Failed (will not auto-recover)
cluster-demo-standby-0 Active (now serving as primary)
Failover is one-way. The operator does not automatically fail back to the original primary even after it returns to health. Auto-failback is a planned feature but currently requires manual intervention. To re-establish HA, the original coordinator becomes the new standby on next reconciliation, after which you can manually swap roles via a planned downtime window.
Automatic segment recovery
When spec.recovery.autoRecover: true, the operator detects failed segments and bring them back online as mirrors:
spec:
recovery:
autoRecover: true
Automatic rebalancing
When spec.recovery.autoRebalance: true, the operator does rebalance the segment data automatically after a configurable grace period:
spec:
recovery:
autoRecover: true
autoRebalance: true
rebalanceDelaySeconds: 30
rebalanceDelaySeconds lets you batch recovery and rebalancing if multiple segments fail in quick succession.
Set
rebalanceDelaySecondsto a few minutes (e.g. 300) on busy production clusters. Rebalance moves data and creates load — better to do it once after the cluster stabilizes than after every individual recovery.
Upgrading the Operator
Operator upgrades use the standard Helm upgrade flow.
Helm upgrade workflow
helm repo update
helm upgrade keldon-operator keldon/keldon-operator -n keldon-system
The upgrade:
- Pulls the new chart from
charts.keldon.io - Updates the operator Deployment to the new image
- Applies any CRD updates included in the new chart
- Rolls the operator pod (brief downtime of the controller, not the database)
During an operator upgrade, running database clusters are unaffected. The operator going offline briefly means no reconciliation happens — but pods stay running, queries continue. The operator catches up on missed work when it comes back.
Rolling back
If an upgrade goes wrong, Helm makes rollback easy:
# See revision history
helm history keldon-operator -n keldon-system
# Roll back to the previous revision
helm rollback keldon-operator -n keldon-system
Helm rollback restores the operator binary and chart configuration, but does not automatically roll back CRD schema changes. If the new version added or modified CRDs, rolling back the chart may leave incompatible CRDs in place. Read the upgrade notes carefully — they’ll tell you whether rollback is safe.
CRD migration between versions
CRDs evolve with the operator. Backward-incompatible changes are flagged in release notes. The chart’s pre-upgrade hook applies any necessary CRD updates before the new operator starts.
To inspect what changed:
# View the current CRD spec
kubectl get crd databaseclusters.keldon.io -o yaml > current-crd.yaml
# After upgrade, compare
kubectl get crd databaseclusters.keldon.io -o yaml > new-crd.yaml
diff current-crd.yaml new-crd.yaml
Upgrading a Database
Upgrading the Cloudberry version inside a running cluster is a separate operation from upgrading the operator.
Creating a new DatabaseImage version
First, register the new image:
apiVersion: keldon.io/v1alpha1
kind: DatabaseImage
metadata:
name: cloudberry-2.2.0
spec:
version: "2.2.0"
image: ghcr.io/keldonio/cloudberry:2.2.0
Apply it. This doesn’t change anything yet — it just registers the new image.
Pointing the cluster at the new image
Update the DatabaseCluster to reference the new image:
kubectl patch dbc cluster-demo --type merge \
-p '{"spec":{"databaseImage":"cloudberry-2.2.0"}}'
The operator detects the image change and starts a rolling update of cluster pods. Pods restart one role at a time:
1. Mirror segments restarted (no client impact, mirrors only)
2. Primary segments restarted (brief query disruption per segment)
3. Standby restarted (no client impact)
4. Coordinator restarted (brief connection disruption)
Total upgrade time depends on data volume and the size of WAL backlogs to replay.
That covers day-to-day cluster operations. Next: Advanced Features for SSH trust modes, multi-fork support, kernel tuning, and webhook configuration.