This post was originally published in January 2022 and was updated in February 2025.
One of the reasons for using MongoDB is its simplicity in scaling its structure, either as Replicaset(scale-up) or as Sharded Cluster(scale-out).
Although a bit tricky in Sharded environments, the reverse process is also feasible.
From the possibilities, you can:
So far, so good, but if for some reason that mongo project which started as a Replica Set does not require such structure and now a Standalone server meets the requirements;
This blog post will walk through the necessary steps to convert a Replica Set into a Standalone server.
Before starting the process, it is important to mention that it’s not advised to run Standalone configuration on production servers as your entire availability will only rely on a single point.
Another observation is this activity requires downtime, that’s because you will need to remove the replication parameter, which can not be at runtime.
Last note; to avoid any data change during the procedure, it’s recommended to stop writing from the application. You will need to adjust the connection string at the end of the process for the new configuration.
The testing environment is a standard Replica Set configuration with three nodes, as seen at the following replication status.
|
1 |
[<br> {<br> "_id" : 0,<br> "name" : "node1:27017",<br> "stateStr" : "PRIMARY"<br> },<br> {<br> "_id" : 1,<br> "name" : "node2:27017",<br> "stateStr" : "SECONDARY",<br> },<br> {<br> "_id" : 2,<br> "name" : "node3:27017",<br> "stateStr" : "SECONDARY",<br>] |
PRIMARY is our reliable data source; thus, it will be the Standalone server at the end of the process.It is not needed to trigger any election process unless you have a preferred node to be the Standalone. If that’s your case and that node is not the PRIMARY yet, please make sure to make PRIMARY before starting the procedure.
PRIMARY node, let’s remove node 2 and 3:|
1 |
rs0:PRIMARY> rs.remove("node2:27017")<br>{<br> "ok" : 1,<br> "$clusterTime" : {<br> "clusterTime" : Timestamp(1640109346, 1),<br> "signature" : {<br> "hash" : BinData(0,"663Y5u3GkZkQ0Ci7EU8IYUldVIM="),<br> "keyId" : NumberLong("7044208418021703684")<br> }<br> },<br> "operationTime" : Timestamp(1640109346, 1)<br>}<br><br> |
|
1 |
rs0:PRIMARY> rs.remove("node3:27017")<br>{<br> "ok" : 1,<br> "$clusterTime" : {<br> "clusterTime" : Timestamp(1640109380, 1),<br> "signature" : {<br> "hash" : BinData(0,"RReAzLZmHWfzVcdnLoGJ/uz04Vo="),<br> "keyId" : NumberLong("7044208418021703684")<br> }<br> },<br> "operationTime" : Timestamp(1640109380, 1)<br>} |
Please note: Although my Replica Set comprises three nodes, you must let only the PRIMARY on replication at this step. If you have more nodes, the process is the same.
Then, the expected status should be:
|
1 |
rs0:PRIMARY> rs.status().members<br>[<br> {<br> "_id" : 0,<br> "name" : "node1:27017",<br> "health" : 1,<br> "state" : 1,<br> "stateStr" : "PRIMARY",<br> "uptime" : 1988,<br> "optime" : {<br> "ts" : Timestamp(1640109560, 1),<br> "t" : NumberLong(1)<br> },<br> "optimeDate" : ISODate("2021-12-21T17:59:20Z"),<br> "syncSourceHost" : "",<br> "syncSourceId" : -1,<br> "infoMessage" : "",<br> "electionTime" : Timestamp(1640107580, 2),<br> "electionDate" : ISODate("2021-12-21T17:26:20Z"),<br> "configVersion" : 5,<br> "configTerm" : 1,<br> "self" : true,<br> "lastHeartbeatMessage" : ""<br> }<br>] |
mongod process. Keeping their data safe, as it can be used in a recovery scenario if necessary:|
1 |
node2-shell> systemctl stop mongod<br>node3-shell> systemctl stop mongod |
PRIMARY and restart it, but first! In the configuration file:
|
1 |
#replication:<br># replSetName: "rs0" |
If you start MongoDB via the command line, please remove the option --replSet.
mongod on PRIMARY:|
1 |
node1-shell> systemctl restart mongod |
PRIMARY, now Standalone, MongoDB will warn you with the following message:|
1 |
2021-12-21T18:23:52.056+00:00: Document(s) exist in 'system.replset', but started without --replSet. Database contents may appear inconsistent with the writes that were visible when this node was running as part of a replica set. Restart with --replSet unless you are doing maintenance and no other clients are connected. The TTL collection monitor will not start because of this. For more info see http://dochub.mongodb.org/core/ttlcollections |
That is because the node was working under a Replica Set configuration, and although it’s a Standalone now, there is the old structure existing.
For further detail, that structure resides under the local database, which holds all necessary data for replication, and for the database itself:
|
1 |
mongo> use local<br>switched to db local<br><br>mongo> show collections<br>oplog.rs<br>replset.election<br>replset.minvalid<br>startup_log<br>system.replset |
To remove that warning, you should remove the old Replica Set object on local.system.replset.
local.system.replsetholds the replica set’s configuration object as its single document. To view the object’s configuration information, issuers.conf()from mongosh. You can also query this collection directly.
However, it’s important to mention the local.system.replset is a system object, and there is no built-in role that you can use to remove objects – Unless you create a custom role that grants anyAction on anyResource to the user, Or you grant the internal role __system to a user.
⚠️
Please note, both of that options gives access to every resource in the system and is intended for internal use. Do not use this permissions, other than in exceptional circumstances.
For this process, we will create a new user, temporary, with __system privilege in which we can drop later after the action.
|
1 |
mongo> db.getSiblingDB("admin").createUser({user: "dba_system", "pwd": "sekret","roles" : [{ "db" : "admin", "role" : "__system" }]}); |
Confirmation output:
|
1 |
Successfully added user:{<br> "user":"dba_system",<br> "roles":[<br> {<br> "db":"admin",<br> "role":"__system"<br> }<br> ]<br>} |
|
1 |
mongo> use local;<br>mongo> db.system.replset.remove({}) |
Confirmation output:
|
1 |
WriteResult({ "nRemoved" : 1 }) |
|
1 |
mongo> db.dropUser("dba_system") |
|
1 |
shell> systemctl restart mongod |
Once reconnected, you will not see that warning again, and the server is ready!
The conversion process, in general, is simple and should not face problems. But is essential to highlight that we strongly recommend you test and run this procedure in a lower environment. And if you plan to use it on production, please bear in mind the said before.
And if you have any questions, feel free to contact us in the comment section below!
Ready to leave MongoDB, Inc. behind? Percona is the cost-effective, enterprise-grade alternative businesses trust.
Resources
RELATED POSTS