Replica sets & high availability

This section documents the current MongoDB 8.x server line as published at the MongoDB Server Manual, which is the reference these pages are written and verified against. No specific patch version is pinned. Some capabilities (Atlas Search, Atlas Vector Search, and parts of encryption and backup) are Atlas-only — they are linked, not documented in depth.

This content was generated with the assistance of AI and should be verified against the official manual before being relied on in production, since MongoDB iterates quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

A replica set is a group of mongod processes that hold the same data. One member is the primary and takes every write; the others are secondaries that copy the primary’s changes and can step up if it fails. This page covers how replication works, how a new primary is chosen, and the client-side knobs — read preference, write concern, read concern — that decide which trade-off between latency, freshness, and durability a given operation makes.

The replica set and the oplog

Every write the primary applies is also recorded, in an idempotent form, in the oplog (oplog.rs in the local database) — a capped collection with a fixed size ceiling that discards its oldest entries once full. Each secondary opens a tailable cursor on the primary’s oplog, copies new entries as they appear, and replays them against its own copy of the data. Because oplog entries are idempotent, replaying one twice produces the same result, which is what makes retryable writes and crash recovery safe.

One primary replicating its oplog to two secondaries, and the same set after the primary fails and a secondary is elected

A brand-new member with no data first performs an initial sync: it copies every database from a source member, records where in the oplog that copy started, then applies every oplog entry produced since to catch up. From then on it just tails the oplog like any other secondary.

sequenceDiagram participant N as New secondary participant P as Sync source (primary) N->>P: begin initial sync, note oplog position T0 P-->>N: clone all databases and collections P-->>N: build indexes N->>P: stream oplog entries since T0 P-->>N: entries T0..T1 (catch-up) Note over N: now caught up loop steady state N->>P: tail oplog with awaitData cursor P-->>N: new entries as they are written N->>N: apply entries in batches end

See Replication for the overview and Replica Set Oplog for oplog sizing and the replSetResizeOplog command.

// https://www.mongodb.com/docs/manual/reference/method/rs.printReplicationInfo/
rs.printReplicationInfo()    // oplog size and the time span it currently covers
rs.printSecondaryReplicationInfo()  // how far behind each secondary is

Elections and heartbeats

Every member sends a heartbeat to every other member every two seconds. When enough heartbeats to the primary go unanswered (default 10 seconds), the secondaries call an election. A candidate becomes primary only if it receives votes from a majority of the voting members of the configured set — so a three-member set tolerates one member down, and a five-member set tolerates two. Without a majority reachable, the set has no primary and rejects writes.

Members carry per-member settings in the replica set config:

  • priority — a float; a higher value makes a member more preferred as primary. priority: 0 means the member can never become primary (but still votes and replicates).

  • votes — 0 or 1. A member with votes: 0 does not participate in elections. At most seven members may vote.

  • hidden: true — the member is invisible to client read preference and drivers; useful for a dedicated analytics or backup node. Implies priority: 0.

  • secondaryDelaySecs — the member applies oplog entries only after that many seconds have passed, giving a rolling "point-in-time" copy that lags on purpose. Implies hidden: true and priority: 0.

// https://www.mongodb.com/docs/manual/tutorial/configure-secondary-only-replica-set-member/
const cfg = rs.conf();
cfg.members[2].priority = 0;
cfg.members[2].hidden = true;
cfg.members[2].secondaryDelaySecs = 3600;   // one-hour delayed copy
rs.reconfig(cfg);

An arbiter is a voting member that holds no data. It exists only to break ties in a set with an even number of data-bearing members. Prefer adding a real data-bearing secondary instead: an arbiter cannot acknowledge w: "majority" writes, so with one data-bearing secondary down, a set of primary + secondary + arbiter cannot satisfy majority write concern and can also expose the cache-pressure "flow control" problem. See Replica Set Arbiter for the caveats.

Rollback

If a former primary accepted writes that never replicated to a majority, then crashed and rejoined after a new primary was elected, those un-replicated writes are not on the new primary’s oplog. On rejoining, the old primary rolls them back: it reverts to the last common oplog point and writes the reverted documents to rollback files (BSON, under the data directory) for manual recovery. Writing with w: "majority" avoids this by ensuring an acknowledged write is already on a majority before the client sees success. See Replica Set Rollbacks.

Read preference

Read preference tells the driver which members a read may target. It does not change consistency guarantees on the primary; it trades freshness and latency.

Mode Behavior

primary

Default. All reads go to the primary; an error if there is no primary.

