Logo

MongoDB: install and securing

MongoDB: install and securing

Deploy MongoDB in production: authentication, replica set, encryption, backups. The most popular document-oriented NoSQL database. Very performant for flexible workloads, but requires special security attention.

Introduction

MongoDB is a JSON-document NoSQL database:

  • Flexible schema (each document can differ)
  • Secondary indexes, aggregation pipeline
  • Replica sets for HA
  • Sharding for horizontal scaling
  • Drivers for all languages
  • Heavily used in the Node.js world (Mongoose)

⚠️ By default, MongoDB listens on all interfaces without auth. Unsecured internet-facing MongoDBs are a classic ransomware target. Always enable auth and restrict binding.

Prerequisites

  • Linux VPS Debian 12 / Ubuntu 24.04
  • 2 vCPU, 4 GB RAM
  • Root access

Step 1: Installation

sudo apt update
sudo apt install -y gnupg curl
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-7.0.gpg

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
sudo systemctl enable --now mongod
sudo systemctl status mongod
mongosh --eval "db.runCommand({ ping: 1 })"

Step 2: Secure binding

/etc/mongod.conf:

net:
  port: 27017
  bindIp: 127.0.0.1

For LAN access:

net:
  port: 27017
  bindIp: 127.0.0.1,10.0.0.5

⚠️ Never 0.0.0.0 unless strict firewall in front.

Step 3: Enable authentication

mongosh
use admin

db.createUser({
  user: "admin",
  pwd: "very-strong-pass",
  roles: [{ role: "userAdminAnyDatabase", db: "admin" },
          { role: "readWriteAnyDatabase", db: "admin" }]
})

exit

/etc/mongod.conf:

security:
  authorization: enabled
sudo systemctl restart mongod
mongosh -u admin -p --authenticationDatabase admin

Step 4: Application user

use myapp

db.createUser({
  user: "myapp_user",
  pwd: "strong-app-pass",
  roles: [{ role: "readWrite", db: "myapp" }]
})

Minimum privilege.

Step 5: First insertion

use myapp

db.users.insertOne({
  name: "Alice",
  email: "[email protected]",
  age: 30,
  tags: ["dev", "admin"]
})

db.users.find({ age: { $gte: 25 } })

Step 6: Indexes

db.users.createIndex({ email: 1 }, { unique: true })
db.users.createIndex({ age: 1, name: 1 })
db.users.getIndexes()

Execution plan:

db.users.find({ email: "[email protected]" }).explain("executionStats")

Look for stage: "IXSCAN" (good) vs COLLSCAN (slow).

Step 7: Replica Set for HA

3-node replica set:

On each node, /etc/mongod.conf:

replication:
  replSetName: rs0

net:
  bindIp: 127.0.0.1,10.0.0.X

security:
  authorization: enabled
  keyFile: /etc/mongodb-keyfile

Generate keyFile (one, copied to all 3 nodes):

openssl rand -base64 756 | sudo tee /etc/mongodb-keyfile
sudo chmod 400 /etc/mongodb-keyfile
sudo chown mongodb:mongodb /etc/mongodb-keyfile

On primary node:

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "10.0.0.1:27017" },
    { _id: 1, host: "10.0.0.2:27017" },
    { _id: 2, host: "10.0.0.3:27017" }
  ]
})

rs.status()

Primary auto-elected.

Step 8: Application connection string

mongodb://myapp_user:[email protected],10.0.0.2,10.0.0.3:27017/myapp?replicaSet=rs0&authSource=myapp

Driver handles failover automatically.

Step 9: Backup with mongodump

mongodump --uri="mongodb://admin:[email protected]:27017" --out=/var/backups/mongo-$(date +%F)

Compressed archive:

mongodump --uri="..." --archive | gzip > /var/backups/mongo-$(date +%F).archive.gz

Restore:

gunzip -c /var/backups/mongo-2026-05-17.archive.gz | mongorestore --uri="..." --archive

Step 10: Backup to S3

#!/bin/bash
DATE=$(date +%F)
mongodump --uri="..." --archive | gzip | aws s3 cp - s3://my-backups/mongo-$DATE.archive.gz
0 2 * * * /root/backup-mongo.sh

Step 11: Monitoring

mongosh --eval "db.serverStatus()"
mongosh --eval "db.stats()"

mongostat --uri="..."
mongotop --uri="..."

For Prometheus, use mongodb_exporter:

docker run -d -p 9216:9216 percona/mongodb_exporter:0.40 \
    --mongodb.uri=mongodb://admin:pass@host:27017

Step 12: Performance tuning

WiredTiger cache

storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 2     # 50% available RAM

Profile slow queries

db.setProfilingLevel(1, { slowms: 100 })
db.system.profile.find().sort({ ts: -1 }).limit(10)

No $where

$where disables indexes. Use aggregation pipeline instead.

Troubleshooting

"Authentication failed"

Check authSource in connection string. User in adminauthSource=admin. User in myappauthSource=myapp.

"exceeded connection limit"

db.serverStatus().connections

Increase net.maxIncomingConnections or reduce client connections (pool).

Replica set: "not master"

You're on a secondary. To read:

rs.secondaryOk()
db.users.find()

For writes, connect to primary.

Disk full

sudo du -sh /var/lib/mongodb

Compact:

db.runCommand({ compact: "users" })

Useful commands

sudo systemctl status mongod
sudo systemctl restart mongod
sudo tail -f /var/log/mongodb/mongod.log

mongostat --uri="..."
mongotop --uri="..."

mongosh -u admin -p --authenticationDatabase admin

mongodump --uri="..." --archive | gzip > backup.archive.gz
mongorestore --uri="..." --archive < backup.archive

mongosh --eval "rs.status()"
mongosh --eval "rs.stepDown()"

mongosh --eval "db.setProfilingLevel(1, { slowms: 100 })"

Conclusion

MongoDB is powerful for flexible workloads:

  • Dynamic schema
  • Horizontal scaling (sharding)
  • HA via replica sets
  • Rich aggregation framework

Limits:

  • ACID transactions less mature than SQL
  • High memory consumption
  • Limited joins (vs SQL)

Going further:

  • Configure sharding for >1 TB databases
  • Explore MongoDB Atlas (managed)
  • For analytics, consider MongoDB Charts or export to BigQuery

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members