Uptime Monitoring for PlanetScale-Powered Apps (Free, Multi-Region)
PlanetScale brings branching, non-blocking schema changes, and serverless connection pooling to MySQL. It removes a lot of operational pain — but it doesn't alert you when your app can't reach the database.
This guide shows you how to add a health endpoint to a PlanetScale-backed application, what to monitor, and how to set up free external uptime monitoring in about 20 minutes.
The failure modes that bite PlanetScale developers
Connection limit exhaustion — PlanetScale's serverless driver pools connections per branch. Serverless functions that open connections without closing them hit the per-branch limit fast. When that happens, new queries hang until timeout. Your app stays up, responses just take 30 seconds instead of 30ms.
Branch or deploy request issues — If you're running automated deploy requests as part of CI, a failed migration leaves your schema mid-flight. Queries against the affected table start returning schema errors. Nothing in your process monitor catches this.
Credential rotation — PlanetScale database passwords are per-branch. After rotation, if a stale password is in your environment, queries fail silently unless your health check verifies connectivity on every probe.
Step 1: Create a health check endpoint
Here's a Node.js example using the PlanetScale serverless driver:
// src/health.ts
import { connect } from '@planetscale/database'
const config = {
host: process.env.DATABASE_HOST,
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
}
interface HealthResult {
status: 'ok' | 'error'
latencyMs?: number
error?: string
}
export async function checkDatabase(): Promise<HealthResult> {
const conn = connect(config)
const start = Date.now()
try {
const results = await conn.execute('SELECT 1 as probe')
const latencyMs = Date.now() - start
if (results.rows.length === 0) {
return { status: 'error', error: 'Empty result from probe query' }
}
return { status: 'ok', latencyMs }
} catch (err: any) {
return { status: 'error', error: err.message }
}
}
export async function healthHandler(req: any, res: any) {
const db = await checkDatabase()
const allOk = db.status === 'ok'
res.status(allOk ? 200 : 503).json({
status: allOk ? 'ok' : 'degraded',
checks: { database: db },
branch: process.env.DATABASE_HOST?.split('.')[0] ?? 'unknown',
timestamp: new Date().toISOString(),
})
}
In Express:
import express from 'express'
import { healthHandler } from './health'
const app = express()
app.get('/health', healthHandler)
app.listen(3000)
In Next.js (App Router):
// app/api/health/route.ts
import { NextResponse } from 'next/server'
import { checkDatabase } from '@/lib/health'
export async function GET() {
const db = await checkDatabase()
const allOk = db.status === 'ok'
return NextResponse.json(
{ status: allOk ? 'ok' : 'degraded', checks: { database: db } },
{ status: allOk ? 200 : 503 }
)
}
Step 2: Track connection pool depth
For long-running servers (not serverless), add a connection pool check:
import { createPool } from '@vercel/postgres' // or mysql2 pooling
const pool = createPool({
connectionString: process.env.DATABASE_URL,
max: 10, // match your PlanetScale plan limit
})
async function checkPool(): Promise<HealthResult> {
const start = Date.now()
let client
try {
client = await pool.connect()
await client.query('SELECT 1')
return { status: 'ok', latencyMs: Date.now() - start }
} catch (err: any) {
return { status: 'error', error: err.message }
} finally {
client?.release()
}
}
Add this metric to your health response and graph it over time — a rising latency trend tells you you're approaching connection exhaustion before queries start failing.
Step 3: Monitor the main branch vs production branches separately
PlanetScale's branching model means you might run separate monitors per branch:
| Branch | URL | Monitor interval |
|---|---|---|
main (production) |
https://api.example.com/health |
60s |
shadow (canary) |
https://shadow.api.example.com/health |
5m |
Set the production monitor to a tighter interval and stricter alert thresholds.
Step 4: Set up external monitoring
- Visit vigilmon.online and create a free account.
- Add an HTTP(S) monitor for
https://your-app.com/health. - Set interval to 60 seconds.
- Set expected status: 200.
- Add two or more monitoring regions (e.g. EU West, US East).
- Configure an alert: email or Slack.
Within a minute your first probes run. If your PlanetScale credentials expire or connection pools exhaust, you'll know in under two minutes.
Step 5: Alert on latency, not just downtime
PlanetScale's serverless connections sometimes slow down before they fail outright. Set a latency threshold alert in your monitoring:
- Warning at 500ms
- Critical at 2000ms
That gives you a heads-up to check the PlanetScale insights dashboard before users start bouncing.
Recap
- Add a
/healthendpoint that runsSELECT 1against PlanetScale and returns structured JSON. - Include the branch name in your health response so alerts tell you which branch is failing.
- Monitor production and canary branches on separate intervals.
- Set up a free external monitor at vigilmon.online — multi-region, 60-second probes, no credit card.
- Alert on latency thresholds before connection exhaustion becomes an outage.
PlanetScale makes your database resilient to schema changes. Uptime monitoring makes your application resilient to the things PlanetScale can't control.
Top comments (0)