--- name: mongodb description: Administer MongoDB databases. Configure replica sets, sharding, and backups. Use when managing MongoDB deployments. category: devops risk: critical source: https://github.com/BagelHole/DevOps-Security-Agent-Skills source_repo: BagelHole/DevOps-Security-Agent-Skills source_type: community date_added: '2026-09-20' license: MIT license_source: https://github.com/BagelHole/DevOps-Security-Agent-Skills/blob/main/LICENSE compatibility: Requires the relevant OS/platform tooling and privileged access where noted. Docs-only; helper scripts and templates not bundled. metadata: author: devops-skills version: '1.0' --- # MongoDB Administer, optimize, and secure MongoDB NoSQL databases in development and production environments. ## When to Use - You need a document-oriented database with flexible schemas. - Your data is semi-structured or heavily nested (JSON-like documents). - You need horizontal scaling through sharding. - Your application benefits from rich querying and aggregation pipelines. ## Prerequisites - Linux server (Debian/Ubuntu or RHEL-based) or Docker. - Root or sudo access for package installation. - Select a MongoDB release with verified current support and tool/OS compatibility; 6.x/7.x examples below are historical baselines. ## Scope and data safety Use packages/repository codename matching the host OS and verify signing-key fingerprints independently. Bootstrap users only on a local/private isolated listener; restrict bindIp to the required private addresses, enable TLS and never expose unauthenticated initialization. Passwords/URIs below are placeholders: use prompts or protected tool configuration rather than real secrets in process arguments or committed Compose. The Compose lab runs all members on one host, requires correct keyfile ownership inside containers and resolvable advertised member names; the host connection URL alone does not make mongo1/2/3 resolvable externally. Dumps need a consistency strategy for concurrent writes; --drop overwrites collections and needs a tested restore into isolation. TTL indexes delete data asynchronously. Profiler/diagnostic output can contain personal records; restrict access and redact. Server options and support status must be checked for the chosen version. ## Installation and Setup ```bash # Debian / Ubuntu — MongoDB 7 curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \ sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] \ https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \ sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list sudo apt update sudo apt install -y mongodb-org # Start and enable sudo systemctl enable --now mongod # Verify mongosh --eval "db.version()" ``` ## Initial User Setup ```javascript // Connect without auth first // mongosh use admin // Create admin user db.createUser({ user: "admin", pwd: "strong_admin_password", roles: [ { role: "userAdminAnyDatabase", db: "admin" }, { role: "readWriteAnyDatabase", db: "admin" }, { role: "clusterAdmin", db: "admin" } ] }) // Create an application-scoped user use mydb db.createUser({ user: "myapp", pwd: "strong_app_password", roles: [{ role: "readWrite", db: "mydb" }] }) ``` Enable authentication in `/etc/mongod.conf`: ```yaml security: authorization: enabled ``` ```bash sudo systemctl restart mongod # Now connect with credentials mongosh -u myapp -p strong_app_password --authenticationDatabase mydb ``` ## mongosh Commands Reference ```javascript // Show databases and collections show dbs use mydb show collections // Insert documents db.users.insertOne({ name: "Alice", email: "alice@example.com", age: 30 }) db.users.insertMany([ { name: "Bob", email: "bob@example.com", age: 25 }, { name: "Carol", email: "carol@example.com", age: 35 } ]) // Query documents db.users.find({ age: { $gte: 25 } }).sort({ name: 1 }).limit(10) db.users.findOne({ email: "alice@example.com" }) db.users.countDocuments({ age: { $gte: 30 } }) // Update db.users.updateOne( { email: "alice@example.com" }, { $set: { age: 31 }, $currentDate: { updatedAt: true } } ) db.users.updateMany( { age: { $lt: 30 } }, { $set: { tier: "junior" } } ) // Delete db.users.deleteOne({ email: "bob@example.com" }) db.users.deleteMany({ tier: "junior" }) ``` ## Indexing ```javascript // Single-field index db.users.createIndex({ email: 1 }, { unique: true }) // Compound index db.orders.createIndex({ userId: 1, createdAt: -1 }) // Text index for search db.articles.createIndex({ title: "text", body: "text" }) db.articles.find({ $text: { $search: "mongodb scaling" } }) // TTL index — auto-delete documents after 30 days db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 2592000 }) // List indexes db.users.getIndexes() // Drop an index db.users.dropIndex("email_1") // Explain a query to verify index usage db.orders.find({ userId: 42 }).explain("executionStats") ``` ## Aggregation Pipeline Examples ```javascript // Revenue per status db.orders.aggregate([ { $group: { _id: "$status", totalRevenue: { $sum: "$total" }, count: { $sum: 1 } }}, { $sort: { totalRevenue: -1 } } ]) // Top 5 customers by order value (with a join) db.orders.aggregate([ { $group: { _id: "$userId", spent: { $sum: "$total" }, orderCount: { $sum: 1 } }}, { $sort: { spent: -1 } }, { $limit: 5 }, { $lookup: { from: "users", localField: "_id", foreignField: "_id", as: "user" }}, { $unwind: "$user" }, { $project: { _id: 0, name: "$user.name", email: "$user.email", spent: 1, orderCount: 1 }} ]) // Daily signup trend db.users.aggregate([ { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } }, signups: { $sum: 1 } }}, { $sort: { _id: 1 } }, { $limit: 30 } ]) ``` ## Replica Set Setup A single-member replica set is possible for development; production availability generally needs three appropriately distributed voting data-bearing members. An arbiter holds no data and is not an equivalent redundant copy. ### Configuration File for Each Member ```yaml # /etc/mongod.conf (adjust port and dbPath per member) storage: dbPath: /var/lib/mongodb net: port: 27017 bindIp: 0.0.0.0 replication: replSetName: rs0 security: authorization: enabled keyFile: /etc/mongodb-keyfile ``` ```bash # Generate a shared keyfile for internal auth openssl rand -base64 756 > /etc/mongodb-keyfile chmod 400 /etc/mongodb-keyfile chown mongodb:mongodb /etc/mongodb-keyfile # Copy this file to all replica set members ``` ### Initialize the Replica Set ```javascript // Connect to the first member // mongosh --port 27017 rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "mongo1:27017", priority: 2 }, { _id: 1, host: "mongo2:27017", priority: 1 }, { _id: 2, host: "mongo3:27017", priority: 1 } ] }) // Check status rs.status() // View replication lag per member rs.printReplicationInfo() rs.printSecondaryReplicationInfo() ``` ## Backup and Restore ```bash # Full dump of all databases mongodump --uri="mongodb://admin:secret@localhost:27017" --out=/backups/full_$(date +%F) # Single database mongodump --uri="mongodb://myapp:secret@localhost:27017/mydb" --out=/backups/mydb_$(date +%F) # Compressed dump mongodump --uri="mongodb://admin:secret@localhost:27017" --gzip --out=/backups/gz_$(date +%F) # Restore all databases mongorestore --uri="mongodb://admin:secret@localhost:27017" /backups/full_2025-01-15/ # Restore a single database, dropping existing data first mongorestore --uri="mongodb://admin:secret@localhost:27017" \ --drop --db mydb /backups/mydb_2025-01-15/mydb/ # Restore compressed dump mongorestore --uri="mongodb://admin:secret@localhost:27017" --gzip /backups/gz_2025-01-15/ ``` ## Docker Compose Setup ```yaml # docker-compose.yml version: "3.9" services: mongo1: image: mongo:7 restart: unless-stopped ports: - "127.0.0.1:27017:27017" environment: MONGO_INITDB_ROOT_USERNAME: admin MONGO_INITDB_ROOT_PASSWORD: secret volumes: - mongo1_data:/data/db - ./mongo-keyfile:/etc/mongodb-keyfile:ro command: > mongod --replSet rs0 --keyFile /etc/mongodb-keyfile --bind_ip_all healthcheck: test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"] interval: 10s timeout: 5s retries: 5 mongo2: image: mongo:7 restart: unless-stopped volumes: - mongo2_data:/data/db - ./mongo-keyfile:/etc/mongodb-keyfile:ro command: > mongod --replSet rs0 --keyFile /etc/mongodb-keyfile --bind_ip_all mongo3: image: mongo:7 restart: unless-stopped volumes: - mongo3_data:/data/db - ./mongo-keyfile:/etc/mongodb-keyfile:ro command: > mongod --replSet rs0 --keyFile /etc/mongodb-keyfile --bind_ip_all mongo-init: image: mongo:7 restart: "no" depends_on: mongo1: condition: service_healthy entrypoint: > mongosh --host mongo1 -u admin -p secret --authenticationDatabase admin --eval ' rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "mongo1:27017", priority: 2 }, { _id: 1, host: "mongo2:27017", priority: 1 }, { _id: 2, host: "mongo3:27017", priority: 1 } ] }) ' volumes: mongo1_data: mongo2_data: mongo3_data: ``` ```bash # Generate keyfile before starting openssl rand -base64 756 > mongo-keyfile chmod 400 mongo-keyfile docker compose up -d # Connect mongosh "mongodb://admin:secret@127.0.0.1:27017/?replicaSet=rs0&authSource=admin" ``` ## Monitoring Queries ```javascript // Server status summary db.serverStatus().connections db.serverStatus().opcounters // Current operations (look for long-running queries) db.currentOp({ secs_running: { $gte: 5 } }) // Collection stats db.orders.stats() // Index sizes db.orders.stats().indexSizes // Profiler — log slow queries (> 100ms) db.setProfilingLevel(1, { slowms: 100 }) db.system.profile.find().sort({ ts: -1 }).limit(5) // Replica set lag rs.printSecondaryReplicationInfo() ``` ## Configuration Tuning ```yaml # /etc/mongod.conf — production recommendations storage: dbPath: /var/lib/mongodb wiredTiger: engineConfig: cacheSizeGB: 4 # ~50% of RAM, leave rest for OS cache collectionConfig: blockCompressor: snappy net: port: 27017 bindIp: 0.0.0.0 maxIncomingConnections: 500 operationProfiling: mode: slowOp slowOpThresholdMs: 100 ``` ## Troubleshooting | Symptom | Likely Cause | Fix | |---|---|---| | `COLLSCAN` in explain output | Missing index on queried field | Create an appropriate index | | Replica member stuck in `RECOVERING` | Oplog window exceeded | Diagnose lag/connectivity and follow a verified member-specific resync procedure with healthy quorum and backups; do not erase data as the first step | | `too many open files` | OS file descriptor limit too low | Set `ulimit -n 65535` in service file | | High memory usage | WiredTiger cache too large | Reduce `cacheSizeGB` in config | | Slow aggregation pipelines | No index on `$match` stage fields | Add index; place `$match` as early as possible in pipeline | | Authentication failure | Wrong `authenticationDatabase` | Specify `--authenticationDatabase admin` for admin users | ## Related Skills - redis (`redis`) - Caching layer in front of MongoDB - database-backups (`database-backups`) - Automated backup strategies - postgresql (`postgresql-devsec`) - Alternative relational database ## Limitations - Infrastructure commands can disrupt services: confirm target host/scope and have backups/snapshots before mutating state. - Docs-only import: upstream scripts and templates not bundled.