tutorial

Monitoring Your Payload CMS App with Vigilmon (Free, Multi-Region)

Payload CMS is the Next.js-native headless CMS taking the TypeScript ecosystem by storm. When it goes down, your entire frontend breaks. Learn how to add a health route, set up external uptime monitoring, and get instant alerts.

Monitoring Your Payload CMS App with Vigilmon (Free, Multi-Region)

Payload CMS is the headless CMS built for the TypeScript ecosystem. It runs natively inside your Next.js app, shares the same database connection, and exposes a full REST and GraphQL API — all without the overhead of a separate service.

That tight integration is its strength. It's also what makes a Payload outage expensive: when Payload goes down, so does your entire Next.js app.

This guide covers how to add a health check route to a Payload app, point Vigilmon at it for external multi-region monitoring, and configure instant alerts so you hear about failures before your users do.


Why Payload monitoring is different

Because Payload is embedded in your Next.js app, it doesn't run as a separate process with its own health endpoint. The health of your Payload instance is the health of your Next.js server — including:

  • The Node.js process
  • Database connectivity (MongoDB or PostgreSQL)
  • The Payload collection API
  • The Payload admin UI

A generic HTTP monitor on your homepage URL will tell you if the server is responding. But it won't tell you if the Payload API is returning errors, or if the database connection pool has exhausted. You need a dedicated health route.


Step 1: Add a health route to your Payload app

With Next.js App Router (Payload 3.x)

Payload 3.x runs inside the Next.js App Router. Add a dedicated API route handler:

// app/api/health/route.ts
import { NextResponse } from 'next/server'
import { getPayload } from 'payload'
import config from '@payload-config'

export async function GET() {
  try {
    const payload = await getPayload({ config })

    // Test database connectivity with a lightweight query
    await payload.find({
      collection: 'users',
      limit: 0,
    })

    return NextResponse.json(
      { status: 'ok', database: 'ok', timestamp: new Date().toISOString() },
      { status: 200 }
    )
  } catch (error) {
    return NextResponse.json(
      { status: 'error', database: 'error', detail: String(error) },
      { status: 503 }
    )
  }
}

Verify it locally:

curl http://localhost:3000/api/health
# {"status":"ok","database":"ok","timestamp":"2026-06-30T10:00:00.000Z"}

With Payload 2.x (Express-based)

For Payload 2.x running on Express, add a custom route in your server file:

// server.ts
import express from 'express'
import payload from 'payload'

const app = express()

// Health check before Payload initialisation completes
app.get('/api/health', async (req, res) => {
  try {
    // Probe the database by running a minimal count query
    await payload.db.connection.db.command({ ping: 1 })
    res.status(200).json({ status: 'ok', database: 'ok' })
  } catch (error) {
    res.status(503).json({ status: 'error', database: 'error' })
  }
})

await payload.init({ express: app, /* ... */ })
app.listen(3000)

Step 2: Set up Vigilmon monitoring

With the health endpoint live, point Vigilmon at it:

  1. Sign up at vigilmon.online — free tier, no credit card
  2. Click New Monitor → HTTP
  3. Enter https://your-app.com/api/health
  4. Set the check interval (5 minutes on free tier)
  5. Save

Vigilmon checks from multiple geographic regions. If any region can't reach your endpoint, it opens an incident immediately.

Add a keyword monitor for body validation

Add a second monitor that asserts the response content, not just the HTTP status code:

  1. New Monitor → Keyword
  2. URL: https://your-app.com/api/health
  3. Keyword: "status":"ok"
  4. If absent, Vigilmon treats it as a failure

This catches the case where your Next.js server returns HTTP 200 (because the server itself is running) but the Payload API layer or database connection is broken.

Recommended monitor set for Payload

| Monitor | URL | Type | What it catches | |---|---|---|---| | App health | /api/health | HTTP | Process down, port not responding | | Database check | /api/health | Keyword | MongoDB/PostgreSQL down | | Payload admin | /admin | HTTP | Admin UI broken or unreachable | | GraphQL endpoint | /api/graphql | HTTP | GraphQL layer broken |


Step 3: Monitor the Payload admin panel

The admin panel is the interface your editors and content managers use daily. It can break independently of the API — for example, after a bad deploy that corrupts the admin build.

Add a separate HTTP monitor:

  • URL: https://your-app.com/admin
  • Type: HTTP
  • Expected status: 200

Step 4: Configure alert delivery

Slack:

  1. Create an incoming webhook in Slack
  2. In Vigilmon: Notifications → New Channel → Slack
  3. Paste the webhook URL and enable it on your monitors

Email:

  1. In Vigilmon: Notifications → New Channel → Email
  2. Add one or more addresses

When your Payload app goes down, you'll get:

🔴 DOWN: your-app.com/api/health
Status: 503 Service Unavailable
Regions: US-East, EU-West, AP-Southeast
Started: 4 minutes ago

And on recovery:

✅ RECOVERED: your-app.com/api/health is back UP
Total downtime: 9 minutes

Step 5: Add a startup readiness check

Payload can take several seconds to initialise on cold start, especially when running migrations. A health check that fires before Payload is ready will return a misleading error. Add a readiness gate:

// app/api/health/route.ts
import { NextResponse } from 'next/server'

let payloadReady = false

// Called from your Payload onInit hook
export function markReady() {
  payloadReady = true
}

export async function GET() {
  if (!payloadReady) {
    return NextResponse.json(
      { status: 'starting', database: 'unknown' },
      { status: 503 }
    )
  }

  // ... existing health check logic
}

In your Payload config:

// payload.config.ts
import { markReady } from './app/api/health/route'

export default buildConfig({
  onInit: async () => {
    markReady()
  },
  // ...
})

This prevents Vigilmon from firing false-positive alerts during deployments or cold starts.


Step 6: Create a public status page

If clients or external developers depend on your Payload-powered API:

  1. Status Pages → New Status Page in Vigilmon
  2. Add your health monitors
  3. Share the public URL with API consumers

What you've built

| What | How | |---|---| | Health route | /api/health — custom App Router handler | | Database check | Lightweight Payload query inside the health route | | External monitoring | Vigilmon HTTP monitor (multi-region) | | Body assertion | Vigilmon keyword monitor on "status":"ok" | | Admin UI check | Separate HTTP monitor on /admin | | Startup guard | Readiness flag prevents cold-start false positives | | Instant alerts | Slack/email on down + recovery |

Your Payload CMS is now externally observable. The next time a deployment breaks the database connection, the admin panel fails to build, or the Next.js process crashes under load, you'll know about it before your editors or end users do.


Get started free at vigilmon.online — your first monitor is running in under a minute.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →