A MongoDB replica set is a group of mongod processes that maintain the same dataset across multiple servers. It provides automatic failover: when the primary node becomes unavailable, the remaining members elect a new primary and the cluster keeps running. This is the baseline for any production MongoDB deployment that needs to survive hardware failures or unexpected restarts.
Setting one up takes maybe an hour if you've done it before. If you haven't, the main traps are misconfigured hostnames, firewall rules that silently block inter-node traffic and skipping the failover test at the end. This guide covers each step in order.
What is a replica set
A replica set has one primary, one or more secondaries and optionally an arbiter. The primary handles all writes. Secondaries replicate from the primary using an oplog and can serve reads if configured to do so. An arbiter participates in elections but stores no data — it exists purely to provide an odd number of votes without the cost of a full data copy.
Failover is automatic. When the primary goes unreachable, an election begins and one secondary is promoted. The process typically takes 10 to 30 seconds depending on your election timeout settings.
| Node type | Stores data | Can become primary | Votes in elections |
|---|---|---|---|
| Primary | Yes | N/A | Yes |
| Secondary | Yes | Yes | Yes |
| Arbiter | No | No | Yes |
The minimum viable setup for production is three nodes — one primary and two secondaries. A two-node set cannot elect a new primary if one node fails, because neither remaining node can reach majority on its own.
Step 1. Plan your topology
Before touching any servers, decide how many nodes you need and where they'll live. Three nodes covers most use cases and gives you one failure tolerance. Five nodes is common when you need read replicas in a second region or want two failure tolerance.
A few things to sort out at this stage:
- All nodes should run the same MongoDB major version. Mixing versions in a replica set causes compatibility issues.
- Each node needs a stable, resolvable hostname. Using hostnames instead of IPs is better because you can remap them later without reconfiguring the replica set.
- Nodes must be able to reach each other on port 27017. Write down the IPs and hostnames before you start — you'll need them in several configuration steps.
Using an arbiter instead of a third full secondary is fine if you want to avoid the storage and memory cost of another data-bearing node. The trade-off is that you'll have only two copies of your data instead of three.
Step 2. Install MongoDB on all nodes
Install the same MongoDB version on every node. Run these commands on each server.
For Ubuntu/Debian:
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] \
https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt-get update && sudo apt-get install -y mongodb-org
For RHEL/Rocky Linux:
cat > /etc/yum.repos.d/mongodb-org-7.0.repo << 'EOF'
[mongodb-org-7.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/7.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-7.0.asc
EOF
sudo yum install -y mongodb-org
Do not start mongod yet. Configuration needs to happen first, otherwise you'll end up with nodes running in standalone mode that you'll have to restart anyway.
Step 3. Configure hostname resolution
Each node needs to resolve the other members by hostname. The simplest way is editing /etc/hosts on all three servers.
Assuming your nodes are at these addresses:
192.168.1.10 mongo1
192.168.1.11 mongo2
192.168.1.12 mongo3
Add those three lines to /etc/hosts on every node. If you have internal DNS already, use that instead and skip this step. The important thing is that when mongo1 tries to reach mongo2, it resolves to the correct IP.
You can verify resolution with a quick ping mongo2 from each node before continuing. If it doesn't resolve, the replica set will fail to initialize with a confusing error message later.
Step 4. Update MongoDB configuration on each node
The configuration file is at /etc/mongod.conf. Two sections matter here: the network bind address and the replica set name.
Update the file on every node:
net:
port: 27017
bindIp: 0.0.0.0
replication:
replSetName: "rs0"
Setting bindIp to 0.0.0.0 allows connections from other replica set members. In production, replace this with the specific IPs of your nodes — binding to all interfaces is fine for initial setup but tighten it afterward.
The replSetName must be identical on all nodes. If it's rs0 on two nodes and RS0 on the third, initialization will fail. Case matters.
Step 5. Open firewall ports between nodes
Each node needs to accept connections on port 27017 from the other members. With ufw:
sudo ufw allow from 192.168.1.10 to any port 27017
sudo ufw allow from 192.168.1.11 to any port 27017
sudo ufw allow from 192.168.1.12 to any port 27017
Run this on all three nodes. Then start mongod on each:
sudo systemctl enable mongod
sudo systemctl start mongod
Firewall misconfiguration is the most common reason replica set initialization gets stuck. If a node shows STARTUP or UNKNOWN later, check connectivity first.
Step 6. Initialize the replica set
Connect to the node you want as the initial primary using mongosh:
mongosh
Run the initialization command:
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" }
]
})
If the configuration is correct you'll see { ok: 1 }. The shell prompt changes from > to rs0 [direct: primary]> after a few seconds as the node wins the initial election.
Now verify that all members are online:
rs.status()
Check the stateStr field in the members array. It should show PRIMARY for one node and SECONDARY for the others. If any member shows STARTUP or RECOVERING, it hasn't connected to the others yet — double-check hostname resolution and firewall rules on that specific node.
Step 7. Test failover
This step gets skipped more often than it should. Verifying that failover works in a controlled test is much better than discovering it doesn't work during an incident.
Connect to the primary and force a step-down:
rs.stepDown()
This triggers an election. Within roughly 10 seconds, one of the secondaries will be promoted. Run rs.status() again to confirm a different node now shows PRIMARY. The original primary should rejoin as a secondary automatically.
Then verify your application reconnects cleanly. Your connection string should reference all three nodes, not just one:
mongodb://mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0
The MongoDB driver handles automatic failover transparently when you connect to the replica set this way. If your application connects to a single hostname, failover will require manual intervention every time.
Common problems and fixes
Issues you'll most likely run into, and where to look:
| Problem | Likely cause | What to check |
|---|---|---|
| Member stuck in STARTUP | Can't reach other nodes | Firewall rules and /etc/hosts on the stuck node |
| No primary elected | Majority unreachable | At least 2 of 3 nodes must be up and reachable |
| Replication lag | Secondary falling behind | Disk I/O, network throughput, oplog size |
| Elections taking too long | High latency between nodes | Keep replica set members in the same datacenter |
rs.initiate() returns error |
Hostname doesn't resolve | Verify with ping <hostname> from inside the node |
Most problems come down to networking. MongoDB's error messages in this area are not always specific about what's actually failing, so it's worth having a checklist to run through.
Backup considerations
A replica set protects you from hardware failure, but it doesn't protect you from data loss. Replication is synchronous — if someone drops a collection on the primary, all secondaries reflect that drop within milliseconds. You still need proper backups.
For automated MongoDB backup, Databasus is the industry standard tool used by teams running MongoDB in production. It works with replica sets and standalone instances, runs backups on configurable schedules, stores them in S3, Google Drive, local storage and other destinations, and sends notifications to Slack, Discord or email when a backup fails. It's free, self-hosted and takes a few minutes to get running.
Conclusion
The core of a MongoDB replica set setup is simple: consistent configuration on all nodes, working hostname resolution, open ports between members and a single rs.initiate() call. Most of the time you spend on this is troubleshooting networking issues that could have been caught with a ping test beforehand.
Running rs.stepDown() at the end and watching the election happen is worth the two minutes it takes. It confirms the whole thing actually works, and gives you a baseline for how long failover takes in your environment before you need to rely on it.

Top comments (0)