Pixel Pete, the CanaryHub canaryCanaryHub

[ connect ]

Monitor your self-hosted app

Deploying on a VPS, Docker, or your own CI? You don't need an AI tool — wire up monitoring by hand in two small pieces: a health endpoint CanaryHub polls, and fire-and-forget error reporting. Pick your stack below.

1 · Add a health endpoint + error reporting

The health endpoint returns ok, degraded, or down with one entry in checksper critical dependency. It must be public, fast (<2s), and side-effect free. Error reporting must never block or crash a request.

Next.js (App Router)

app/api/health/route.tsts
import { NextResponse } from "next/server";export const dynamic = "force-dynamic";export async function GET() {  const started = performance.now();  let db = "ok";  try {    await sql`SELECT 1`; // your DB client here  } catch {    db = "down";  }  const latencyMs = Math.round(performance.now() - started);  return NextResponse.json({    status: db === "ok" ? "ok" : "down",    version: process.env.GIT_SHA ?? "dev",    checks: [{ name: "database", status: db, latencyMs }],  });}
error reporting — anywhere on the serverts
$ npm install @canaryhub/sdk// once at startup (instrumentation.ts or your server entry):import { canary } from "@canaryhub/sdk";canary.init("ch_live_YOUR_KEY_HERE");// wherever you catch an unexpected server error:canary.error("checkout_failed", { message: String(err) });

Express

server.jsjs
import express from "express";import { canary } from "@canaryhub/sdk";canary.init("ch_live_YOUR_KEY_HERE");const app = express();app.get("/api/health", async (_req, res) => {  const started = performance.now();  let db = "ok";  try {    await pool.query("SELECT 1"); // your DB client here  } catch {    db = "down";  }  res.json({    status: db === "ok" ? "ok" : "down",    version: process.env.GIT_SHA ?? "dev",    checks: [{ name: "database", status: db, latencyMs: Math.round(performance.now() - started) }],  });});// last middleware — report, then keep normal error handling:app.use((err, _req, res, _next) => {  canary.error("unhandled_error", { message: String(err) });  res.status(500).json({ error: "internal error" });});

FastAPI

main.pypy
import os, time, httpxfrom fastapi import FastAPI, Requestfrom fastapi.responses import JSONResponseapp = FastAPI()INGEST = "https://ingest.canaryhub.ai/api/v1/events"KEY = "ch_live_YOUR_KEY_HERE"@app.get("/api/health")async def health():    started = time.monotonic()    db = "ok"    try:        await database.execute("SELECT 1")  # your DB client here    except Exception:        db = "down"    latency_ms = round((time.monotonic() - started) * 1000)    return {        "status": "ok" if db == "ok" else "down",        "version": os.environ.get("GIT_SHA", "dev"),        "checks": [{"name": "database", "status": db, "latencyMs": latency_ms}],    }@app.exception_handler(Exception)async def report_error(request: Request, exc: Exception):    # report is capped at 2 s; a monitoring outage must never take down a request    try:        async with httpx.AsyncClient(timeout=2) as client:            await client.post(                INGEST,                headers={"Authorization": f"Bearer {KEY}"},                json={"events": [{"type": "error", "name": type(exc).__name__, "message": str(exc)[:2000]}]},            )    except Exception:        pass    return JSONResponse(status_code=500, content={"error": "internal error"})

Replace ch_live_YOUR_KEY_HERE with a real key — sign up free and create one under API keys.

2 · Verify it worked

Deploy, then open yourapp.com/api/health in a browser — you should see the JSON above. Events show up live in your dashboard.

Different language? Anything that can serve JSON and make an HTTP POST can do this — the full contract is in the HTTP API docs.

← all platforms