Type something to search...
How to Upgrade MongoDB Versions Safely in Production

How to Upgrade MongoDB Versions Safely in Production

Database upgrades have a way of getting postponed. The current version works, the upgrade feels risky, and there's always something more urgent. Then one day you realize you're two major versions behind, your version is approaching end of life, and the driver you want to use has dropped support for it. Now the upgrade is urgent and bigger than it needed to be.

The good news is that MongoDB is designed to be upgraded without downtime. Replica sets let you upgrade one member at a time while the others keep serving traffic, and the feature compatibility version (FCV) gives you a window where you can still roll back after the new binaries are running. Upgrades go wrong when people skip steps, not because the process is inherently dangerous.

This guide covers planning an upgrade, the rules about upgrade paths, how FCV works, a step-by-step rolling upgrade for replica sets and sharded clusters, how to roll back, and what's different on MongoDB Atlas.

Know the Rules Before You Start

Upgrade One Major Version at a Time

You can't jump from 6.0 straight to 8.0. Major version upgrades must go through each intermediate major release in order: 6.0 to 7.0, then 7.0 to 8.0. At each step, you upgrade binaries and then raise the FCV before moving to the next major version.

Patch releases within a major version (for example, 8.0.x to a later 8.0.x) are simpler: they're binary replacements with no FCV change, and you should apply them regularly for bug and security fixes.

Check Support Timelines

Each major version has an end-of-life date, after which it stops receiving fixes. Check MongoDB's lifecycle schedule and plan upgrades well ahead of EOL so you're never forced to rush one.

Check Driver Compatibility

Your application drivers must support the server version you're moving to. MongoDB publishes driver compatibility tables for every language. As a rule, upgrade drivers first: a recent driver works with both your current server version and the new one, so it removes a variable from the server upgrade. Driver upgrades have their own breaking changes, so ship them as a separate release.

Understanding Feature Compatibility Version

The FCV is the key to safe upgrades. It controls which features, and more importantly which on-disk data formats, a server is allowed to use.

When you install 8.0 binaries on a server that was running 7.0, the FCV stays at "7.0". The server runs the new code, but it avoids features that would write data older binaries can't read. That's what makes rollback possible: until you raise the FCV, you can replace the binaries with the previous version and start up on the same data files.

Check the current FCV:

db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 });
{ featureCompatibilityVersion: { version: '7.0' }, ok: 1 }

Once you raise the FCV to "8.0", new features become available and data may be written in formats the old version can't read. Downgrading from that point requires lowering the FCV first (where supported) and is a much bigger operation. The practical advice: don't raise the FCV until you're confident in the new version, typically after it has run in production for several days to a couple of weeks.

Step 1: Plan and Prepare

A little preparation removes most of the risk.

Read the release notes and compatibility changes. Every major version has a "Compatibility Changes" page listing removed commands, changed defaults, and behavior differences. Search your codebase for anything listed. Common categories include removed or renamed commands and parameters, changed query or aggregation behavior in edge cases, and stricter validation.

Check for startup warnings and deprecated settings. Look at your mongod.conf against the new version's configuration options. Settings removed in the target version can prevent mongod from starting.

Verify replica set health. Upgrade a healthy cluster, never a struggling one:

rs.status().members.map((m) => ({
  name: m.name,
  state: m.stateStr,
  health: m.health,
  lagSeconds: m.optimeDate
    ? (rs.status().members.find((p) => p.stateStr === "PRIMARY").optimeDate -
        m.optimeDate) /
      1000
    : null,
}));
[
  { name: "db1:27017", state: "PRIMARY", health: 1, lagSeconds: 0 },
  { name: "db2:27017", state: "SECONDARY", health: 1, lagSeconds: 0 },
  { name: "db3:27017", state: "SECONDARY", health: 1, lagSeconds: 1 },
];

All members healthy, lag near zero. Also confirm the oplog window is comfortable, since members will be offline briefly during the upgrade and need to catch up.

Take a backup. Take a fresh, verified backup immediately before the upgrade. If something goes badly wrong, this is your ultimate fallback. Backup and Restore Strategies for MongoDB covers the options.

