Documentation
Advanced Features
Deep dives on SSH trust modes, kernel tuning, reusable topologies, custom configuration, multi-fork support, the admission webhook, and pod probes.
This page goes beyond the basics — it covers the operator’s more sophisticated features, the reasoning behind them, and operational considerations for production use.
For field references, see Custom Resources Reference. For day-two operational topics, see Operations.
SSH Trust Models
Greenplum-family databases rely heavily on SSH between nodes — for gpinitsystem, gpstart/gpstop, segment recovery, and ad-hoc administration. Traditional deployments use the authorized_keys model: every node has every other node’s public key, distributed manually.
Keldon supports two SSH trust models. You choose per cluster via spec.sshTrust.
Static mode
spec:
sshTrust: static
In static mode, the operator generates one SSH key pair per cluster and distributes the public key to every pod’s authorized_keys. This is the simpler model and works without certificate infrastructure.
How it works:
- A
Secretnamed<cluster>-ssh-keyholds the cluster’s RSA key pair - The operator copies the public key into each pod’s
~/.ssh/authorized_keysat startup - All inter-pod SSH uses this one key pair
Trade-offs:
- Simple to understand and debug
- One key for the entire cluster lifetime — rotation requires re-keying every pod
- Adding a new segment requires updating SSH config on existing pods
Static mode is the right choice for development clusters, CI environments, and any setup where SSH keys aren’t part of your security perimeter. It’s faster to bootstrap and easier to debug.
Certificate mode
spec:
sshTrust: certificate
In certificate mode, the operator runs an SSH certificate authority (CA) and signs short-lived certificates for every pod. Pods are configured to trust the CA’s public key via TrustedUserCAKeys in sshd_config.
How it works:
- The operator generates an SSH CA on first reconcile (stored in
<cluster>-ssh-caSecret) - Each pod gets host and user certificates signed by the CA, valid for a configurable TTL
- Pods trust any certificate signed by the CA — no
authorized_keysdistribution needed - Certificates are rotated before expiry automatically
Trade-offs:
- No
N²key distribution problem — adding a segment is trivial - CA rotation rotates all pod certs in a single operation
- Short-lived certificates limit the blast radius of a key compromise
- Slightly more complex to debug (
ssh -vshows certificate-based auth)
Certificate mode is the right choice for production. The operational savings on rotation alone justify the slight added complexity. If you’re going to grow your cluster fleet over time, start with certificate mode from day one.
Choosing a mode
| Concern | Static | Certificate |
|---|---|---|
| Bootstrap speed | Fastest | ~5s slower for CA setup |
| Adding segments | Requires authorized_keys update |
Trivial — just sign a new cert |
| Key rotation | Manual, re-keys everything | Built-in, annotation-driven |
| Blast radius if compromised | Cluster lifetime | Certificate TTL only |
| Debugging | Easier (familiar SSH model) | Harder (ssh -v for cert validation) |
| Best for | Dev, CI, ephemeral clusters | Production, long-lived clusters |
You can change modes by updating spec.sshTrust and reapplying — but it’s a disruptive operation that re-keys all pods. Don’t switch modes lightly on a running production cluster.
Triggering CA rotation
Rotate the CA at any time (in certificate mode):
kubectl annotate dbc cluster-demo keldon.io/rotate-ca=true
The operator:
- Generates a new CA key pair
- Updates the
<cluster>-ssh-caSecret - Updates
sshd_configon every pod to trust the new CA - Reissues all pod certificates signed by the new CA
- Removes the annotation when complete
Total rotation time is typically 30 – 60 seconds for a small cluster, longer for clusters with many segments.
Rotation does not require pod restarts. SSHd is reloaded on each pod after the new CA is in place. Active SSH sessions are not interrupted; new connections use the new CA.
TTL configuration
Configure certificate TTL via DatabaseClusterClass:
apiVersion: keldon.io/v1alpha1
kind: DatabaseClusterClass
metadata:
name: prod-medium
spec:
sshTrust: certificate
sshTrustConfig:
certTTL: 24h
Valid range: 1h to 720h (30 days). The webhook validates these bounds.
Shorter TTLs increase rotation frequency but reduce blast radius. Longer TTLs reduce churn but extend the period during which a compromised cert could be misused. 24h is the recommended default for production.
Certificates are automatically renewed before expiry (typically at 80% of TTL). If you set
certTTL: 1h, you’ll get rotation churn every 50 minutes. Reasonable production values are24hto168h.
Kernel Tuning
Cloudberry benefits from kernel parameters tuned for high-concurrency MPP workloads. However, Kubernetes pods share the host kernel, so not every Linux sysctl can safely or reliably be applied from inside a pod.
Keldon separates kernel tuning into two categories:
- Pod-level sysctls — namespaced settings that can be applied to the database pods.
- Node-level sysctls — host-wide settings that must be configured directly on every Kubernetes node.
Enabling pod-level defaults
spec:
kernelTuning: true
When enabled, the operator applies a conservative set of pod-compatible sysctls to the cluster pods.
Pod-level sysctls that Keldon can tune:
kernel.shmall— shared memory pageskernel.shmmax— maximum shared memory segment sizekernel.shmmni— maximum number of shared memory segmentskernel.sem— semaphore limitskernel.msgmnb,kernel.msgmax,kernel.msgmni— System V message queue limitsnet.ipv4.ip_local_port_range— local ephemeral port rangenet.ipv4.tcp_syncookies— TCP SYN cookie behavior, when supported by the kernel
The kernel.shm*, kernel.msg*, and kernel.sem families are namespaced but considered unsafe sysctls by Kubernetes. They must be explicitly allowed on the kubelet before pods can use them.
Safe sysctls such as net.ipv4.ip_local_port_range are available by default, but net.* sysctls cannot be used with hostNetwork.
For K3s, allow the unsafe pod-compatible sysctls with:
kubelet-arg:
- "allowed-unsafe-sysctls=kernel.shm*,kernel.msg*,kernel.sem"
Then restart K3s:
sudo systemctl daemon-reload
sudo systemctl restart k3s
sudo systemctl restart k3s-agent
Overriding individual parameters
For fine-grained control, override specific parameters via spec.kernelParameters:
spec:
kernelTuning: true
kernelParameters:
kernel.shmmax: "810810728448"
kernel.shmall: "197951838"
kernel.sem: "250 2048000 200 8192"
Overrides merge with defaults: anything you set replaces the operator’s value; anything you do not set keeps the default.
Node-level sysctls
Some useful Linux tuning parameters are not pod-safe. They are either host-wide, not namespaced, or not reliably writable inside a container runtime.
These should be configured directly on every node:
vm.overcommit_memoryvm.overcommit_ratiovm.swappinessvm.dirty_ratiovm.dirty_background_ratiofs.file-maxnet.core.rmem_maxnet.core.wmem_maxnet.core.netdev_max_backlogkernel.pid_maxkernel.core_patternkernel.core_uses_pid
In particular, net.core.rmem_max and net.core.wmem_max should not be treated as reliable pod-level tuning parameters. They may look like network namespace sysctls, but they are not safe candidates for predictable pod-level tuning and should be configured at the node level instead.
Example node-level configuration:
sudo nano /etc/sysctl.d/keldon-cloudberry.conf
vm.overcommit_memory = 2
vm.overcommit_ratio = 95
vm.swappiness = 10
fs.file-max = 1000000
net.core.rmem_max = 2097152
net.core.wmem_max = 2097152
Apply the configuration:
sudo sysctl --system
Keldon can tune only pod-compatible sysctls from the cluster spec. Host-wide settings must be configured outside the operator, using node provisioning,
/etc/sysctl.d, cloud-init, systemd-sysctl, or a privileged node-level automation mechanism.
Cloud-managed Kubernetes services such as EKS, GKE, and AKS may restrict privileged workloads or unsafe sysctls. If a pod fails during startup, check the kubelet allowlist, Pod Security policy, and container runtime errors.
Reusable Cluster Topologies
DatabaseClusterClass lets you define a cluster shape once and reference it from many clusters. This is the way to enforce consistency across environments — every staging cluster has the same topology as production, just smaller.
When to use a class
Use DatabaseClusterClass when:
- You manage 3+ clusters with similar shapes
- Platform team owns the "shape", app team owns the "instance"
- You want to enforce production HA standards (mirroring + standby)
- Clusters are templated/promoted across environments
Skip and use inline spec when:
- You have one or two unique clusters
- Every cluster has genuinely different requirements
- Quick prototyping
For a full spec reference, see DatabaseClusterClass in the CRD reference.
Override semantics
The merging rule between class and cluster is simple: cluster fields beat class fields, at the leaf level.
Example:
# Class definition
apiVersion: keldon.io/v1alpha1
kind: DatabaseClusterClass
metadata:
name: prod-medium
spec:
topology:
segments:
count: 4
mirroring: true
resources:
segments:
requests:
cpu: "4"
memory: 16Gi
---
# Cluster referencing the class
apiVersion: keldon.io/v1alpha1
kind: DatabaseCluster
metadata:
name: cluster-demo
spec:
databaseImage: cloudberry-2.1.0
databaseClusterClass: prod-medium
segments:
count: 8 # overrides class count
storage: 100Gi
storageClassName: gp3 # cluster-only setting
# resources not specified → inherited from class
The resulting cluster has:
- 8 segments (cluster override)
- Mirroring enabled (inherited from class)
- 100Gi storage on
gp3(cluster-only) - CPU/memory requests inherited from class
Override is at the leaf field level, not whole-object replacement. Setting
spec.segments.countdoesn’t blank out the rest of thesegmentsobject from the class —storage,resources, etc. are still inherited.
Updating a
DatabaseClusterClassdoes not automatically reconcile existing clusters that reference it. This is intentional — silent behavior changes from a class update would be dangerous in production.
Custom Database Configuration
DatabaseConfig provides declarative management of postgresql.conf and pg_hba.conf. The operator applies the config to the cluster pods and reloads PostgreSQL.
For a spec reference, see DatabaseConfig in the CRD reference.
Authentication rules (pg_hba)
pg_hba.conf controls who can connect from where and how they authenticate. The operator-managed rules cover internal traffic (coordinator ↔ segments, standby replication, mirror replication). Your DatabaseConfig.spec.pgHba rules are appended to those.
Common patterns:
Allow internal cluster network only:
spec:
pgHba:
- type: host
database: all
user: all
address: "10.0.0.0/8"
method: scram-sha-256
Mixed internal trust + external auth:
spec:
pgHba:
- type: host
database: all
user: all
address: "10.0.0.0/16" # internal pod network
method: trust
- type: host
database: all
user: all
address: "0.0.0.0/0" # external clients
method: scram-sha-256
Per-database access:
spec:
pgHba:
- type: host
database: analytics
user: app_user
address: "10.0.0.0/8"
method: scram-sha-256
- type: host
database: reporting
user: reader
address: "10.0.0.0/8"
method: scram-sha-256
Validation
The admission webhook validates DatabaseConfig for:
- Unknown
postgresql.confparameters (typos) - Invalid values for numeric or unit-bearing parameters
pg_hbaentries that would break operator-managed connectivity- Syntactic errors in pg_hba
addressfields
Invalid configurations are rejected at admission time with a clear error message, preventing broken state from reaching the cluster.
Admission Webhook Validation
Keldon’s admission webhook validates every CRD operation before it reaches the cluster’s etcd. Webhook validation catches errors at the API boundary, where they’re easy to fix, instead of producing failed pods deep in a reconcile loop.
What the webhook checks
| Check | Resource | What it enforces |
|---|---|---|
| Cluster name length | DatabaseCluster |
Name ≤ 18 characters (Cloudberry MAXHOSTNAMELEN constraint) |
| Required fields | All CRDs | Mandatory fields like databaseImage, coordinator.storage |
| CRD references | DatabaseCluster |
Referenced DatabaseImage, DatabaseClusterClass, DatabaseConfig exist and are valid |
| CertTTL bounds | DatabaseClusterClass |
sshTrustConfig.certTTL between 1h and 720h |
| HA node availability | DatabaseCluster |
If standby.enabled or mirroring, at least 2 schedulable nodes |
| Standby storage default | DatabaseCluster |
Auto-fills standby.storage to coordinator’s storage if unset |
| Segment count delta | DatabaseCluster |
Reject scale-down (decrementing spec.segments.count) |
Required field validation
Beyond simple presence checks, the webhook validates relationships:
- A
DatabaseClusterwithmirroring: trueandspec.segments.count = 1is rejected — mirroring requires at least 2 segments - A
DatabaseRestorereferencing a non-existentDatabaseBackupis rejected - A
DatabaseClusterreferencing a non-existentDatabaseImageis rejected (CRD-level reference validation)
HA node availability check
If you enable HA (standby.enabled or mirroring) on a single-node cluster, the webhook rejects the spec — there’s no value in HA if both replicas land on the same node.
The check counts schedulable nodes at admission time. If you have 2+ nodes but anti-affinity rules prevent scheduling, that failure happens at pod scheduling time, not admission time.
Default value mutators
Some webhook handlers run in “mutating” mode — they fill in missing fields rather than rejecting them. Current mutators:
standby.storagedefaults tocoordinator.storageif unsetrecovery.rebalanceDelaySecondsdefaults to30ifautoRebalance: truebut the delay is unsetsshTrustdefaults tostaticif unset
That covers the advanced features. Next: Reference for the spec-level details — Helm values, all annotations, phase transitions, validation rules, and known limitations.