What mongod needs from the hardware
Memory decides most of it. The production notes say MongoDB uses two caches at once: the WiredTiger internal cache, which holds collection data uncompressed, and the operating system's filesystem cache, which holds the compressed on-disk files and takes whatever memory nothing else is using. The default internal cache is the larger of 50% of (RAM minus 1 GB) or 0.256 GB, and the notes advise against raising it above that default. They also assume one mongod per machine.
| Machine RAM | Default WiredTiger cache | Left for file cache, OS and connections |
|---|---|---|
| 32 GB | 15.5 GB | 16.5 GB |
| 64 GB | 31.5 GB | 32.5 GB |
| 128 GB | 63.5 GB | 64.5 GB |
The figures are our arithmetic from the documented formula. MongoDB gives no fixed ratio of memory to data size; what matters is whether your working set, the documents and indexes your queries touch every day, fits in that cache.
- CPU: at least two real cores (or one multi-core physical CPU) per mongod. On x86_64 MongoDB 5.0 and later needs AVX, and the notes set the floor at a Haswell or later Intel Core-class processor or a Bulldozer or later AMD one.
- Storage: SSD where it is economical, XFS on Linux (the notes call it strongly recommended for data-bearing nodes with WiredTiger), the
noneI/O scheduler for NVMe or SSD on physical servers, andnoatimeon the data volume. - RAID: the notes name RAID 10 for optimal storage performance and say RAID 5 and RAID 6 do not typically provide enough.
Which machine to pick
For one node or a small replica set, an NVMe dedicated server is the direct fit: a whole HPE Moonshot cartridge with its NVMe SSD on PCIe, root, and a 1 Gbps unmetered port. The NVMe lines are Intel Xeon D-1500 (D-1548 with 8 cores, D-1587 with 16, 32 GB to 128 GB DDR4), Intel Xeon E3-1585L v5 (4 cores, 32 GB to 64 GB) and Intel Xeon E-2286M (8 cores, 32 GB to 64 GB, Bucharest only). All three are Intel generations after Haswell with AVX, and the first block below checks for AVX before it installs anything.
- Largest working set: Xeon D-1587 with 128 GB. By the formula above that is a 63.5 GB default cache.
- Two drives in RAID 1: each NVMe cartridge takes one drive or two mirrored in software, 240 GB to 3.84 TB per drive. Usable space stays the size of one drive, and a mirror covers a failed drive, not a dropped collection.
- Console: the E3-1585L v5 line is the m710x cartridge, which has its own browser console. Moonshot cartridges other than the m510 and m710x have power control only.
A racked server is live about 30 minutes after the order is approved; a build to order takes 4 to 24 hours.
Install with authorization on from the start
The block follows the Ubuntu steps from the MongoDB install page for 9.0 (it supports the 22.04, 24.04 and 26.04 LTS releases; for 22.04 or 26.04 replace both noble strings with jammy or resolute). Our changes: the key is downloaded to a file and dearmored with --batch --yes so a re-run cannot stop at an overwrite prompt; ufw comes on with SSH only and IPv6 rules enabled; security.authorization is appended before the first start; vm.swappiness goes in /etc/sysctl.d rather than /etc/sysctl.conf. The block stops if the packaged config does not show bindIp: 127.0.0.1.
# Ubuntu 24.04 (noble), as a user with sudo. Safe to re-run.
grep -q '^VERSION_CODENAME=noble' /etc/os-release && grep -qw avx /proc/cpuinfo &&
sudo apt-get install -y gnupg curl &&
curl -fsSL https://pgp.mongodb.com/server-9.asc -o /tmp/server-9.asc &&
sudo gpg --batch --yes --dearmor -o /usr/share/keyrings/mongodb-server-9.gpg /tmp/server-9.asc &&
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-9.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/9.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-9.0.list &&
sudo apt-get update && sudo apt-get install -y mongodb-org &&
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw &&
sudo ufw allow OpenSSH && sudo ufw --force enable &&
grep -q '^ bindIp: 127.0.0.1$' /etc/mongod.conf &&
{ grep -q '^security:' /etc/mongod.conf || printf 'security:\n authorization: enabled\n' | sudo tee -a /etc/mongod.conf >/dev/null; } &&
grep -q '^ authorization: enabled$' /etc/mongod.conf &&
echo 'vm.swappiness = 1' | sudo tee /etc/sysctl.d/90-mongodb.conf && sudo sysctl --system >/dev/null &&
sudo systemctl enable --now mongod && sleep 5 && systemctl is-active --quiet mongod &&
echo "mongod is up on 127.0.0.1 with authorization on" ||
echo "STOP - read the output above and /var/log/mongodb/mongod.log"
With access control on and no users yet, the localhost exception lets a connection from the machine itself create exactly one first user in the admin database. Start mongosh, then enter the remaining lines one at a time: each passwordPrompt() waits for a password, and a pasted line after it would be read as one.
mongosh
use admin
db.createUser({ user: "admin", pwd: passwordPrompt(), roles: [ { role: "userAdminAnyDatabase", db: "admin" }, { role: "readWriteAnyDatabase", db: "admin" } ] })
db.auth("admin", passwordPrompt())
db.createUser({ user: "backup", pwd: passwordPrompt(), roles: [ "backup", "restore" ] })
db.getSiblingDB("app").createUser({ user: "appuser", pwd: passwordPrompt(), roles: [ { role: "readWrite", db: "app" } ] })
The first user follows the SCRAM tutorial (userAdminAnyDatabase plus readWriteAnyDatabase). The backup user carries the built-in backup and restore roles that mongodump and mongorestore need; appuser can only read and write the app database. Your application then connects with mongodb://appuser:PASSWORD@127.0.0.1:27017/app?authSource=app.
Production notes worth applying before data arrives
- Transparent Hugepages and TCMalloc. From 8.0, MongoDB's TCMalloc page says THP must be enabled before mongod starts, through a oneshot systemd unit (
enable-transparent-huge-pages.service, orderedBefore=mongod.service). The unit sets THPenabledtoalways,defragtodefer+madviseand khugepagedmax_ptes_noneto 0, and it also setsvm.overcommit_memoryto 1. Copy it from that page, thensudo systemctl daemon-reload && sudo systemctl enable --now enable-transparent-huge-pages && sudo systemctl restart mongod(enable alone would wait for the next boot); confirm the THPenabledfile under/sysreads[always], then check in mongosh thatdb.serverStatus().tcmalloc.usingPerCPUCachesis true. If it is false, the page says to disable glibc rseq: addEnvironment=GLIBC_TUNABLES=glibc.pthread.rseq=0under[Service]withsudo systemctl edit mongod, then restart. - File system. Check it with
findmnt -no FSTYPE --target /var/lib/mongodb. If it prints ext4, plan an XFS volume for the data directory before you load anything. - Open files.
systemctl show mongod -p LimitNOFILEshould be 64000 or more; below 64000 mongod logs a startup warning. - NUMA. The Xeon D line is single socket. On a multi-socket machine the notes ask for NUMA off in the BIOS, or zone reclaim off plus
numactl --interleave=allin the unit, and nonumad. - Clock and readahead. Run an NTP-compatible daemon such as systemd-timesyncd; keep readahead between 8 and 32.
Port 27017 stays off the internet
The install page warns that before you bind to a publicly reachable address you must secure the deployment, and the security checklist asks for access control, TLS on every connection and a firewall that admits only trusted clients. For a GUI such as Compass, forward the port over SSH under your own account instead of opening anything: ssh -N -L 27017:127.0.0.1:27017 you@server.
If the application runs on another machine, three changes go together: add the server's own address to net.bindIp next to 127.0.0.1, configure net.tls, and admit only the client with sudo ufw allow from 203.0.113.10 to any port 27017 proto tcp. mongod runs on the host, not in Docker, so ufw filters it on IPv4 and IPv6. Never ufw allow 27017.
Backups with mongodump, and the restore
The backup methods page calls mongodump and mongorestore tools for small deployments, and the backup-tools tutorial adds that they force the database to read all data through memory, evicting frequently used data and slowing the regular workload while they run. Make the password file empty and private with sudo install -m 600 /dev/null /root/.mongodump.yaml, then open it with sudo nano /root/.mongodump.yaml and write one YAML line: the key password, a colon, and the password of the backup user in double quotes (pick one without a double quote or backslash in it). mongodump's --config option is the documented way to keep the password off the command line and out of ps.
# As root, after creating /root/.mongodump.yaml (see above)
install -d -m 700 /var/backups/mongodb && tee /usr/local/sbin/mongo-backup.sh >/dev/null <<'EOF'
set -euo pipefail
umask 077
d=/var/backups/mongodb
f="$d/mongo-$(date +%F-%H%M%S).archive.gz"
mongodump --config=/root/.mongodump.yaml --username=backup --authenticationDatabase=admin \
--archive="$f" --gzip --quiet
gzip -t "$f"
tar -czf "$f.etc.tgz" -C / etc/mongod.conf
find "$d" -name 'mongo-*' -mtime +7 -delete
echo "backup ok $f"
EOF
printf '15 3 * * * root bash /usr/local/sbin/mongo-backup.sh >>/var/log/mongo-backup.log 2>&1\n' > /etc/cron.d/mongo-backup &&
bash /usr/local/sbin/mongo-backup.sh || echo "STOP - backup FAILED, see the output above"
The script stops at the first error, checks the gzip stream, keeps a copy of mongod.conf, and prunes archives older than seven days only after a good one exists. Copy /var/backups/mongodb to another host with plain rsync -a, never --delete. A standalone has no oplog, so these dumps are not point-in-time; --oplog needs a replica set.
- Before restoring over live data, stop the application so nothing writes, then run the backup script yourself (
sudo bash /usr/local/sbin/mongo-backup.sh) and confirm it printsbackup okwith a new file name. That archive is your rollback, and the seven-day prune will not touch it. - Restore as root:
gzip -t FILE && mongorestore --config=/root/.mongodump.yaml --username=backup --authenticationDatabase=admin --archive=FILE --gzip --drop --nsExclude='admin.*'.--dropreplaces only the collections in the archive. Excludingadminmatters: with--drop, a restore that includes it replaces every user with the users in the dump. - If the result is wrong, keep the application stopped and run the same command against the archive from step 1.
--droponly replaces collections that archive contains, so drop by hand any collection the first restore added that the step-1 archive does not have.
Filesystem snapshots are the other documented method. MongoDB's LVM tutorial asks for the data files, the journal and the configuration on one logical volume that holds nothing else; if the journal sits on another volume, flush and lock writes with db.fsyncLock() for the length of the snapshot. LVM snapshots are full images with no incremental option, and a snapshot on the same drives only counts as a backup once it is copied off the machine. Decide on LVM for /var/lib/mongodb before you load data.
Replica sets and version upgrades
The replica set tutorial recommends three members, an odd number, each on its own machine, addressed by DNS hostnames rather than IPs. For members proving their identity to each other, the keyfile tutorial limits keyfiles to testing and development and points production at X.509 certificates. We sell no private VLAN, so traffic between members crosses the public port: turn on TLS (net.tls) on every member, and add ufw rules that admit each member's address and nothing else. Initiate the set from one member over localhost, then add users once a primary is elected.
Upgrades go one major release at a time. MongoDB 9.0 accepts an upgrade from 8.0 or 8.3 only, with featureCompatibilityVersion already at the old version; from 7.0 you go to 8.0 first. With the apt install, add the new repository and upgrade the packages; a replica set upgrades secondaries one at a time, then steps the primary down. Set the new feature compatibility version only after a burn-in period, because it complicates a downgrade.
The SSPL, read plainly
MongoDB's licensing page puts the Database Server under its Server Side Public License v1.0 for every version released after 16 October 2018, and under the GNU AGPL v3.0 before that; the client libraries MongoDB supports are Apache 2.0. Community Server costs nothing to download or run, and MongoDB also offers commercial licences.
Our general reading, not legal advice. The SSPL lets you run the unmodified program, subject to section 13. Distributing copies, modified or not, means passing on the licence and the corresponding source. Section 13: if you make the functionality of the program, or a modified version, available to third parties as a service, you must release the Service Source Code, meaning the program plus the management, interface, API, automation, monitoring, backup, storage and hosting software behind that service, under the SSPL. Whether your product counts is a question for your lawyer.
When this is the wrong machine
- Data past 3.84 TB, or RAID 10. NVMe cartridges stop at two drives in RAID 1. The production notes prefer RAID 10, which on our side means a rack or blade server with a hardware controller: see enterprise dedicated servers.
- Enterprise features. The checklist lists LDAP and Kerberos authentication and the auditing facility as Enterprise or managed-service features; Community has SCRAM and X.509.
- Search and vector search. Community does not include MongoDB Search by default. Both MongoDB Search and Vector Search come from a separate process, mongot, which needs MongoDB 8.3 or later running as an initiated replica set with keyfile access control and a user holding the
searchCoordinatorrole. The standalone set up on this page is not enough. - A relational schema. For tables and joins, read the PostgreSQL on bare metal or the MariaDB setup notes. For a pure cache next to MongoDB, see Redis on bare metal.
Questions
Is self hosted MongoDB free?
Community Server has no licence fee: you download it from MongoDB's repository and run it. It is licensed under the SSPL v1.0, which puts conditions on distributing it and on making its functionality available to third parties as a service; whether your product falls under that clause is a legal question. You pay only for the machine.
How much memory should the server have?
Enough that the working set, the documents and indexes queried every day, fits in the WiredTiger cache. That cache defaults to half of (RAM minus 1 GB), and the rest serves the filesystem cache, so a 64 GB machine gives about 31.5 GB to WiredTiger by default. MongoDB publishes no fixed ratio of RAM to data size.
Do I need a replica set, or is one node enough?
One node with tested backups works for development and for data you can restore from a dump. MongoDB recommends replica sets for production: three members on separate machines survive the loss of one, and the oplog allows point-in-time dumps. Plan three machines and TLS between them from the start.