In my earlier post, “How to set up MongoDB 8 master–slave on Debian 12 (Bookworm)”, we ended with a working replica set and basic authentication.
Now comes the daily-ops reality: your app user needs to write documents, but your ops tasks also need to tweak schema rules, adjust TTLs, rename collections, or rebuild indexes during a migration.
This is the point where many teams discover that readWrite
alone isn’t enough and why we deliberately grant a second role, dbAdmin
, to the same database user.
What readWrite
actually covers
readWrite
is a data-plane role. It grants the ability to read and modify data in non-system collections of the target database.
Practically, that includes common CRUD operations and routine developer needs like creating collections and (yes) building indexes things your application does as part of normal operation.
The takeaway: readWrite
is exactly right for application traffic and developer CRUD.
It intentionally does not include higher-risk administration actions that can reshape metadata or the database itself.
What dbAdmin
adds and why it matters in production
dbAdmin
is an administration role scoped to a single database. It allows schema-level and metadata operations: modifying collection options, running validation, profiling, compacting, renaming, dropping, and managing indexes beyond simple creation.
Concretely, dbAdmin
adds privileges like collMod
, renameCollectionSameDB
, dropCollection
, dropIndex
, reIndex
, validate
, createSearchIndexes
, and profiling controls the kinds of operations you need during controlled maintenance windows, migrations, and incident response.
One especially relevant example is collMod
. If you want to tighten document validation, flip a collection to timeseries
, adjust a TTL, or change index expireAfterSeconds via collMod
, the server requires the collMod
privilege supplied by dbAdmin
.
Typical scenarios where readWrite
is not enough
When you hit any of these, you’ll appreciate having dbAdmin
on the same user (or a separate ops-only user):
- Tightening schema validation or adjusting TTLs (
collMod
). - Re-indexing after a migration (
reIndex
) or dropping obsolete indexes (dropIndex
). - Renaming a collection for a blue/green rollout (
renameCollectionSameDB
). - Running deeper integrity checks and stats (
validate
,dbStats
,collStats
).
But doesn’t dbOwner
already include both?
Yes, dbOwner
on a database bundles readWrite
, dbAdmin
, and userAdmin
. It’s convenient, but it’s also far more authority than most application users should have. Sticking to readWrite
+ dbAdmin
is a tighter, least-privilege stance for day-to-day ops without drifting into user/role management powers.
Forward-looking, keep your roles split, and customize when needed
Granting two roles to the same user is intentional separation of concerns, the app keeps its data-plane capabilities (readWrite
), while ops tasks can be performed safely and auditable via dbAdmin
.
For teams with stricter controls, you can also create a custom role that inherits readWrite
and only the admin actions you actually need (for example, just collMod
), instead of the full dbAdmin
surface.
Practical examples (you can run these in mongosh)
Create a user that has both roles on a single database:
0 1 2 3 4 5 6 7 8 9 10 |
use your_database_name db.createUser({ user: "thisisuser", pwd: "thisisupersecretpass", roles: [ { role: "readWrite", db: "your_database_name" }, { role: "dbAdmin", db: "your_database_name" } ] }) |
Tighten a TTL on an index with collMod
(requires dbAdmin
):
0 1 2 3 4 5 |
db.runCommand({ collMod: "events", index: { name: "expires_at_1", expireAfterSeconds: 3600 } // 1 hour }) |
Rename a collection during a deploy (requires dbAdmin
):
0 1 2 3 4 5 |
db.adminCommand({ renameCollection: "your_database_name.events", to: "your_database_name.events_2025q3" }) |
Create a custom “minimal admin” role that keeps readWrite
but only adds collMod
:
0 1 2 3 4 5 6 7 8 9 10 11 12 |
use your_database_name db.createRole({ role: "rw_with_collmod_only", privileges: [ { resource: { db: "your_database_name", collection: "" }, actions: [ "collMod" ] } ], roles: [ { role: "readWrite", db: "your_database_name" } ] }) // Optionally grant it: db.grantRolesToUser("thisisuser", [ { role: "rw_with_collmod_only", db: "your_database_name" } ]) |
This pattern gives you just-enough power for schema tuning without unlocking the entire dbAdmin
toolbox.
Closing thought
On fresh deployments it’s tempting to throw dbOwner
at a user and move on. Resist that. For long-lived systems, especially on Debian 12 with MongoDB 8, keep data plane (readWrite
) and admin plane (dbAdmin
) clearly separated, and fall back to a custom role when you can narrow privileges further.
That balance keeps your app productive while preserving the guardrails you’ll need when future migrations, validations, and index clean-ups arrive.