Percona Operator for MongoDB in practice
Deploy replica sets and sharded clusters with the PerconaServerMongoDB custom resource, wire in PBM backups, and run day-2 operations safely.
- Read and write a PerconaServerMongoDB custom resource
- Configure storage, anti-affinity and disruption budgets
- Schedule backups and restore with PITR on Kubernetes
- Scale, upgrade and troubleshoot
conceptThe concept
Open the interactive lesson to use this diagram.
The operator watches PerconaServerMongoDB resources (kubectl get psmdb) and reconciles StatefulSets for each replica set, the config server replica set and mongos, plus Services, Secrets (system users, TLS) and PBM agent sidecars. Backups and restores are themselves resources: PerconaServerMongoDBBackup and PerconaServerMongoDBRestore.
Key spec areas: replsets (size, volumeSpec, resources, affinity), sharding (configsvrReplSet, mongos), backup (storages, tasks, pitr), users and secrets, and updateStrategy: SmartUpdate, which restarts secondaries first and steps down the primary last.
whyWhy it matters
Running MongoDB on Kubernetes without an operator means scripting membership changes, rolling restarts and backups by hand. The operator encodes those runbooks; understanding its resources is now a common requirement for DBRE roles on self-hosted platforms.
Use caseReal-world scenario
A platform team offers MongoDB as a service on Kubernetes: each product team gets a PerconaServerMongoDB resource from a template with three members across zones, 200 Gi of SSD storage, nightly physical backups to S3 with PITR, and PMM monitoring. Upgrades are done by changing the image tag; SmartUpdate rolls members one at a time.
Try itExamples
Run and edit these in the interactive lesson.
apiVersion: psmdb.percona.com/v1
kind: PerconaServerMongoDB
metadata:
name: orders-db
spec:
crVersion: "1.21.0"
image: percona/percona-server-mongodb:8.0
updateStrategy: SmartUpdate
secrets:
users: orders-db-users
replsets:
- name: rs0
size: 3
affinity:
antiAffinityTopologyKey: topology.kubernetes.io/zone
podDisruptionBudget:
maxUnavailable: 1
resources:
limits: { cpu: "4", memory: 16Gi }
volumeSpec:
persistentVolumeClaim:
storageClassName: fast-ssd
resources:
requests: { storage: 200Gi }
backup:
enabled: true
image: percona/percona-backup-mongodb:2
storages:
s3-main:
type: s3
s3: { bucket: acme-mongo-backups, region: ap-south-1, credentialsSecret: s3-creds, prefix: orders-db }
pitr:
enabled: true
tasks:
- name: nightly-physical
enabled: true
schedule: "0 23 * * *"
type: physical
storageName: s3-main
keep: 7kubectl get psmdb # status: ready / initializing / error
kubectl get pods -l app.kubernetes.io/instance=orders-db
kubectl patch psmdb orders-db --type=merge -p '{"spec":{"replsets":[{"name":"rs0","size":5}]}}' # scale (merge replaces the list: include all fields in real use)
kubectl get psmdb-backup # backup objects and their state
kubectl logs orders-db-rs0-0 -c backup-agent --tail=50
kubectl logs deploy/percona-server-mongodb-operator --tail=100 # reconcile errorsapiVersion: psmdb.percona.com/v1
kind: PerconaServerMongoDBRestore
metadata:
name: restore-before-incident
spec:
clusterName: orders-db
backupName: cron-orders-db-20261007230000
pitr:
type: date
date: "2026-10-08 14:04:59"rs.conf().members.map(m => ({ host: m.host, priority: m.priority, votes: m.votes, hidden: m.hidden }))internalsUnder the hood
Pods get stable names (cluster-rs0-0) and PersistentVolumeClaims, so a rescheduled pod reattaches its data and rejoins the replica set. podDisruptionBudget.maxUnavailable: 1 keeps node drains from taking down a majority. Set container memory limits and let the operator size the WiredTiger cache from them, or configure it explicitly; never rely on host RAM detection in containers. Exposing members outside the cluster needs expose settings and split-horizon DNS so drivers see reachable hostnames.
Avoid / preferDo this, not that
kubectl patch psmdb my-cluster-name --type=merge \
-p '{"spec":{"replsets":[{"name":"rs0","size":5}]}}'kubectl patch psmdb my-cluster-name --type=json \
-p '[{"op":"replace","path":"/spec/replsets/0/size","value":5}]'A merge patch replaces the whole replsets array, dropping every other setting on rs0 (resources, storage, affinity). A JSON patch changes only the size.
kubectl delete pod my-cluster-name-rs0-0 # to "apply" a config change
# edit the PerconaServerMongoDB resource and let the operator roll it kubectl apply -f cr.yaml kubectl get psmdb my-cluster-name -w
The operator owns the pods. Deleting pods by hand races the reconcile loop and can restart the primary first; the operator's SmartUpdate restarts secondaries first and steps the primary down last.
PitfallsCommon mistakes
- Editing the StatefulSet directly; the operator will revert it on the next reconcile.
- No anti-affinity, so all three members land on one node or zone.
- Running without a PodDisruptionBudget, letting a node drain take two members at once.
- Restoring into the live cluster without reading the restore resource semantics (it replaces data).
LimitsWhen not to use it
The operator automates what it models: replica sets, sharding, backups through PBM, upgrades. Hand-edits to generated StatefulSets or Secrets are overwritten. If you need a topology it does not support, run that piece outside the operator rather than fighting the reconcile loop.
SecuritySecurity notes
System users and their passwords live in the cluster's users Secret; rotate them through that Secret so the operator applies the change, and restrict access to it with RBAC. Pin operator and image versions, review release notes before upgrades, and enable TLS with cert-manager issued certificates.
ChallengeTry it yourself
Write the commands to scale replica set rs0 of my-cluster-name from 3 to 5 members without touching any other setting, then watch the rollout.
Show one solution
kubectl patch psmdb my-cluster-name --type=json \
-p '[{"op":"replace","path":"/spec/replsets/0/size","value":5}]'
kubectl get psmdb my-cluster-name -w
kubectl get pods -l app.kubernetes.io/instance=my-cluster-nameInterviewQuestions you should be able to answer
How do you upgrade MongoDB managed by the Percona Operator?
Check the operator and MongoDB version compatibility, take and verify a backup, then change the image (and later the featureCompatibilityVersion setting) in the custom resource. With updateStrategy: SmartUpdate the operator restarts secondaries one at a time, waits for them to be healthy, then steps down the primary and restarts it last. Watch the operator logs and replication lag throughout, and set FCV only after the soak period.
QuizCheck your understanding
4 questions with instant feedback in the interactive lesson; a wrong answer goes into your review deck.