Rehearse in staging. Run the exact upgrade procedure on a staging environment with production-like data and configuration. Then run your application's test suite and a load test against it. Pay attention to query performance: the query planner changes between major versions, and occasionally a query picks a different plan.

Step 2: Rolling Upgrade of a Replica Set

The rolling upgrade replaces binaries one member at a time, always keeping a majority available. The order is: secondaries first, then step down the primary, then upgrade the former primary.

Upgrade Each Secondary

On one secondary at a time:

# 1. Stop mongod cleanly
sudo systemctl stop mongod

# 2. Install the new binaries (example for Ubuntu/Debian with the 8.0 repo configured)
VERSION=8.0.12   # the exact patch release you tested in staging
sudo apt-get update
sudo apt-get install -y mongodb-org=$VERSION mongodb-org-server=$VERSION \
  mongodb-org-mongos=$VERSION mongodb-org-database=$VERSION mongodb-org-tools

# 3. Start mongod with the same config and data files
sudo systemctl start mongod

Adding the new major version's package repository is part of step 2; follow the installation docs for your platform. Pin the exact patch version you tested in staging rather than whatever is newest (the 8.0.12 above is just an example).

After it starts, confirm the version and wait for the member to return to SECONDARY and catch up before touching the next one:

db.version();
// '8.0.x'

rs.status().members.find((m) => m.self).stateStr;
// 'SECONDARY'

Check the log for errors or new warnings too. Don't rush this. Upgrading the next member while the previous one is still recovering reduces your fault tolerance.

Step Down the Primary

With all secondaries upgraded and healthy, connect to the primary and step it down:

rs.stepDown(120);

An election chooses one of the upgraded secondaries as the new primary. Drivers with retryable writes enabled (the default in modern drivers) handle the election transparently for most operations, though you'll see a few seconds of elevated latency. Schedule this for a low-traffic period anyway.

Upgrade the Former Primary

It's now a secondary. Upgrade it exactly like the others: stop, install, start, verify, wait for it to catch up.

At this point, every member runs the new binaries, but FCV is still at the old version.

Step 3: Upgrading a Sharded Cluster

Sharded clusters follow the same principle with a specific component order:

  1. Stop the balancer so no chunk migrations happen mid-upgrade:

    sh.stopBalancer();
    sh.getBalancerState(); // false
    
  2. Upgrade the config server replica set using the rolling procedure above.

  3. Upgrade each shard replica set, one shard at a time, each with the rolling procedure.

  4. Upgrade the mongos routers, one at a time, so applications always have a router available.

  5. Restart the balancer:

    sh.startBalancer();
    

Always check the upgrade documentation for your specific target version, as the exact requirements for sharded clusters occasionally change between releases.

Step 4: Run and Observe

Now let the new version run with the old FCV. Watch the metrics you care about most: operation latency, CPU, cache usage, slow query logs, and application error rates. Compare them to your pre-upgrade baseline.

Look specifically for:

  • Query plan changes. New slow queries in the profiler or logs often mean a plan changed. Check them with explain() and consider hinting or adjusting indexes.
  • New warnings in the logs that point at deprecated usage.
  • Driver errors in application logs, especially around commands that changed behavior.

Most teams wait several days to two weeks here. Longer gives more confidence, but remember that while the FCV is on the old version, you can't use the new version's features.

Step 5: Raise the Feature Compatibility Version

When you're satisfied, raise the FCV. Run this on the primary (or through mongos for a sharded cluster):

db.adminCommand({ setFeatureCompatibilityVersion: "8.0", confirm: true });
{
  ok: 1;
}

The confirm: true field is required in recent versions, as a deliberate speed bump reminding you that this step makes downgrading harder. Verify it took effect on every member:

db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 });
// { featureCompatibilityVersion: { version: '8.0' }, ok: 1 }

If you're going to the next major version afterward (for example, 7.0 to 8.0 on the way from 6.0), this is the point where you repeat the whole process for the next step.

Rolling Back

How you roll back depends on how far you got.

