How to Monitor Your Remix Application with Vigilmon
Remix is a full-stack React framework that runs on Node.js, Deno, Cloudflare Workers, and more. Whether you're deploying to Fly.io, Railway, Vercel, or a VPS, you need uptime monitoring. This guide covers adding health endpoints to Remix apps and setting up Vigilmon monitoring.
Remix Deployment Targets
Remix adapts to your deployment environment:
| Adapter | Platform |
|---|---|
| @remix-run/node | Node.js servers (Express, Fly.io, Railway) |
| @remix-run/cloudflare | Cloudflare Workers/Pages |
| @remix-run/vercel | Vercel |
| @remix-run/deno | Deno Deploy |
Adding a Health Route to Remix
Method 1: Resource Route (Recommended)
Create pp/routes/health.tsx:
` ypescript
// app/routes/health.tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
interface HealthData {
status: "ok" | "error";
timestamp: string;
uptime: number;
environment: string;
}
export async function loader({ request }: LoaderFunctionArgs) {
// Only respond to GET requests
if (request.method !== "GET") {
return json({ error: "Method not allowed" }, { status: 405 });
}
const health: HealthData = {
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
environment: process.env.NODE_ENV || "unknown",
};
return json(health, {
headers: {
"Cache-Control": "no-store",
},
});
}
// No default export = resource route (no UI component)
`
Vigilmon (or any monitor) hits https://yourapp.com/health and gets the JSON response.
Method 2: Health with Database Check
` ypescript
// app/routes/health.tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { db } from "~/db.server"; // Your database connection
export async function loader({ request }: LoaderFunctionArgs) {
const start = Date.now();
try {
// Check database connectivity
await db.execute("SELECT 1");
const dbLatency = Date.now() - start;
return json({
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
components: {
database: { status: "ok", latencyMs: dbLatency },
},
});
} catch (error) {
return json(
{
status: "error",
timestamp: new Date().toISOString(),
components: {
database: {
status: "error",
error: error instanceof Error ? error.message : "Unknown",
},
},
},
{ status: 503 }
);
}
}
`
Method 3: Prisma Database Check
` ypescript
// app/routes/health.tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { prisma } from "~/db.server";
export async function loader({ request }: LoaderFunctionArgs) {
try {
await prisma.SELECT 1;
return json({ status: "ok", timestamp: new Date().toISOString() });
} catch (error) {
return json({ status: "error" }, { status: 503 });
}
}
`
Remix on Express (Custom Server)
If you're using a custom Express server with Remix:
` ypescript
// server.ts
import express from "express";
import { createRequestHandler } from "@remix-run/express";
import * as build from "./build/index.js";
const app = express();
// Health endpoint BEFORE Remix handler
app.get("/health", (req, res) => {
res.json({
status: "ok",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
});
app.all(
"*",
createRequestHandler({ build })
);
app.listen(3000);
`
The Express /health route bypasses Remix routing entirely-simpler and faster.
Remix on Cloudflare Workers
` ypescript
// app/routes/health.tsx (Cloudflare adapter)
import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { json } from "@remix-run/cloudflare";
export async function loader({ context }: LoaderFunctionArgs) {
// context.env gives you access to KV, D1, etc.
const health = {
status: "ok",
timestamp: new Date().toISOString(),
runtime: "cloudflare-workers",
};
return json(health, {
headers: { "Cache-Control": "no-store" },
});
}
`
Protecting the Health Endpoint
If you need the health endpoint to be accessible externally but want to prevent abuse:
` ypescript
// app/routes/health.tsx
export async function loader({ request }: LoaderFunctionArgs) {
// Optional: validate a secret token for detailed health data
const token = request.headers.get("X-Health-Token");
const detailed = token === process.env.HEALTH_TOKEN;
const basic = { status: "ok", timestamp: new Date().toISOString() };
if (!detailed) {
return json(basic);
}
// Return detailed health info only with token
return json({
...basic,
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.env.npm_package_version,
});
}
`
In Vigilmon, you can add the X-Health-Token as a custom header in the monitor configuration.
Deployment Configuration
Fly.io
` oml
fly.toml
[http_service]
internal_port = 3000
force_https = true
[[http_service.checks]]
grace_period = "10s"
interval = "30s"
method = "GET"
timeout = "5s"
path = "/health"
`
Fly's internal health check + Vigilmon's external check = complete coverage.
Railway
` oml
railway.toml
[deploy]
healthcheckPath = "/health"
healthcheckTimeout = 5
`
Docker Healthcheck
dockerfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
Setting Up Vigilmon
- Go to vigilmon.online
- Click Add Monitor
-
Configure:
- URL: https://yourdomain.com/health
- Method: GET
- Expected status: 200
- Interval: 1 minute (Pro) / 5 minutes (free)
Add SSL certificate monitoring
Connect Slack or email for alerts
Remix-Specific Monitoring Tips
Route health vs app health: Your health route lives inside Remix's request handling. If Remix fails to boot, the health endpoint won't respond either-which is exactly what you want Vigilmon to catch.
Cache headers: Always set Cache-Control: no-store on health endpoints so CDNs and proxies don't cache stale responses.
Prefetch: Remix's can trigger your health endpoint unintentionally if your link points to /health. Use a non-HTML resource route (no default export) to prevent this.
Summary
- Create pp/routes/health.tsx as a resource route (no default export)
- Return 200 for healthy, 503 for degraded
- Add database checks with proper error handling
- Set Cache-Control: no-store to prevent caching
- Monitor externally with Vigilmon for platform-level visibility
Monitor your Remix app at vigilmon.online - free for 3 monitors, no credit card required.
Top comments (0)