Run show users in mongosh to list the accounts on the current database, or db.getUsers() to get the same information as an array you can filter — but both only ever show one database at a time.
That last part is the whole problem. Users in MongoDB belong to the database they were created against, so a show users that returns nothing usually means you are standing in the wrong place, not that there are no users. Auditing an entire cluster takes a different query.
Table of contents
- The two commands
- Why the list looks empty
- Listing users across every database
- Inspecting one user closely
- What the mechanisms field is telling you
- Making the audit routine rather than occasional
- How this fits the rest of the stack
- FAQ
The two commands
show users is the shell helper. It prints the users defined on whatever database you last ran use against.
use appdb
show users
db.getUsers() returns the same set as a document you can work with programmatically.
use appdb
db.getUsers()
Typical output for one account:
{
users: [
{
_id: 'appdb.app_service',
userId: UUID('...'),
user: 'app_service',
db: 'appdb',
roles: [ { role: 'readWrite', db: 'appdb' } ],
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
}
],
ok: 1
}
The _id is the authentication database and username joined by a dot. That prefix is the thing worth reading — it is where the user actually lives, and therefore what has to appear as authSource in any connection string using this account.
Why the list looks empty
Two reasons, and they need different fixes.
You are on the wrong database. show users on test will not show users created against admin. Since mongosh opens on test by default, running show users immediately after connecting is one of the most reliable ways to see nothing on a cluster full of accounts.
You do not have permission. Listing users requires the viewUser privilege on that database, held by userAdmin, userAdminAnyDatabase, and root. Without it, the command fails with an authorization error rather than returning an empty list — read the message rather than assuming the database is empty.
MongoServerError: not authorized on appdb to execute command { usersInfo: 1 }
An empty result and a refused result mean opposite things. Conflating them is how people convince themselves a database has no accounts on it.
Listing users across every database
This is what you actually want during an audit, and neither helper does it. Query the system collection directly from admin.
use admin
db.system.users.find({}, { user: 1, db: 1, roles: 1, _id: 0 }).pretty()
That returns every user on the cluster regardless of which database they belong to. It needs privileges on admin — realistically root or userAdminAnyDatabase.
For a compact report of who holds what:
use admin
db.system.users.find({}, { _id: 0, user: 1, db: 1, roles: 1 }).forEach(u => {
const roles = u.roles.map(r => `${r.role}@${r.db}`).join(", ");
print(`${u.db}.${u.user} -> ${roles}`);
})
admin.backup_agent -> backup@admin, restore@admin
appdb.app_service -> readWrite@appdb
admin.root_user -> root@admin
That third line is the one to go looking for. A root account used by anything other than a human doing break-glass work is the finding, and this is the query that surfaces it.
Inspecting one user closely
db.getUser takes a username and returns a single record.
use appdb
db.getUser("app_service")
By default it shows the roles as granted. To see what those roles actually permit — including everything inherited from built-in roles — ask for the expanded privileges:
db.getUser("app_service", { showPrivileges: true })
This is worth running before you conclude a role is safe. dbOwner looks like one entry in the roles array and expands into readWrite, dbAdmin, and userAdmin — the last of which lets the account create more accounts. Seeing it as a flat list of privileges makes the blast radius concrete in a way the role name does not.
You cannot list passwords. show users and getUsers return the authentication mechanisms but never credential material, by design. If you have lost a password the only path is db.changeUserPassword.
What the mechanisms field is telling you
The mechanisms array shows which SCRAM variants the stored credential supports. Modern MongoDB creates users with both SCRAM-SHA-1 and SCRAM-SHA-256. An account showing only SCRAM-SHA-1 was created under an older version or an older feature compatibility setting, and it is using the weaker hash.
You cannot upgrade the mechanism in place, because the server never stored anything it could re-derive the stronger hash from. Resetting the password regenerates the credential under whatever the current settings allow:
use appdb
db.changeUserPassword("app_service", passwordPrompt())
db.getUser("app_service") // mechanisms should now include SCRAM-SHA-256
Worth checking on any cluster that has been upgraded across major versions. Old accounts quietly keep their old credential format, and nothing surfaces it until someone looks.
Making the audit routine rather than occasional
The reason cluster-wide user listings are a chore is that nothing forces you to look. Accounts accumulate — a contractor’s login, a migration account from a project two years ago, a service that was decommissioned but whose credential still authenticates. None of them announce themselves.
The cross-database query above is short enough to run monthly, and the output is short enough to actually read. Anything holding root or dbOwner, anything whose owner you cannot name, and anything showing only SCRAM-SHA-1 is a candidate for removal or rotation.
A managed database on RunxBuild starts from a narrower position — authentication enforced by default, credentials injected as environment variables rather than living in config files, and a listener that is not exposed to the public internet. That does not audit your application accounts for you, but it means the list you are auditing is shorter and the credentials in it are not also sitting in a repository.
How this fits the rest of the stack
User audits are cheap; the database underneath them is not. Connections, storage, backups, and the services querying it each carry a number, and the sum is the monthly reality. The RunxBuild hosting calculator puts those line items on one page so you can model them before committing.
Useful related references:
- Install MongoDB on Ubuntu: Official Repo, systemd, and 7.0 Setup
- MongoDB push: Append to Arrays Without Growing Documents Forever
- Mongo Express: The Web Admin for MongoDB That Shouldn’t Run in Production
- Databases on RunxBuild
FAQ
How do I show all users in MongoDB?
Run show users in mongosh to list accounts on the current database, or db.getUsers() for the same data as a document. Both are scoped to the database you last ran use against, so neither shows the whole cluster.
Why does show users return nothing when users exist?
Usually you are on the wrong database — mongosh opens on test by default, and users belong to the database they were created against. The other cause is missing the viewUser privilege, which produces an authorization error rather than an empty list. Read the message before concluding the database is empty.
How do I list users across all databases?
Switch to admin and query the system collection directly: db.system.users.find({}, { user: 1, db: 1, roles: 1, _id: 0 }). This returns every account on the cluster and requires root or userAdminAnyDatabase.
Can I see a user’s password in MongoDB?
No. The user listing commands return authentication mechanisms but never credential material. If a password is lost, reset it with db.changeUserPassword against the user’s authentication database.
What does the mechanisms field mean?
It lists the SCRAM variants the stored credential supports. An account showing only SCRAM-SHA-1 was created under an older version and uses the weaker hash. You cannot upgrade it in place — reset the password to regenerate the credential under current settings.