Why self-host MongoDB on a VPS
MongoDB Atlas bills per cluster, per GB, and per operation, and restricts certain features outside the premium tiers. By deploying MongoDB on your VPS, you set up a full replica set for high availability, you enable the profiler and the indexes you want, and you keep your documents on infrastructure you control. It's a good fit for a Node.js/Express application, a logging backend, a product catalog with a variable structure, or any system where the schema evolves quickly. With WiredTiger as the storage engine and proper RAM sizing for the working set, a VPS delivers performance entirely comparable to a managed instance, at a fraction of the cost.
The concrete benefits of a self-hosted MongoDB
- A full replica set (primary + secondaries) across multiple VPSs for high availability.
- Indexes and profiler without restriction: compound, partial, TTL, geospatial, full-text.
- Control of the WiredTiger cache to keep the working set in memory.
- No billing per operation or per cluster size: a predictable VPS plan.
- Backups via
mongodumpor volume snapshots, depending on your data volume. - Sovereignty and full access to the server's logs, metrics, and configuration.
Hardware and software prerequisites
MongoDB leverages RAM for its WiredTiger cache: aim to be able to fit your working set (the frequently accessed data and indexes) in it. For a development project or a small app, 2 vCPU and 4 GB of RAM with 40 GB of SSD are suitable. For production with a replica set, plan for 4 vCPU and 8 GB of RAM per node, and ideally three separate VPSs (one primary, two secondaries, or an arbiter for the quorum). On the software side: Ubuntu 22.04/24.04 LTS, Docker and Compose v2, a persistent volume for /data/db, and an XFS file system recommended by MongoDB for WiredTiger. A domain is useful (e.g., mongo.myapp.com) if you expose access via TLS.
Deploying MongoDB with Docker in production
Prepare the VPS
Install Docker, disable Transparent Huge Pages (a MongoDB recommendation), and close port 27017 to the outside with ufw deny 27017. A MongoDB without authentication exposed on the Internet is an immediate ransomware target.
Define the docker-compose.yml
Declare a mongo:7 service, mount a volume on /data/db, and pass MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD through .env. Add the --auth command to enforce authentication from startup.
Create the application users
After docker compose up -d, connect with docker compose exec mongo mongosh, then create a dedicated user for your database with the readWrite role only. Reserve the root account for administration, never for the application.
Enable the replica set
For high availability, start each node with --replSet rs0, then initialize with rs.initiate() listing the members. The replica set provides automatic failover and allows reads on the secondaries (readPreference: secondary).
Secure remote access with TLS
If an external app connects, enable TLS with a Let's Encrypt certificate mounted in the container (--tlsMode requireTLS), and use the internal key (--keyFile) to authenticate the replica set members with each other.
Index and back up
Create your indexes with db.collection.createIndex() while analyzing queries via explain(). Schedule a daily mongodump or, for large volumes, consistent volume snapshots, and archive them off the VPS.
Enable the profiler in slow mode (db.setProfilingLevel(1, { slowms: 100 })) to capture queries exceeding 100 ms, then cross-reference the results with explain('executionStats'): most MongoDB performance problems come from a missing index that forces a COLLSCAN. For time series (logs, metrics), leverage the native time-series collections of MongoDB 5+: they compress automatically and dramatically reduce the disk footprint.