DEV Community

Vigilmon
Vigilmon

Posted on

How to Monitor Your Remix Application with Vigilmon

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 },
  },
});
Enter fullscreen mode Exit fullscreen mode

} 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() });
Enter fullscreen mode Exit fullscreen mode

} 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

  1. Go to vigilmon.online
  2. Click Add Monitor
  3. Configure:

  4. Add SSL certificate monitoring

  5. 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)