KeystoneJS is a modern headless CMS and GraphQL/REST API framework that puts your content data model in code — making it a popular choice for teams building content-driven applications on Node.js. When KeystoneJS goes down or its database connection drops, your frontend applications lose their content API and your editorial team loses admin access. Vigilmon gives you continuous monitoring for your KeystoneJS server, GraphQL API, REST endpoints, database, and background services.
What You'll Set Up
- Web server availability monitoring on port 3000
- GraphQL API endpoint health checks
- REST API response time monitoring
- Database connectivity monitoring (PostgreSQL or SQLite)
- Session store health checks
- File storage backend monitoring
- Image transformation service health
- Background job processing health
- Admin UI availability monitoring
Prerequisites
- KeystoneJS 6+ deployed and serving on port 3000 (or your configured port)
- A PostgreSQL or SQLite database connected and migrated
- A free Vigilmon account
Step 1: Monitor Web Server Availability
The first thing to monitor is whether your KeystoneJS server is up and accepting connections.
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter your KeystoneJS URL:
https://cms.yourdomain.com - Set Check interval to
1 minute. - Set Expected HTTP status to
200. - Click Save.
For a more reliable signal, add a dedicated health check endpoint to your KeystoneJS application. In your keystone.ts configuration:
import { config } from '@keystone-6/core';
import express from 'express';
export default config({
// ... your existing config
server: {
extendExpressApp: (app) => {
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
},
},
});
Update your Vigilmon monitor URL to https://cms.yourdomain.com/health for a purpose-built health signal.
Step 2: GraphQL API Endpoint Health
KeystoneJS's primary API is GraphQL, served at /api/graphql. Monitor it with an HTTP check that sends a minimal introspection query:
- Click Add Monitor → HTTP / HTTPS.
- URL:
https://cms.yourdomain.com/api/graphql - Set Method to
POST. - Set Expected HTTP status to
200. - Under Request body, add:
{"query": "{ __typename }"}
- Set Content-Type header to
application/json. - Set Check interval to
1 minute. - Click Save.
The { __typename } query is the lightest possible valid GraphQL query — it asks for the root type name, which requires the schema to be loaded and the database connection to be active. A 200 response with {"data":{"__typename":"Query"}} confirms the GraphQL API is fully operational.
Enable Response body keyword check and require "__typename" in the response to detect cases where the server returns 200 but the GraphQL layer has failed.
Step 3: REST API Response Time Monitoring
If your KeystoneJS deployment uses REST API routes (via the extendExpressApp hook or Keystone's built-in REST endpoints), add dedicated response time monitors for your critical routes:
- Add an HTTP monitor for each critical REST endpoint.
- Enable Response time alerting and set a threshold of
2000ms(2 seconds). - Set Check interval to
5 minutes.
For a content listing endpoint used by your frontend:
https://cms.yourdomain.com/api/rest/posts?take=1
Response time spikes on this endpoint often signal database query performance degradation before the endpoint fails completely. Early alerting on slow responses prevents user-facing timeouts.
Step 4: Database Connectivity
KeystoneJS requires a live database connection for every API request. Add a database health check to your application:
import { config } from '@keystone-6/core';
import { PrismaClient } from '.prisma/client';
const prisma = new PrismaClient();
export default config({
server: {
extendExpressApp: (app) => {
app.get('/health/db', async (req, res) => {
try {
await prisma.$queryRaw`SELECT 1`;
res.json({ status: 'ok', database: 'connected' });
} catch (err) {
res.status(503).json({ status: 'error', database: 'disconnected' });
}
});
},
},
});
Then in Vigilmon:
- Add an HTTP monitor for
https://cms.yourdomain.com/health/db. - Set Expected HTTP status to
200. - Enable Response body keyword check requiring
"database":"connected". - Set Check interval to
1 minute.
For PostgreSQL deployments, you can also add a direct TCP port monitor:
- Click Add Monitor → TCP Port.
- Enter your PostgreSQL host and port
5432. - Set Check interval to
1 minute.
The TCP check fires even if your KeystoneJS app is down, telling you whether the database itself is the problem.
Step 5: Session Store Health
KeystoneJS's admin UI and authenticated API routes rely on sessions. If your session store (in-memory, database, or Redis) is unavailable, users get logged out or can't log in.
Add a session health check endpoint:
app.get('/health/session', async (req, res) => {
try {
// Attempt to touch the session store
req.session.touch();
res.json({ status: 'ok', sessions: 'available' });
} catch (err) {
res.status(503).json({ status: 'error', sessions: 'unavailable' });
}
});
If you use Redis as a session store, also add a direct TCP check on port 6379:
- Click Add Monitor → TCP Port.
- Enter your Redis host and port
6379. - Set Check interval to
1 minute.
Step 6: File Storage Backend
KeystoneJS's image and file fields store uploads to a local filesystem path or an external storage provider (S3, Cloudflare R2, Google Cloud Storage). Monitor the storage backend's availability:
Local filesystem storage:
Add a health check that verifies write access to the uploads directory:
import { writeFileSync, unlinkSync } from 'fs';
import { join } from 'path';
app.get('/health/storage', (req, res) => {
const testFile = join(process.env.UPLOADS_DIR || './public/images', '.health');
try {
writeFileSync(testFile, 'ok');
unlinkSync(testFile);
res.json({ status: 'ok', storage: 'writable' });
} catch (err) {
res.status(503).json({ status: 'error', storage: 'not writable' });
}
});
S3-compatible storage:
Add an HTTP monitor for your S3 bucket's endpoint:
- URL:
https://your-bucket.s3.amazonaws.com/(or your S3-compatible endpoint). - Set Expected HTTP status to
200or403(both indicate the service is up; 403 means auth required, which is expected for a private bucket root).
Step 7: Image Transformation Service
If your KeystoneJS setup uses @keystone-6/core's built-in image transformations or an external service like Sharp or imgproxy, monitor the transformation pipeline:
- Upload a small test image to your CMS.
- Add an HTTP monitor targeting the transformed image URL with a specific size parameter:
https://cms.yourdomain.com/images/test-image.jpg?w=100&h=100
- Set Expected HTTP status to
200. - Set Check interval to
5 minutes.
A 404 or 500 on this URL indicates the image transformation service or the underlying Sharp native module has crashed.
Step 8: Background Job Processing
If your KeystoneJS application uses background jobs (email sending, import processing, webhook delivery), add a cron heartbeat to confirm the job processor is running:
- Click Add Monitor → Cron Heartbeat.
- Set the expected interval to match your job processing frequency.
- Copy the heartbeat URL.
- In your job processor, ping Vigilmon on each successful processing cycle:
import axios from 'axios';
async function processJobQueue() {
const jobs = await getQueuedJobs();
for (const job of jobs) {
await processJob(job);
}
// Ping Vigilmon after each processing cycle
await axios.get('https://vigilmon.online/heartbeat/YOUR_HEARTBEAT_ID').catch(() => {});
}
// Run every 5 minutes
setInterval(processJobQueue, 5 * 60 * 1000);
If the job processor crashes or hangs, the heartbeat stops and Vigilmon alerts after the expected interval.
Step 9: Admin UI Availability
The KeystoneJS admin UI serves at / (or /keystonejs-admin depending on your configuration). It's the primary interface for your content editors — monitor it separately from the API:
- Add an HTTP monitor for
https://cms.yourdomain.com/. - Enable Response body keyword check and require
KeystoneJSor the title of your admin UI page in the response body. - Set Check interval to
5 minutes.
This catches cases where the Next.js frontend serving the admin UI fails to build or crashes while the API continues running.
Step 10: Configure Alert Channels
- Go to Alert Channels in Vigilmon and add Slack, email, or webhook notifications.
- For the database connectivity monitor, set Consecutive failures before alert to
1— a lost database connection is an immediate incident. - For the web server and GraphQL monitors, set Consecutive failures before alert to
2to handle brief Node.js restarts. - Route admin UI alerts to your content team's Slack channel so editors know when to stop trying to publish.
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| Web server | /health or / | Node.js process crash |
| GraphQL API | POST /api/graphql | Schema load failure, DB query errors |
| REST endpoints | Critical REST routes | Slow queries, route handler failures |
| Database | /health/db or TCP port 5432 | PostgreSQL connectivity loss |
| Session store | /health/session or Redis TCP 6379 | Auth system failure |
| File storage | /health/storage or S3 endpoint | Upload storage unavailable |
| Image transform | Transformed image URL | Sharp/imgproxy crash |
| Background jobs | Cron heartbeat | Job processor stopped |
| Admin UI | / with body check | Next.js admin frontend failure |
KeystoneJS gives content teams a powerful, code-driven CMS — but it has several independently-failable layers. With Vigilmon monitoring the GraphQL API, database connection, file storage, and background job health, you catch failures at the layer they originate rather than discovering them through user complaints.