This guide provides a comprehensive plan for upgrading our stack from MongoDB 6.0 and Mongoose 6.x to the latest stable versions. We are doing this because our current version of MongoDB has reached its end of life, and upgrading will also provide significant performance and security benefits.
Warning
MongoDB 6.0 is no longer supported as of July 31, 2025. Continuing to use an EOL version poses a security risk and means we will not receive bug fixes or support. You can view the official support policy on the MongoDB Legal website.
This document merges all previous analysis into a single, comprehensive guide. It covers the server upgrade path, all required code changes, and clarifies which previously discussed changes are not necessary.
| Feature / Aspect | MongoDB 6.0 | MongoDB 7.0 | MongoDB 8.0 / 8.2 (Latest) |
|---|---|---|---|
| Release Date | July 2022 | August 2023 | October 2024 / September 2025 |
| End of Life (EOL) | July 31, 2025 | August 31, 2027 | October 31, 2029 |
| Key Features | Enhanced time series, Change Stream improvements, Queryable Encryption (Preview) | Compound wildcard indexes, improved query performance, Queryable Encryption (GA) | Significant performance gains, improved memory management (TCMalloc), enhanced security features, native vector search (preview in 8.2) |
| Upgrade Path | Must upgrade to 7.0 before 8.0 | Must upgrade from 6.0 | Must upgrade from 7.0 |
| Critical Change | - | Free Monitoring decommissioned | Queries for null no longer match undefined fields |
Upgrading to MongoDB 8.0 brings major performance improvements. According to official benchmarks from MongoDB, the gains are significant:
- 36% Better Read Throughput
- 56% Faster Bulk Writes
- 32% Faster Mixed Workloads
- 60% Faster Time Series
Upgrading is not just about performance; it's also critical for security. Early versions of MongoDB 8.0 had several critical vulnerabilities that have since been patched.
Important
It is strongly recommended to upgrade directly to MongoDB 8.0.15+ or the latest 8.2.x release. These versions include fixes for all known critical security vulnerabilities and bugs.
Important
You cannot upgrade directly from MongoDB 6.0 to 8.0. The upgrade must be done sequentially: 6.0 → 7.0 → 8.0.
This procedure must be followed twice: once for 6.0 → 7.0, and again for 7.0 → 8.0.
- Backup the Database: Always create a full backup before starting.
- Verify FCV: Ensure the
featureCompatibilityVersionis set to the current version (e.g.,"6.0"before upgrading to 7.0).db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 }) - Shut Down: Shut down the
mongodinstance cleanly.db.adminCommand({ shutdown: 1 }) - Replace Binaries: Replace the old MongoDB binaries with the new version's binaries.
- Restart: Restart the
mongodprocess. - Enable New Features: After a burn-in period to ensure stability, update the FCV to the new version.
# For 7.0 db.adminCommand({ setFeatureCompatibilityVersion: "7.0", confirm: true }) # For 8.0 db.adminCommand({ setFeatureCompatibilityVersion: "8.0", confirm: true })
After verifying against the npm registry, here are the correct latest versions for our dependencies.
Warning
@ladjs/mongoose has not been updated in over a year. Its peerDependencies only specify mongoose: '>=6'. While this means it should work with Mongoose 8, it is not guaranteed. This is a migration risk that must be mitigated with thorough testing of our Mongoose configuration and connection logic.
Update package.json with these versions and run pnpm install:
{
"dependencies": {
"mongoose": "^8.20.0",
"@ladjs/mongoose": "^7.0.0",
"passport-local-mongoose": "^8.0.0",
"mongoose-unique-validator": "^4.0.1"
}
}Note
The extensive refactoring of doc.field = undefined is NOT required.
Our initial analysis flagged this pattern. However, after a deeper look, we can confirm that Mongoose is designed to handle this correctly. When you set a field to undefined and call .save(), Mongoose automatically translates this into an $unset command for MongoDB.
While there was a bug in older Mongoose 6.x versions related to nested objects, upgrading to Mongoose 8.x resolves this. The MongoDB 8.0 query behavior change is only a problem if undefined is actually stored in the database, which Mongoose prevents.
Conclusion: We can save the effort of refactoring 24+ files. The risk is very low, and the code will function as expected after the Mongoose upgrade.
This section covers all breaking changes from Mongoose 6 through 8 and how they affect our codebase.
1. Set strictQuery: true (Global Impact)
- The Change: In Mongoose 7,
strictQuerydefaults tofalse, which can cause unexpected behavior by allowing queries with fields not in our schema. We must set it totrueglobally to maintain our current, safer behavior. See the Mongoose 7 migration guide for details. - File:
/config/mongoose.js
const m = new Mongoose({
logger,
hideMeta: true,
bindEvents: false,
strictQuery: true, // Add this line
mongo: {
options: {
compressors: ["snappy"]
}
}
});2. Replace document.remove()
- The Change: The
.remove()method was removed in Mongoose 7. It must be replaced with.deleteOne(). - File:
/app/controllers/web/admin/domains.js
- await domain.remove();
+ await domain.deleteOne();3. Replace findByIdAndRemove()
- The Change: This method was removed in Mongoose 8. It must be replaced with
findByIdAndDelete(). - File:
/app/controllers/api/v1/paypal.js
- await Payments.findByIdAndRemove(payment._id);
+ await Payments.findByIdAndDelete(payment._id);- Dropped Callback Support: Mongoose 7 dropped support for callbacks. No action required, as our codebase consistently uses
async/await. ObjectIdConstructor:mongoose.Types.ObjectId()can no longer be called without thenewkeyword. No action required, as our codebase already uses the correctnewkeyword.- Removed
Model.count():Model.count()was removed in Mongoose 8. No action required, as our codebase already usescountDocuments(). document.deleteOne()Return Value: In Mongoose 8,doc.deleteOne()returns aDeleteResultobject instead of the deleted document. Since we don't use the return value of this operation, no further logic changes are needed.
-
$lookupBehavior Change: The$lookupaggregation stage in MongoDB 8.0 will no longer match anullvalue in alocalFieldwith anundefinedor non-existentforeignField. We use$lookupin/app/controllers/web/admin/inquiries.jsand/app/controllers/web/admin/payments.js. The current usage appears safe, but we must verify that thelocalField(user) in both collections can never benullorundefinedif a join is expected. -
nullis Valid for Non-Required String Enums: In Mongoose 8, you can now savenullto a string field with anenumvalidator (ifrequiredis nottrue). This could change validation behavior. We must review all schemas with stringenumvalidators to ensure this is acceptable. If not, we must add a custom validator to disallownull.
passport-local-mongoose: We are upgrading to^8.0.0. While this version was released to add Mongoose 7 support, it is the latest available and is being used by the community with Mongoose 8. We must thoroughly test all authentication flows (registration, login, password reset) after the upgrade to confirm compatibility.
- Create a full backup of the production database.
- Set up a staging environment that mirrors production.
- Upgrade MongoDB server from 6.0 → 7.0 in staging.
- Upgrade MongoDB server from 7.0 → 8.0 in staging.
- Update
package.jsondependencies to the verified versions and runpnpm install. - Apply the 3 required code changes (
strictQuery,deleteOne,findByIdAndDelete). - Review all schemas with
enumvalidators to check for desired behavior withnullvalues. - Verify
$lookupusage in/app/controllers/web/admin/inquiries.jsand/app/controllers/web/admin/payments.js. - Thoroughly test the application in staging, paying close attention to:
- All authentication flows (registration, login, password reset).
- All database interactions affected by the Mongoose method changes.
- The Mongoose connection logic handled by
@ladjs/mongoose.
- Run the full test suite to ensure no regressions.
- Schedule and perform the production deployment.
- Monitor application logs and performance after deployment.