00:00

MongoDB Replication

Last updated: 7 min read

A single database server is a single point of failure: if it crashes, your application stops. MongoDB solves this with replication, keeping identical copies of your data on several servers that work together as a replica set. If one server fails, another takes over automatically, usually within seconds.

This tutorial explains how replica sets work, from the oplog to elections, how write concern and read preference control durability and reads, which member types exist, and how to build and test a three-member replica set yourself.

1. What Is a Replica Set?

A replica set is a group of mongod processes that hold the same data:

  • One primary receives all writes.
  • One or more secondaries copy the primary's changes and apply them to their own data.
  • If the primary becomes unavailable, the members elect a new primary from the secondaries.
BenefitHow replication provides it
High availabilityAutomatic failover to a secondary
DurabilityWrites can be confirmed only once several servers have them
Disaster recoveryMembers can live in different data centres or regions
Maintenance without downtimeUpgrade or restart members one at a time
Read scaling and analyticsSome reads can go to secondaries

MongoDB Atlas clusters are always replica sets (or sharded clusters made of replica sets), and production self-managed deployments should be too. A standalone server is fine only for development.

2. The Oplog

The primary records every change in the oplog (operations log), a special capped collection called local.oplog.rs. Secondaries continuously read the primary's oplog and apply the same operations, in the same order, to their own copy of the data.

mongosh
use local
db.oplog.rs.find().sort({ $natural: -1 }).limit(1)   // the most recent operation
rs.printReplicationInfo()                            // oplog size and the time window it covers

Oplog entries are idempotent: an $inc is stored as the resulting $set, so applying an entry twice gives the same result. The oplog must be large enough to cover maintenance windows; if a secondary falls further behind than the oplog reaches, it needs a full resync.

3. Heartbeats, Elections and Failover

  • Members send heartbeats to each other every 2 seconds.
  • If the primary does not respond for the election timeout (10 seconds by default), an eligible secondary calls an election.
  • A candidate needs votes from a majority of the voting members. Among eligible members, the one with the most recent data and the highest priority is preferred.
  • Drivers detect the new primary and retry eligible reads and writes automatically (retryable reads and writes are on by default).

The majority rule is why replica sets should have an odd number of voting members, typically three or five. With three members, the set survives one failure; with five, two.

Voting membersMajority neededFailures tolerated
110
321
532
7 (maximum)43

A replica set can have up to 50 members, but at most 7 of them can vote.

4. Member Types

MemberConfigurationPurpose
Regular secondarydefaultFull copy, can become primary
Priority 0priority: 0Holds data and votes, but never becomes primary (for example in a distant data centre)
Hiddenpriority: 0, hidden: trueInvisible to applications; used for backups or reporting
Delayedpriority: 0, hidden: true, secondaryDelaySecs: 3600Stays one hour behind, a safety net against accidental deletes
ArbiterarbiterOnly: trueVotes but holds no data; avoid when possible because it weakens durability

5. Write Concern: How Safe Is a Write?

Write concern decides how many members must confirm a write before the driver reports success:

  • w: 1: the primary has it. Fast, but the write can be rolled back if the primary fails before replicating it.
  • w: "majority": a majority of voting members have it. Such writes survive any failover. This is the default since MongoDB 5.0.
  • j: true: the write is also in the on-disk journal of the confirming members.
mongosh
db.payments.insertOne({ orderNo: 5001, amount: 919 }, { writeConcern: { w: "majority", wtimeout: 5000 } })

6. Read Preference: Where Do Reads Go?

ModeReads from
primary (default)Only the primary: always the latest data
primaryPreferredThe primary, or a secondary if no primary is available
secondaryOnly secondaries
secondaryPreferredSecondaries, or the primary if none is available
nearestThe member with the lowest network latency

Secondaries apply changes slightly later than the primary (replication lag), so reads from them can be a little out of date. Use secondary reads for reporting, analytics or geographically close reads that tolerate this, not for "read your own write" flows.