primaryPreferred

Primary if available, otherwise a secondary.

secondary

Reads only from secondaries; an error if none are eligible.

secondaryPreferred

A secondary if any is eligible, otherwise the primary.

nearest

The member with the lowest network latency, primary or secondary.

Any non-primary mode can return stale data, because a secondary may lag the primary. maxStalenessSeconds (minimum 90) tells the driver to skip secondaries estimated to be further behind than that. Tag sets filter eligible members by arbitrary key/value tags in the config (for example { region: "eu", disk: "ssd" }), so a read can be pinned to a data center.

// https://www.mongodb.com/docs/manual/core/read-preference/
db.events.find({ userId: 42 }).readPref(
  "secondaryPreferred",
  [ { region: "eu" }, {} ],   // prefer an eu-tagged member, fall back to any
  120                          // maxStalenessSeconds
)

// or per connection:
// mongodb://h1,h2,h3/app?replicaSet=rs0&readPreference=nearest&maxStalenessSeconds=120

Write concern, read concern, causal consistency

Write concern { w, j, wtimeout } decides how many members must acknowledge a write before the driver returns (introduced in Inserting data & write concern). For a replica set the important values are:

  • w: "majority" — a majority of data-bearing voting members have applied the write; it will survive any single failover. This is the default.

  • w: <number> — an explicit count of acknowledgements.

  • w: "<name>" — a custom write concern defined under settings.getLastErrorModes in the config, built from member tags (for example "one node in each of two data centers").

  • wtimeout — a millisecond cap on waiting for w; on expiry the client gets an error even though the write may still commit later.

// https://www.mongodb.com/docs/manual/reference/write-concern/
// define a custom write concern named "multiDC" from tags, then use it
const cfg = rs.conf();
cfg.settings = cfg.settings || {};
cfg.settings.getLastErrorModes = { multiDC: { dc: 2 } };  // members tagged dc, in 2 distinct values
rs.reconfig(cfg);

db.orders.insertOne({ _id: 1, total: 10 }, { writeConcern: { w: "multiDC", wtimeout: 5000 } })

Read concern decides how consistent and durable the data a read returns must be:

Level Meaning

local

Whatever the queried member has, no durability guarantee. Default on the primary for most reads.

available

Like local but, on a sharded cluster, skips the check for orphaned documents (lowest latency, may return orphans).

majority

Only data that a majority of members have acknowledged, so it cannot be rolled back.

linearizable

On the primary only: reflects every write acknowledged with w: "majority" before the read began. Combine with maxTimeMS.

snapshot

A single consistent snapshot across the whole read; used with multi-document transactions.

// https://www.mongodb.com/docs/manual/reference/read-concern/
db.accounts.find({ _id: "a-1" }).readConcern("majority")

Causal consistency is scoped to a session. Inside a causally consistent session the server guarantees "read your writes", "monotonic reads", and "monotonic writes" even when reads are served by different secondaries, by passing operation timestamps between calls.

// https://www.mongodb.com/docs/manual/core/read-isolation-consistency-recency/#causal-consistency
const s = db.getMongo().startSession({ causalConsistency: true });
const coll = s.getDatabase("app").tickets;
coll.updateOne({ _id: 7 }, { $set: { status: "closed" } }, { writeConcern: { w: "majority" } });
coll.find({ _id: 7 }).readConcern("majority").toArray();  // sees the update even on a secondary
s.endSession();

Configuring and connecting to a set

Start each mongod with the same --replSetName, then initiate the set once from any member and add the rest.

// https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set/
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "h1:27017", priority: 2 },
    { _id: 1, host: "h2:27017" },
    { _id: 2, host: "h3:27017" }
  ]
})

rs.add({ host: "h4:27017", priority: 0, votes: 0 })   // non-voting analytics node
rs.addArb("h5:27017")                                  // discouraged; prefer a data node

const cfg = rs.conf();
cfg.members[1].tags = { region: "eu" };
rs.reconfig(cfg, { force: false })

rs.status()   // member states, election metadata, replication lag, oplog window

Applications connect with a replica-set connection string that lists seed members and replicaSet=; the driver discovers the full membership and the current primary from any reachable seed, and re-discovers after a failover automatically.

mongodb://h1:27017,h2:27017,h3:27017/app?replicaSet=rs0&w=majority&readPreference=primaryPreferred&retryWrites=true

Driver-specific URI options and pooling behavior are covered in Drivers & tooling. For scaling writes across many primaries rather than replicating one, see Horizontal scaling with sharded clusters.