How to use this sheet
Commands are for mongosh, the current MongoDB shell (the legacy mongo shell was removed in 6.0). Run admin commands against the admin database or with db.adminCommand(). On Atlas, some commands (for example shutdown, setParameter, logRotate) are not available to your user; use the Atlas UI or Admin API instead.
Connect
# Replica set, prompting for the password
mongosh "mongodb://db1:27017,db2:27017,db3:27017/?replicaSet=rs0" \
--username dbadmin --authenticationDatabase admin
# Atlas / SRV with TLS
mongosh "mongodb+srv://cluster0.example.mongodb.net/" --username dbadmin
# Run one command and exit (good for scripts)
mongosh --quiet "mongodb://db1:27017/admin" -u dbadmin -p --eval 'db.hello().isWritablePrimary'
Server health
db.version()
db.hello() // primary/secondary, set name, hosts
db.adminCommand({ getCmdLineOpts: 1 }).parsed // effective config file settings
db.serverStatus().connections // current, available, totalCreated
db.serverStatus().opcounters // insert/query/update/delete/getmore/command
db.serverStatus().mem // resident and virtual MB
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })
WiredTiger cache use:
const c = db.serverStatus().wiredTiger.cache;
print("used GB: ", (c["bytes currently in the cache"] / 1024 ** 3).toFixed(1));
print("max GB: ", (c["maximum bytes configured"] / 1024 ** 3).toFixed(1));
print("dirty GB: ", (c["tracked dirty bytes in the cache"] / 1024 ** 3).toFixed(2));
From the OS shell, live counters every 5 seconds:
mongostat --uri "mongodb://dbadmin@db1:27017/?authSource=admin" 5
mongotop --uri "mongodb://dbadmin@db1:27017/?authSource=admin" 5
What is running right now
// Operations running longer than 5 seconds
db.currentOp({ active: true, secs_running: { $gt: 5 } })
// Aggregation form: more filtering, includes other users' ops
db.getSiblingDB("admin").aggregate([
{ $currentOp: { allUsers: true, idleConnections: false } },
{ $match: { active: true, secs_running: { $gte: 5 } } },
{ $project: { opid: 1, secs_running: 1, op: 1, ns: 1, client: 1,
"command.filter": 1, planSummary: 1, waitingForLock: 1 } },
{ $sort: { secs_running: -1 } }
])
// Index builds in progress
db.currentOp({ $or: [ { op: "command", "command.createIndexes": { $exists: true } },
{ op: "none", msg: /^Index Build/ } ] })
db.killOp(12345) // opid from the output above
Warning
Check
ns,clientandopbeforekillOp. Killing an internal replication or balancer operation, or a long multi-document transaction near its commit, can do more harm than the slow query.
Replica sets
rs.status() // member state, health, optime, lag
rs.conf() // members, priorities, votes, hidden, delay
rs.printReplicationInfo() // oplog size and time window on this member
rs.printSecondaryReplicationInfo() // how far each secondary is behind the primary
rs.stepDown(60) // primary steps down, not re-electable for 60 s
rs.freeze(120) // run on a secondary: don't seek election for 120 s
rs.add({ host: "db4:27017", priority: 0, votes: 0 })
rs.remove("db4:27017")
Quick lag summary:
rs.status().members.map(m => ({ name: m.name, state: m.stateStr,
lagSec: (rs.status().members.find(p => p.stateStr === "PRIMARY").optimeDate - m.optimeDate) / 1000 }))
Change a priority safely by editing the config object and reapplying it:
cfg = rs.conf();
cfg.members[2].priority = 0.5;
rs.reconfig(cfg);
Caution
rs.reconfig(cfg, { force: true })is for losing a majority only. Forced reconfiguration can roll back writes and create divergent members. Follow the no-primary runbook before using it.
Sizes and storage
db.stats(1024 * 1024) // database sizes in MB
db.orders.totalSize() // data + indexes, bytes
db.orders.storageSize()
db.orders.totalIndexSize()
db.orders.estimatedDocumentCount()
// Every collection in the current database, biggest first
db.getCollectionNames().map(n => ({ n, mb: (db[n].totalSize() / 1048576).toFixed(0) }))
.sort((a, b) => b.mb - a.mb).slice(0, 15)
// Free space reusable inside a collection (WiredTiger)
db.orders.aggregate([{ $collStats: { storageStats: {} } }])
.next().storageStats.wiredTiger["block-manager"]["file bytes available for reuse"]
compact returns free space to the OS. Run it per collection and check the documentation for your version: since 4.4 it no longer blocks reads and writes on the collection, but it is resource-intensive.
db.runCommand({ compact: "orders" })
Indexes
db.orders.getIndexes()
db.orders.createIndex({ customerId: 1, status: 1, createdAt: -1 }, { name: "ix_cust_status_created" })
db.orders.hideIndex("ix_old") // planner ignores it; test before dropping
db.orders.unhideIndex("ix_old")
db.orders.dropIndex("ix_old")
// Usage since the last restart, per index, on this member only
db.orders.aggregate([{ $indexStats: {} }, { $project: { name: 1, "accesses.ops": 1, "accesses.since": 1 } }])
Tip
$indexStatscounters are per member and reset at restart. Check every data-bearing member, including secondaries that serve reads, and look at a period that covers month-end jobs before dropping an index. Hide it first; unhide is instant.
Slow queries: profiler and explain
db.setProfilingLevel(1, { slowms: 100 }) // log ops slower than 100 ms in this database
db.getProfilingStatus()
db.system.profile.find({ millis: { $gt: 100 } })
.sort({ ts: -1 }).limit(10)
.projection({ op: 1, ns: 1, millis: 1, planSummary: 1, docsExamined: 1, keysExamined: 1, nreturned: 1 })
db.setProfilingLevel(0) // off again; level 2 logs everything - avoid in production
db.orders.find({ customerId: 42, status: "open" }).sort({ createdAt: -1 })
.explain("executionStats")
In explain, compare totalDocsExamined to nReturned and look for COLLSCAN or SORT stages. See the slow-query post for the ESR rule.
Users and roles
use admin
db.createUser({
user: "app_orders",
pwd: passwordPrompt(),
roles: [ { role: "readWrite", db: "orders" } ]
})
db.getUsers()
db.getUser("app_orders", { showPrivileges: true })
db.grantRolesToUser("app_orders", [ { role: "read", db: "reporting" } ])
db.revokeRolesFromUser("app_orders", [ { role: "read", db: "reporting" } ])
db.changeUserPassword("app_orders", passwordPrompt())
db.dropUser("app_orders")
db.runCommand({ connectionStatus: 1 }) // who am I and what roles do I have
On Atlas, create database users through the UI, CLI (atlas dbusers create) or Admin API rather than db.createUser.
Logs and parameters
db.adminCommand({ getLog: "global" }).log.slice(-20) // recent log lines (JSON)
db.setLogLevel(1, "query") // more detail for one component
db.setLogLevel(0, "query") // back to default
db.adminCommand({ logRotate: 1 })
db.adminCommand({ getParameter: 1, slowOpThresholdMs: 1 })
Sharded clusters (run on mongos)
sh.status()
sh.getBalancerState()
sh.isBalancerRunning()
sh.stopBalancer() // before backups or maintenance
sh.startBalancer()
db.orders.getShardDistribution()
Backup and restore with the database tools
# Full replica set member dump with a consistent point via the oplog
mongodump --uri "mongodb://dbadmin@db2:27017/?authSource=admin&readPreference=secondary" \
--oplog --gzip --archive=/backup/rs0_$(date +%F).archive.gz
# One database
mongodump --uri "mongodb://dbadmin@db2:27017/?authSource=admin" --db orders \
--gzip --archive=/backup/orders_$(date +%F).archive.gz
# Test a restore without writing anything
mongorestore --uri "mongodb://dbadmin@restore01:27017/?authSource=admin" \
--gzip --archive=/backup/rs0_2026-10-26.archive.gz --oplogReplay --dryRun
# Restore one database under a new name
mongorestore --uri "mongodb://dbadmin@restore01:27017/?authSource=admin" \
--gzip --archive=/backup/orders_2026-10-26.archive.gz \
--nsInclude "orders.*" --nsFrom "orders.*" --nsTo "orders_restore.*"
Caution
mongorestore --dropdrops each collection before restoring it. Never point it at production by mistake: restore to a separate host or a renamed namespace first, then validate counts.
mongodump suits small and medium databases. For large ones, use filesystem snapshots with journaling on the same volume, Ops Manager / Cloud Manager, or Atlas cloud backups with point-in-time restore.
Upgrades: feature compatibility version
After every member runs the new binary and the cluster is stable:
db.adminCommand({ setFeatureCompatibilityVersion: "8.0", confirm: true }) // confirm required from 7.0
Set FCV only when you no longer need to downgrade the binaries.
Prevent surprises
- Script
rs.printReplicationInfo()and alert when the oplog window falls below your longest expected maintenance or outage (for example 24 hours). - Keep the profiler at level 1 with a sensible
slowmsin non-production and sample production with care. - Store these snippets in a shared
.mongoshrc.jsor snippet file so the whole team runs the same checks. - Test a
mongorestore --dryRunand a real restore to a scratch host every quarter.