Uptime Monitoring for MongoDB Atlas Applications (Free, Multi-Region)
MongoDB Atlas handles backups, scaling, and multi-region replication for you. It's a genuinely managed database. But "managed database" doesn't mean "your application layer is monitored."
When Atlas is healthy and your app can't reach it — because of a bad connection string, a VPC peering issue, a network ACL change, or connection pool exhaustion — Atlas's dashboard shows everything green. Your users see errors.
This guide shows you how to add a health endpoint to a MongoDB Atlas-backed application and set up external monitoring.
What breaks and why Atlas won't tell you
Connection pool exhaustion — Each Atlas cluster tier has a connection limit. A poorly written serverless function that opens a connection on every invocation without closing it will exhaust the pool. Queries start timing out. Atlas's Metrics tab shows high connection count, but you won't know unless you're watching.
VPC peering / private endpoint issues — If you're using Atlas VPC peering or private endpoints, a network ACL change on the AWS/GCP/Azure side can block all traffic. Atlas shows the cluster as healthy because it's looking at MongoDB internals, not the network layer.
Slow queries blocking writes — Atlas doesn't alert you on slow queries by default. A missing index on a hot collection causes queries to scan millions of documents, blocking write operations. The cluster appears healthy; your app response times degrade.
IP access list changes — Someone removes your application server's IP from the Atlas access list. Every connection attempt returns ECONNREFUSED. Zero visibility unless you're actively monitoring connectivity.
Step 1: Create a health check that tests Atlas connectivity
// health.js — Node.js with Mongoose
const mongoose = require('mongoose')
async function checkMongoDB() {
const start = Date.now()
// Use readyState to check existing connection
if (mongoose.connection.readyState === 1) {
try {
// Run a lightweight ping command
await mongoose.connection.db.admin().ping()
return { status: 'ok', latencyMs: Date.now() - start }
} catch (err) {
return { status: 'error', error: err.message }
}
}
// Not yet connected — try connecting
try {
await mongoose.connect(process.env.MONGODB_URI, {
serverSelectionTimeoutMS: 3000,
connectTimeoutMS: 3000,
})
await mongoose.connection.db.admin().ping()
return { status: 'ok', latencyMs: Date.now() - start }
} catch (err) {
return { status: 'error', error: err.message }
}
}
module.exports = { checkMongoDB }
// routes/health.js — Express route
const { checkMongoDB } = require('../health')
module.exports = async function healthRoute(req, res) {
const mongo = await checkMongoDB()
const allOk = mongo.status === 'ok'
res.status(allOk ? 200 : 503).json({
status: allOk ? 'ok' : 'degraded',
checks: { mongodb: mongo },
timestamp: new Date().toISOString(),
})
}
Register it:
// app.js
const express = require('express')
const healthRoute = require('./routes/health')
const app = express()
app.get('/health', healthRoute)
Step 2: Python version (Motor async driver)
# health.py — FastAPI + Motor
from motor.motor_asyncio import AsyncIOMotorClient
import os
import time
_client = None
def get_client():
global _client
if _client is None:
_client = AsyncIOMotorClient(
os.environ['MONGODB_URI'],
serverSelectionTimeoutMS=3000,
)
return _client
async def check_mongodb() -> dict:
start = time.time()
try:
client = get_client()
await client.admin.command('ping')
return {'status': 'ok', 'latency_ms': round((time.time() - start) * 1000)}
except Exception as e:
return {'status': 'error', 'error': str(e)}
# FastAPI route
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from health import check_mongodb
app = FastAPI()
@app.get('/health')
async def health():
mongo = await check_mongodb()
all_ok = mongo['status'] == 'ok'
return JSONResponse(
status_code=200 if all_ok else 503,
content={'status': 'ok' if all_ok else 'degraded', 'checks': {'mongodb': mongo}}
)
Step 3: Check replica set status (optional, for advanced monitoring)
For production apps where read preference matters, check replica set health directly:
async function checkReplicaSetStatus() {
try {
const status = await mongoose.connection.db.admin().replSetGetStatus()
const primary = status.members.find(m => m.stateStr === 'PRIMARY')
const secondaries = status.members.filter(m => m.stateStr === 'SECONDARY')
return {
status: primary ? 'ok' : 'error',
primary: primary?.name,
secondaryCount: secondaries.length,
totalMembers: status.members.length,
}
} catch (err) {
return { status: 'error', error: err.message }
}
}
Step 4: Enable Atlas built-in alerts (complement, not substitute)
Atlas has a built-in alerting system. Enable at minimum:
- Connections % of available: alert at 80%
- Opcounters: alert on unusual query rate spikes
- Replication lag: alert if lag > 10 seconds
Do this in Atlas → Project Alerts → Add Alert. Route alerts to your email or PagerDuty.
These Atlas alerts complement external monitoring — Atlas knows its internals, your external monitor knows if your application can reach it.
Step 5: Set up external uptime monitoring
- Go to vigilmon.online — free, no credit card.
- Create an HTTP(S) monitor.
- URL:
https://your-api.example.com/health - Interval: 60s
- Expected status: 200
- Select 2+ monitoring regions
- Alert via email or Slack
When Atlas becomes unreachable from your app (not from Atlas's own network), this monitor fires. Atlas won't.
Recap
- Add a
/healthendpoint that pings Atlas viaadmin().ping()or the equivalent — don't just checkmongoose.connection.readyState. - Use a short serverSelectionTimeoutMS (3000ms) so the health check fails fast, not after 30 seconds.
- Enable Atlas built-in alerts for internal cluster metrics (connections, replication lag).
- Point an external monitor at your health endpoint — it catches application-layer connectivity issues Atlas can't see.
- vigilmon.online covers the external layer for free with multi-region probes.
MongoDB Atlas is a great database. Now it's a great database you know about when your app can't reach it.
Top comments (0)