MongoDB Replication
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.
| Benefit | How replication provides it |
|---|---|
| High availability | Automatic failover to a secondary |
| Durability | Writes can be confirmed only once several servers have them |
| Disaster recovery | Members can live in different data centres or regions |
| Maintenance without downtime | Upgrade or restart members one at a time |
| Read scaling and analytics | Some 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.
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 members | Majority needed | Failures tolerated |
|---|---|---|
| 1 | 1 | 0 |
| 3 | 2 | 1 |
| 5 | 3 | 2 |
| 7 (maximum) | 4 | 3 |
A replica set can have up to 50 members, but at most 7 of them can vote.
4. Member Types
| Member | Configuration | Purpose |
|---|---|---|
| Regular secondary | default | Full copy, can become primary |
| Priority 0 | priority: 0 | Holds data and votes, but never becomes primary (for example in a distant data centre) |
| Hidden | priority: 0, hidden: true | Invisible to applications; used for backups or reporting |
| Delayed | priority: 0, hidden: true, secondaryDelaySecs: 3600 | Stays one hour behind, a safety net against accidental deletes |
| Arbiter | arbiterOnly: true | Votes 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.
db.payments.insertOne({ orderNo: 5001, amount: 919 }, { writeConcern: { w: "majority", wtimeout: 5000 } })
6. Read Preference: Where Do Reads Go?
| Mode | Reads from |
|---|---|
primary (default) | Only the primary: always the latest data |
primaryPreferred | The primary, or a secondary if no primary is available |
secondary | Only secondaries |
secondaryPreferred | Secondaries, or the primary if none is available |
nearest | The 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
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:
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
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
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
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
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
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
| Replication | Sharding | |
|---|---|---|
| Each server holds | A full copy of the data | Part of the data |
| Main goal | Availability and durability | Scaling data size and write throughput |
| Used together? | Yes: in a sharded cluster, every shard is itself a replica set | |
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.