MongoDB Mastery
MongoDB Mastery lessons / Module 34: MongoDB on Kubernetes

Percona Operator for MongoDB in practice

Level 5: Database Architect25 min

Deploy replica sets and sharded clusters with the PerconaServerMongoDB custom resource, wire in PBM backups, and run day-2 operations safely.

This lesson is interactive. Run every example against an in-browser MongoDB, see explain() plans drawn as trees, and take the quiz.
Open the interactive lesson
You will learn to
  • 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.

A three-member replica set with backups
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: 7
Check crVersion and image tags against the operator release you install.
Day-2 commands
kubectl 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 errors
Point-in-time restore resource
apiVersion: 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"
What the operator configures inside MongoDB
rs.conf().members.map(m => ({ host: m.host, priority: m.priority, votes: m.votes, hidden: m.hidden }))
In a real operator-managed cluster the hosts are the stable pod DNS names, for example orders-db-rs0-0.orders-db-rs0.ns.svc.cluster.local.

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

Avoid
kubectl patch psmdb my-cluster-name --type=merge \
  -p '{"spec":{"replsets":[{"name":"rs0","size":5}]}}'
Prefer
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.

Avoid
kubectl delete pod my-cluster-name-rs0-0   # to "apply" a config change
Prefer
# 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
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-name
Run against a cluster with the Percona Operator for MongoDB installed.

InterviewQuestions 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.