Read concern complements this by choosing which data a read may see: local (default), majority (only data that cannot be rolled back), linearizable or snapshot (used in transactions).

7. Hands-On: A Three-Member Replica Set

For learning, you can run three members on one machine on different ports. In production, each member must run on its own server, ideally in different availability zones.

Step 1: start three mongod processes

Terminal
mkdir -p ~/rs0/db1 ~/rs0/db2 ~/rs0/db3

mongod --replSet rs0 --port 27017 --dbpath ~/rs0/db1 --bind_ip localhost --fork --logpath ~/rs0/db1.log
mongod --replSet rs0 --port 27018 --dbpath ~/rs0/db2 --bind_ip localhost --fork --logpath ~/rs0/db2.log
mongod --replSet rs0 --port 27019 --dbpath ~/rs0/db3 --bind_ip localhost --fork --logpath ~/rs0/db3.log

The same settings in a configuration file, which is how you would normally run mongod:

/etc/mongod.conf (one member)
storage:
  dbPath: /var/lib/mongodb
net:
  port: 27017
  bindIp: 0.0.0.0          # restrict with firewalls; enable authentication and TLS
replication:
  replSetName: rs0
  oplogSizeMB: 4096

Step 2: initiate the replica set

mongosh --port 27017
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017", priority: 2 },   // preferred primary
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
})
// { ok: 1 }   after a few seconds the prompt shows rs0 [direct: primary]

Step 3: check the status

mongosh
rs.status().members.map(m => ({ name: m.name, state: m.stateStr, health: m.health }))
// [ { name: 'localhost:27017', state: 'PRIMARY', health: 1 },
//   { name: 'localhost:27018', state: 'SECONDARY', health: 1 },
//   { name: 'localhost:27019', state: 'SECONDARY', health: 1 } ]

rs.conf()                              // current configuration
db.hello()                             // which member is primary, as the driver sees it
rs.printSecondaryReplicationInfo()     // how far behind each secondary is

Step 4: connect your application to the whole set

Connection string
mongodb://localhost:27017,localhost:27018,localhost:27019/bookstore?replicaSet=rs0

With the replicaSet option and several hosts, the driver discovers all members, always sends writes to the current primary and follows failovers on its own.

Step 5: test a failover

mongosh (on the primary)
rs.stepDown(60)        // primary steps down and will not stand for election for 60 seconds
// reconnect and run rs.status(): another member is now PRIMARY

You can also stop the primary's process completely. Writes pause for a few seconds during the election and then continue on the new primary.

8. Changing the Configuration

mongosh (on the primary)
rs.add({ host: "db4.example.com:27017", priority: 0, hidden: true })   // add a hidden member
rs.remove("db4.example.com:27017")                                     // remove it again

// Change a member's priority
const cfg = rs.conf()
cfg.members[2].priority = 0.5
rs.reconfig(cfg)

9. Replication vs Sharding

ReplicationSharding
Each server holdsA full copy of the dataPart of the data
Main goalAvailability and durabilityScaling data size and write throughput
Used together?Yes: in a sharded cluster, every shard is itself a replica set
Replication is not a backup. A mistaken deleteMany() is copied to every member within milliseconds. Keep separate backups as described in MongoDB Backup and Restore.

10. Best Practices

  • Use at least three data-bearing members, each on separate hardware or availability zones.
  • Keep an odd number of voting members, and avoid arbiters in production.
  • Use write concern majority (the default) for data you cannot lose.
  • Size the oplog to cover at least a day of changes, more for long maintenance windows.
  • Monitor replication lag and member health, and alert on them.
  • Enable authentication between members with keyfiles or x.509 certificates, and encrypt traffic with TLS.

11. Summary

  • A replica set has one primary that takes writes and secondaries that copy them through the oplog.
  • Heartbeats and majority elections provide automatic failover within seconds.
  • Write concern controls durability, and read preference and read concern control where reads go and what they see.
  • Priority 0, hidden and delayed members serve special purposes; arbiters should be avoided.
  • Replication provides availability, not backups; sharding is for scale and is built from replica sets.