MySQL powers millions of applications. When it goes down or becomes unreachable, your entire app follows. This guide shows how to monitor MySQL health externally using Vigilmon - no agent installation, no database credentials exposed to your monitoring tool.
Why External MySQL Monitoring Matters
Database-internal tools (MySQL slow query log, Performance Schema, SHOW PROCESSLIST) tell you what's happening inside MySQL. But they don't catch:
- Connection refused: MySQL is listening but your app can't reach it (firewall rule change, bind-address misconfiguration)
- Connection pool exhausted: Too many connections, app starts failing even though MySQL is "up"
- Application layer failures: MySQL is up but your ORM connection pool is broken
- Network path failures: The database is reachable from localhost but not from your application servers
External HTTP monitoring via a health endpoint catches all of these.
Setting Up a MySQL Health Endpoint
Expose a /health/db endpoint in your application that tests the MySQL connection:
Node.js (mysql2)
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10
});
app.get('/health/db', async (req, res) => {
try {
const [rows] = await pool.execute('SELECT 1 AS health');
res.json({
status: 'ok',
db: 'connected',
result: rows[0].health
});
} catch (err) {
res.status(503).json({
status: 'error',
db: 'disconnected',
message: err.message
});
}
});
PHP/Laravel
Route::get('/health/db', function () {
try {
$result = DB::select('SELECT 1 as health');
return response()->json([
'status' => 'ok',
'db' => 'connected',
'result' => $result[0]->health
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'db' => 'disconnected',
'message' => $e->getMessage()
], 503);
}
});
Python/Django
from django.db import connection
from django.http import JsonResponse
def db_health(request):
try:
with connection.cursor() as cursor:
cursor.execute("SELECT 1")
cursor.fetchone()
return JsonResponse({"status": "ok", "db": "connected"})
except Exception as e:
return JsonResponse(
{"status": "error", "db": str(e)},
status=503
)
Ruby on Rails
get '/health/db', to: proc { |env|
begin
ActiveRecord::Base.connection.execute("SELECT 1")
[200, {"Content-Type" => "application/json"}, ['{"status":"ok","db":"connected"}']]
rescue => e
[503, {"Content-Type" => "application/json"}, ["{\"status\":\"error\",\"db\":\"#{e.message.to_json}\"}"]]
end
}
Go
func dbHealthHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{
"status": "error",
"db": err.Error(),
})
return
}
json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
"db": "connected",
})
}
}
Configuring Vigilmon for MySQL Health Monitoring
Once your health endpoint is live:
- Sign up at vigilmon.online
- Add a new HTTP(S) monitor:
URL: https://yourapp.com/health/db
Method: GET
Expected status: 200
Keyword check: "connected"
Interval: 3 minutes
Multi-region: enabled
The keyword check confirms MySQL is actually connected, not just that the endpoint returned 200. If your app incorrectly returns 200 when the DB is down, the keyword check catches it.
Monitoring MySQL Replication
If you use MySQL replication (master-replica), monitor the replica health separately:
Monitor 1: Primary yourapp.com/health/db
Monitor 2: Replica yourapp.com/health/db-replica
Your replica health endpoint should check both connectivity and replication lag:
app.get('/health/db-replica', async (req, res) => {
try {
const [rows] = await replicaPool.execute(
'SHOW SLAVE STATUS'
);
const status = rows[0];
const lag = parseInt(status.Seconds_Behind_Master || 0);
if (!status.Slave_IO_Running || !status.Slave_SQL_Running) {
return res.status(503).json({
status: 'error',
replication: 'stopped'
});
}
if (lag > 30) {
return res.status(503).json({
status: 'degraded',
lag_seconds: lag
});
}
res.json({
status: 'ok',
replication: 'running',
lag_seconds: lag
});
} catch (err) {
res.status(503).json({ status: 'error', db: err.message });
}
});
MySQL Connection Pool Monitoring
If you use connection pooling (ProxySQL, MySQL Router, or app-level pools), monitor pool saturation:
app.get('/health/pool', (req, res) => {
const stats = {
total: pool.pool._allConnections.length,
free: pool.pool._freeConnections.length,
queue: pool.pool._connectionQueue.length
};
if (stats.queue > 5) {
return res.status(503).json({
status: 'degraded',
pool: stats,
message: 'Connection queue building up'
});
}
res.json({ status: 'ok', pool: stats });
});
What This Setup Monitors vs. What It Doesn't
Monitors:
- Database reachability from your application
- Connection pool health and saturation
- Replication status and lag
- Application-layer DB failures
Doesn't monitor (needs internal tooling):
- Slow query performance (use slow query log)
- Index usage and query plans (use EXPLAIN)
- InnoDB buffer pool hit rate (use Performance Schema)
- Disk usage and table sizes
Combine Vigilmon (external availability) with your existing MySQL monitoring tools (internal performance) for complete coverage.
Sample MySQL Monitoring Setup
Monitor 1: App + MySQL health yourapp.com/health/db 3 min interval
Monitor 2: Replica health yourapp.com/health/db-replica 5 min interval
Monitor 3: Connection pool yourapp.com/health/pool 3 min interval
Monitor 4: phpMyAdmin/adminer dbadmin.yourapp.com 10 min interval
Start Monitoring Your MySQL Database
Set up free database health monitoring at vigilmon.online. No agent installation, no database credentials in your monitoring tool - just an HTTP endpoint that tests connectivity from your application layer.
When MySQL goes down, you want to know before your users see a 500 error.
Top comments (0)