Before raising the FCV: reverse the rolling upgrade. Replace the binaries on each member with the previous version, secondaries first, stepping down the primary, just like the upgrade. Because the data files are still in the old format, the old binaries start normally.

After raising the FCV: it's more involved. Where the version supports it, you first set the FCV back to the previous version, then roll the binaries back. Some features used after the FCV bump have to be removed first (the server will tell you when a downgrade is blocked). Check the downgrade documentation for your specific version. In the worst case, you restore the pre-upgrade backup, which is exactly why you took one.

This asymmetry is the whole reason for waiting before you raise the FCV.

Upgrading on MongoDB Atlas

Atlas automates most of this. For patch versions, Atlas applies updates automatically during your project's maintenance window, using a rolling process. You can configure the maintenance window so it lands at a low-traffic time.

For major versions, you choose when to upgrade from the cluster's configuration (Edit Configuration, then the MongoDB version setting). Atlas performs the rolling upgrade for you. Depending on your configuration, Atlas may also give you options around FCV handling after the upgrade, which can provide a period where downgrade is possible; check the current Atlas documentation for how this works on your tier.

Atlas also notifies you well in advance when a version is approaching end of life and will eventually upgrade clusters running unsupported versions automatically. Don't let it come to that: upgrading on your own schedule, after testing, is always better.

The preparation steps still apply on Atlas: upgrade drivers first, read the compatibility notes, test against a staging cluster on the new version, and take an on-demand snapshot before you start.

Upgrade Checklist

Before:

  • Read the release notes and compatibility changes for the target version.
  • Upgrade application drivers to versions that support the target server.
  • Rehearse the full upgrade in staging, including a load test.
  • Confirm the cluster is healthy, with low replication lag and a comfortable oplog window.
  • Take and verify a backup.

During:

  • For sharded clusters, stop the balancer first.
  • Upgrade secondaries one at a time, waiting for each to catch up.
  • Step down the primary and upgrade it last.
  • For sharded clusters: config servers, then shards, then mongos, then restart the balancer.

After:

  • Monitor latency, errors, and slow queries against your baseline.
  • Wait before raising the FCV.
  • Raise the FCV with confirm: true, and verify it on every member.

Common Mistakes

Skipping a major version. Installing 8.0 binaries on data last used by 6.0 doesn't work. Go through each major version and raise the FCV at each step.

Raising the FCV immediately. It removes your easy rollback path on day one, when you're least sure the upgrade is fine.

Upgrading several members at once. You lose fault tolerance during the window where you need it most. One member at a time, with verification in between.

Forgetting the balancer. Chunk migrations during a sharded upgrade can fail or cause inconsistencies. Stop it first.

Not testing query performance. Most upgrade surprises are plan changes, not crashes. Load-test in staging with realistic queries.

Conclusion

A safe MongoDB upgrade is a sequence of small, verifiable steps: upgrade drivers, rehearse in staging, back up, roll new binaries through secondaries and then the primary (config servers, shards, and routers in order for sharded clusters), observe, and only then raise the feature compatibility version. Going one major version at a time and keeping the FCV low until you're confident gives you a simple rollback path throughout.

Your next step: run db.version() and check the FCV on your production cluster today, then look up that version's end-of-life date. If it's within the next year, schedule the staging rehearsal now.

Tags :
Share :

Related Posts

A Complete Guide to MongoDB Query Operators

A Complete Guide to MongoDB Query Operators

Your first MongoDB queries are usually simple equality filters: find the user with this email, find orders with this status. That covers a surprising

Continue Reading
Async MongoDB in Python with Motor and FastAPI

Async MongoDB in Python with Motor and FastAPI

FastAPI runs your endpoints on an event loop. That's what lets a single worker juggle hundreds of concurrent requests: while one request waits on the

Continue Reading
Atlas Online Archive: Tiering Cold Data to Cut Costs

Atlas Online Archive: Tiering Cold Data to Cut Costs

Look at almost any production database and you'll find the same shape. A small slice of recent data gets nearly all the reads and writes: this week's

Continue Reading