Running a live migration from on-premises MongoDB to Atlas used to mean one of two things: accepting a multi-hour maintenance window, or building a bespoke change-data-capture pipeline with Debezium, Kafka, and a custom consumer that you would own forever. MongoDB 7.0 introduced Cluster-to-Cluster Sync — powered by the mongosync utility — as a first-party answer to both problems. It continuously replicates an entire MongoDB cluster to another in real time, handles the initial bulk load and subsequent oplog tailing automatically, and lets your application cut over with a measured pause of seconds rather than hours. This guide covers everything you need to deploy, monitor, and cut over a production sync job using mongosync.
mongosyncis MongoDB's official utility for continuous, real-time replication between two MongoDB clusters — no Kafka, no Debezium, no custom CDC pipeline required.- It performs a full initial sync followed by oplog tailing to keep the destination cluster perpetually current.
- Primary use cases: live migrations to Atlas, multi-cloud active-passive replication, disaster recovery, and environment cloning.
- Both clusters must run MongoDB 6.0+ and share the same major version; sharded-to-replica-set topology changes are not supported.
- Cutover requires pausing sync, verifying the destination, and flipping application connection strings — target downtime is under 60 seconds on most workloads.
- mongosync does not replicate user credentials, system collections, or
localdatabase contents.
What is Cluster-to-Cluster Sync (mongosync)?
mongosync is a standalone binary — separate from mongod and mongos — that MongoDB ships as part of the Database Tools package starting in version 7.0. It connects to a source cluster and a destination cluster simultaneously, performs a full initial sync of all user databases and collections, and then tails the source oplog to continuously apply subsequent writes to the destination. The destination cluster stays within seconds of the source for as long as the sync job runs.
Unlike a replica set secondary — which is tightly coupled to its primary through a shared replication protocol — mongosync is topology-agnostic. The source can be a standalone replica set, a sharded cluster, or a MongoDB Atlas dedicated cluster. The destination can be any of the same. The two clusters do not need to be on the same network, in the same cloud region, or running identical hardware. This flexibility is what makes mongosync useful for cross-cloud and on-premises-to-cloud scenarios that traditional MongoDB replication cannot address.
How mongosync Works Internally
mongosync operates in two phases. During the collection copy phase, it reads all documents from every collection on the source using a cursor-based bulk read, writes them to the destination in parallel batches, and builds indexes on the destination as it goes. The initial sync is resumable — if the process crashes mid-copy, it picks up from a checkpoint rather than restarting from scratch. During the change event application phase, it tails the source oplog (or change stream on Atlas) and applies each insert, update, and delete to the destination in order, maintaining causal consistency. The sync job exposes a REST API on port 27182 that reports progress, lag, and health at every stage.
mongosync vs. Atlas Live Migration vs. mongodump
| Method | Continuous Sync | Required Downtime | Topology Flexibility | Sharded Support |
|---|---|---|---|---|
mongosync |
Yes | Seconds (cutover pause) | Any-to-any | Sharded-to-sharded only |
| Atlas Live Migration | Yes | Seconds | Self-managed → Atlas only | Yes |
mongodump / mongorestore |
No | Full duration of restore | Any-to-any | Yes |
| Replica set member sync | Yes | None (transparent) | Same replica set only | Within shard only |
Use Cases
Live Migration to MongoDB Atlas
This is the most common deployment pattern. An engineering team running a self-managed MongoDB replica set on AWS EC2 or on-premises hardware wants to migrate to Atlas without a maintenance window. mongosync starts the initial sync, the application continues writing to the on-premises source, and once lag drops to near zero the team pauses sync, validates the destination, updates the connection string in their application configuration, and resumes writes against Atlas. Total application-visible downtime: 30–90 seconds depending on connection pool drain time.
Multi-Cloud Active-Passive Replication
Teams with regulatory or latency requirements that span cloud providers — AWS in the US, GCP in Europe — use mongosync to maintain a continuously synchronized passive cluster in the secondary region. In a failover event, the passive cluster is already current and can accept writes immediately after a DNS or connection-string update. This is fundamentally different from MongoDB's built-in cross-region replica set, which places all nodes in a single logical replica set and requires direct network connectivity between all members.
Disaster Recovery
A mongosync job to an isolated DR cluster gives you a recovery point objective (RPO) measured in seconds and a recovery time objective (RTO) measured in minutes. Because the destination cluster is a fully functional, independently queryable MongoDB deployment, you can validate it against the source at any time and promote it to primary without any data reconstruction step.
Staging Environment Refresh
Teams that need a staging cluster to reflect production data without the complexity of snapshot restores use mongosync to run a one-time full sync to staging, then stop the sync and let staging diverge. When staging needs a refresh, they restart the sync job (which re-syncs the full dataset) and stop it again. This eliminates the mongodump/mongorestore cycle that typically takes hours on large datasets.
Prerequisites and Compatibility Requirements
MongoDB Version Requirements
Both the source and destination clusters must run MongoDB 6.0 or later. The source and destination must be on the same major version — you cannot use mongosync to simultaneously migrate data and upgrade from MongoDB 6.0 to 7.0. Version mismatch will cause mongosync to reject the configuration at startup.
- Sharded cluster to replica set migrations are not supported. If your source is sharded, your destination must also be sharded with a compatible shard count.
- Standalone
mongodinstances (not in a replica set) cannot be used as a source. The source must be a replica set or a sharded cluster with replica set shards. mongosyncdoes not replicate thelocaldatabase, system collections (admin,config), user credentials, or role definitions. Recreate users and roles on the destination before cutover.- The
featureCompatibilityVersion(FCV) of the source and destination must match.
Network and Permissions
The host running mongosync must have outbound network access to both the source and destination clusters on their respective ports. For Atlas destinations, whitelist the mongosync host IP in the Atlas Network Access settings. On both clusters, the mongosync user needs the clusterMonitor, readAnyDatabase, and readWrite built-in roles — plus atlasAdmin on Atlas destinations.
// Create the mongosync user on the SOURCE cluster
use admin
db.createUser({
user: "mongosync_src",
pwd: "StrongPassw0rd!",
roles: [
{ role: "clusterMonitor", db: "admin" },
{ role: "readAnyDatabase", db: "admin" },
{ role: "backup", db: "admin" }
]
})
// Create the mongosync user on the DESTINATION cluster
use admin
db.createUser({
user: "mongosync_dst",
pwd: "StrongPassw0rd!",
roles: [
{ role: "clusterMonitor", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" },
{ role: "dbAdminAnyDatabase", db: "admin" },
{ role: "restore", db: "admin" }
]
})Setting Up mongosync — Installation and Configuration
Step 1: Download and Install mongosync
mongosync is distributed as part of MongoDB Database Tools. Download the appropriate package for your OS from the MongoDB Download Center or install via package manager.
# Install on Ubuntu/Debian (replace with your MongoDB version and OS)
wget https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.9.4.deb
sudo dpkg -i mongodb-database-tools-ubuntu2204-x86_64-100.9.4.deb
# Verify mongosync is available
mongosync --version
# Output: mongosync version: 1.9.x
# macOS via Homebrew
brew install mongodb/brew/mongodb-database-toolsStep 2: Create the Configuration File
mongosync accepts all configuration via a JSON or YAML file passed with the --config flag. Storing credentials in the config file (rather than the command line) prevents them from appearing in process lists.
# /etc/mongosync/mongosync.conf
cluster0: "mongodb://mongosync_src:StrongPassw0rd!@source-rs-primary.example.com:27017,source-rs-secondary1.example.com:27017/?replicaSet=rs0&authSource=admin"
cluster1: "mongodb+srv://mongosync_dst:StrongPassw0rd!@mycluster.abc123.mongodb.net/?authSource=admin"
logPath: "/var/log/mongosync/mongosync.log"For sharded source clusters, point the connection string at a mongos router rather than individual shards. mongosync discovers all shards automatically through the mongos.
Step 3: Validate Connectivity Before Starting
# Test that mongosync can reach both clusters before starting the sync job
mongosync \
--cluster0 "mongodb://mongosync_src:StrongPassw0rd!@source-rs-primary.example.com:27017/?replicaSet=rs0&authSource=admin" \
--cluster1 "mongodb+srv://mongosync_dst:StrongPassw0rd!@mycluster.abc123.mongodb.net/?authSource=admin" \
--verifyConnectivity
# Expected output:
# Verifying cluster0 connection... OK
# Verifying cluster1 connection... OK
# Connectivity verification passed.Starting and Monitoring a Sync Job
Starting mongosync
Launch mongosync in the background or as a systemd service. Once running, it listens on port 27182 for REST API calls. The sync job does not start automatically — you must issue a start API call after the process is up. This separation lets you configure and validate the process before committing to the sync.
# Start the mongosync process (in a screen session or systemd unit)
mongosync --config /etc/mongosync/mongosync.conf &
# Confirm the process is up and listening
curl -s http://localhost:27182/api/v1/progress | jq .
# Expected: { "progress": { "state": "IDLE", ... } }
# Issue the start command to begin the sync job
curl -s -X POST http://localhost:27182/api/v1/start \
-H "Content-Type: application/json" \
-d '{
"source": "cluster0",
"destination": "cluster1"
}' | jq .Monitoring Sync Progress
The /api/v1/progress endpoint is the primary observability surface. Poll it at 30-second intervals during the initial sync phase to track how much data has been copied and estimate completion time.
# Check sync progress
curl -s http://localhost:27182/api/v1/progress | jq '{
state: .progress.state,
phase: .progress.phase,
lagTimeSeconds: .progress.lagTimeSeconds,
collectionsCopied: .progress.collectionsCopy.estimatedDocumentCounts.copied,
collectionsTotal: .progress.collectionsCopy.estimatedDocumentCounts.total,
estimatedCompletionSeconds: .progress.estimatedTotalSecondsToComplete
}'
# Example output during initial sync:
# {
# "state": "RUNNING",
# "phase": "collection copy",
# "lagTimeSeconds": null,
# "collectionsCopied": 12483920,
# "collectionsTotal": 48201038,
# "estimatedCompletionSeconds": 3240
# }
# Example output during continuous replication (post-copy):
# {
# "state": "RUNNING",
# "phase": "change event application",
# "lagTimeSeconds": 4,
# "collectionsCopied": 48201038,
# "collectionsTotal": 48201038,
# "estimatedCompletionSeconds": 0
# }Running mongosync as a systemd Service
For production deployments, manage mongosync with systemd to ensure it restarts automatically on failure and survives host reboots during long initial syncs.
# /etc/systemd/system/mongosync.service
[Unit]
Description=MongoDB Cluster-to-Cluster Sync
After=network.target
[Service]
Type=simple
User=mongosync
ExecStart=/usr/bin/mongosync --config /etc/mongosync/mongosync.conf
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mongosync
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable mongosync
sudo systemctl start mongosync
# Tail logs via journald
journalctl -u mongosync -fThe Cutover Process
Cutover is the moment you flip your application from the source cluster to the destination. The goal is a window measured in seconds, not minutes. A disciplined cutover follows four steps: pause sync, verify, switch connections, and commit.
Wait until lagTimeSeconds in the progress API is consistently below 5 seconds across at least three consecutive polls before pausing. A lag spike at the moment you pause means the destination is behind and your application may read stale data on the destination before the pause completes catch-up.
Step 1: Pause the Sync Job
Pausing tells mongosync to stop accepting new change events from the source but to finish applying all events it has already buffered. The destination is consistent and current once the pause completes. The source cluster continues serving your application normally during this pause.
# Issue the pause command
curl -s -X POST http://localhost:27182/api/v1/pause | jq .
# Poll until state is PAUSED
watch -n 2 "curl -s http://localhost:27182/api/v1/progress | jq '{state: .progress.state, lagTimeSeconds: .progress.lagTimeSeconds}'"
# Expected output when paused:
# {
# "state": "PAUSED",
# "lagTimeSeconds": 0
# }Step 2: Validate the Destination
With sync paused and lag at zero, run document count and spot-check queries against both clusters to confirm the destination matches the source before directing traffic to it.
// Run on both source and destination — counts should match exactly when lag = 0
db.orders.countDocuments() // e.g., 9,847,231
db.customers.countDocuments() // e.g., 1,204,588
// Spot-check a recently modified document on both sides
db.orders.findOne(
{ updatedAt: { $gte: new Date(Date.now() - 60000) } },
{ _id: 1, status: 1, updatedAt: 1 }
)
// Verify indexes were replicated correctly
db.orders.getIndexes()Step 3: Switch Application Connection Strings
Update your application's MongoDB connection string to point to the destination cluster. In Atlas, copy the SRV connection string from the Atlas UI. For self-managed destinations, update the replica set seed list. Deploy the updated configuration using your standard blue/green or rolling deployment process.
# Before cutover (source — on-premises)
MONGODB_URI="mongodb://app_user:pass@source-primary.internal:27017,source-secondary.internal:27017/mydb?replicaSet=rs0"
# After cutover (destination — Atlas)
MONGODB_URI="mongodb+srv://app_user:pass@mycluster.abc123.mongodb.net/mydb?retryWrites=true&w=majority"Step 4: Commit or Resume
Once your application is confirmed healthy against the destination, commit the sync job. Committing permanently terminates the sync relationship and cleans up internal state on the destination. If validation reveals a problem before you commit, you can resume the sync and continue running against the source.
# Application validated — commit the sync (permanent)
curl -s -X POST http://localhost:27182/api/v1/commit | jq .
# If validation failed — resume sync and stay on source
curl -s -X POST http://localhost:27182/api/v1/resume | jq .Limitations and Supported Topologies
- No sharded-to-replica-set migration. If the source is a sharded cluster, the destination must also be sharded. Reducing or changing topology requires a separate manual data migration step.
- No major version upgrades. mongosync requires matching major versions (6.0 → 6.0, 7.0 → 7.0). You cannot use it to simultaneously migrate and upgrade.
- System collections not replicated. User accounts, roles,
admindatabase configuration, and thelocaldatabase are excluded. Provision these manually on the destination before cutover. - Capped collections are replicated as regular collections. The capped property is not preserved on the destination.
- Single source per mongosync process. Each mongosync instance syncs one source cluster to one destination. For sharded clusters, one mongosync process handles all shards through the mongos router.
- Destination must be writable but empty. The destination cluster must not contain the databases being synced at the time the sync job starts. Pre-existing data in the destination will cause the sync job to fail.
- Oplog window. If mongosync is paused for longer than the source's oplog window (typically 24–72 hours on production clusters), it cannot resume — a fresh initial sync is required.
Supported Topology Matrix
| Source Topology | Destination Topology | Supported |
|---|---|---|
| Replica Set | Replica Set | Yes |
| Replica Set | Atlas Dedicated (M10+) | Yes |
| Sharded Cluster | Sharded Cluster | Yes |
| Atlas Dedicated | Replica Set | Yes |
| Sharded Cluster | Replica Set | No |
| Replica Set | Sharded Cluster | No |
| Standalone mongod | Any | No |
- Use
mongosyncfor any migration that requires a live application during the sync period — it eliminates the maintenance window thatmongodump/mongorestoreimposes. - Always wait for
lagTimeSecondsto drop below 5 seconds across multiple polling intervals before initiating the pause-and-cutover sequence. - Recreate users, roles, and system-level configuration on the destination before cutover — mongosync does not replicate the
admindatabase or user credentials. - Size your source cluster's oplog retention to cover the full initial sync duration plus a buffer — if the oplog wraps before mongosync completes the copy phase, the sync fails and must restart.
- Validate document counts and spot-check recently modified documents on the destination while sync is paused before committing — committing is irreversible.
- mongosync cannot change topology: sharded-to-sharded works, but sharded-to-replica-set or the reverse requires a different approach.
- For DR use cases, keep a permanent mongosync process running and monitor
lagTimeSecondsas a first-class SLO — a lag spike is an early indicator of replication problems.
Working with JusDB on MongoDB Migrations
JusDB manages MongoDB cluster migrations for engineering teams — mongosync setup, cutover planning, and post-migration validation. Our DBAs have migrated terabytes of MongoDB data with zero data loss and minimal downtime.
Explore JusDB MongoDB Management → | Talk to a DBA
Related reading: