Self-Hosted BigAGI Monitoring — Uptime, TLS, and Database Health (Free)
BigAGI is a full-featured, self-hosted alternative to Claude.ai and ChatGPT. You get AI personas, multi-model comparison, voice chat, image generation, document analysis, and code execution — all under your own domain and data policy. But self-hosting means you're responsible for every layer of availability. When BigAGI goes down at midnight, your team finds out the hard way.
This guide adds external HTTP monitoring, heartbeat checks, TLS certificate expiry alerts, and database health monitoring — all on the free Vigilmon tier.
The failure modes self-hosted BigAGI users miss
Next.js app startup failure — BigAGI is a Next.js application that can fail to start after an update or config change. If the app crashes on startup, port 3000 goes silent. No health check endpoint means no alert.
LLM provider API outages — BigAGI routes requests to OpenAI, Anthropic, Ollama, and other backends. If an API key expires, a provider rate-limits you, or the provider itself goes down, your users see errors but your uptime monitor shows green.
TLS certificate expiry — Self-hosted deployments often manage their own certificates via Let's Encrypt or Caddy. Certificates expire, renewal automation fails, and users get browser security warnings. Certificate expiry is entirely preventable with monitoring.
PostgreSQL connectivity loss — When BigAGI is configured in database mode, a dropped database connection means conversations and personas are unavailable. The Next.js process may still be running and returning 200s for the shell, but user data is inaccessible.
Code execution sandbox downtime — BigAGI supports code execution via external sandboxes (e2b, Rooibos). These are network-dependent services that can become unreachable independently of the main BigAGI app.
Step 1: Verify BigAGI is serving on port 3000
BigAGI's Next.js frontend doesn't ship a dedicated /health route, but you can probe the root path or the API route to confirm the app is up.
The simplest approach: add a custom Next.js health API route.
// src/app/api/health/route.ts (Next.js App Router)
import { NextResponse } from 'next/server';
export const dynamic = 'force-dynamic';
async function checkDatabase(): Promise<{ ok: boolean; detail?: string }> {
// Only check database if DATABASE_URL is configured
if (!process.env.DATABASE_URL) {
return { ok: true, detail: 'not configured' };
}
try {
// Import your DB client — adjust for Prisma, postgres.js, etc.
const { db } = await import('@/lib/db');
await db.execute('SELECT 1');
return { ok: true };
} catch (e) {
return { ok: false, detail: String(e) };
}
}
async function checkLLMProvider(): Promise<{ ok: boolean; providers: Record<string, boolean> }> {
const providers: Record<string, boolean> = {};
// Check OpenAI if key is configured
if (process.env.OPENAI_API_KEY) {
try {
const res = await fetch('https://api.openai.com/v1/models', {
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
signal: AbortSignal.timeout(5000),
});
providers.openai = res.ok;
} catch {
providers.openai = false;
}
}
// Check local Ollama if configured
const ollamaUrl = process.env.OLLAMA_API_HOST || process.env.NEXT_PUBLIC_OLLAMA_API_HOST;
if (ollamaUrl) {
try {
const res = await fetch(`${ollamaUrl}/api/tags`, {
signal: AbortSignal.timeout(3000),
});
providers.ollama = res.ok;
} catch {
providers.ollama = false;
}
}
const allOk = Object.values(providers).every(Boolean);
return { ok: allOk, providers };
}
export async function GET() {
const [database, llm] = await Promise.all([
checkDatabase(),
checkLLMProvider(),
]);
const healthy = database.ok; // LLM degradation is soft — don't 503 on it
const status = healthy ? 'ok' : 'degraded';
return NextResponse.json(
{
status,
database,
llm,
uptime: process.uptime(),
},
{ status: healthy ? 200 : 503 }
);
}
Test it:
curl https://bigagi.yourdomain.com/api/health
# {"status":"ok","database":{"ok":true},"llm":{"ok":true,"providers":{"openai":true}},"uptime":3847}
If database is down:
{
"status": "degraded",
"database": { "ok": false, "detail": "Connection refused" },
"llm": { "ok": true, "providers": { "openai": true } }
}
Step 2: HTTP monitoring with Vigilmon
With the health endpoint live, set up external monitoring via Vigilmon:
- Sign up at vigilmon.online — free, no credit card
- Click New Monitor → HTTP
- Enter
https://bigagi.yourdomain.com/api/health - Set interval to 5 minutes
- Save
Add a second monitor for the main app URL:
| Monitor | URL | What it catches |
|---|---|---|
| Health endpoint | https://bigagi.yourdomain.com/api/health | DB down, startup failure |
| App root | https://bigagi.yourdomain.com/ | Reverse proxy failure, CDN issues |
Step 3: TLS certificate expiry monitoring
Self-hosted TLS certificates are the most commonly missed expiry in self-hosted stacks. Vigilmon monitors TLS expiry automatically when you set up an HTTPS monitor — you'll receive an alert 30 days before expiry.
To add an explicit certificate monitor:
- New Monitor → HTTP
- URL:
https://bigagi.yourdomain.com/ - Enable SSL certificate expiry alert (Vigilmon checks the cert on every probe)
- Set alert threshold to 30 days
You'll receive:
⚠️ SSL Certificate expiring in 14 days
Domain: bigagi.yourdomain.com
Expires: 2026-02-01
This fires even when the app is otherwise healthy, giving you time to renew before users see browser security warnings.
Step 4: Heartbeat monitoring for scheduled tasks
If you have cron jobs around BigAGI — nightly database backups, model cache warming, usage report generation — heartbeat monitors ensure they keep running.
// scripts/backup-db.ts
import https from 'https';
async function pingHeartbeat(url: string) {
return new Promise<void>((resolve) => {
https.get(url, () => resolve()).on('error', () => resolve());
});
}
async function backupDatabase() {
// Your backup logic here
await runPgDump();
// Ping only on success
const heartbeatUrl = process.env.BACKUP_HEARTBEAT_URL;
if (heartbeatUrl) {
await pingHeartbeat(heartbeatUrl);
console.log('Database backup complete, heartbeat sent');
}
}
backupDatabase().catch(console.error);
In Vigilmon:
- New Monitor → Heartbeat
- Set expected interval to 25 hours (nightly job with buffer)
- Copy the ping URL →
BACKUP_HEARTBEAT_URLin your environment
# .env
BACKUP_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/your-backup-token
Step 5: LLM provider API health monitoring
LLM provider outages are common and unpredictable. While you can't control provider availability, you can alert on it.
Add provider availability to your health check response (Step 1), then create a keyword-based check in Vigilmon:
For monitors where your plan supports keyword assertions, add:
- Assert response body contains:
"openai":true
Alternatively, create a separate lightweight probe that only checks provider connectivity:
// src/app/api/providers/route.ts
export async function GET() {
// ... same provider checks as in health route
return NextResponse.json({ providers }, { status: allOk ? 200 : 503 });
}
Create a Vigilmon HTTP monitor for this route with a degraded severity — provider outages are worth knowing but don't page your on-call engineer at 3 AM.
Step 6: Image generation and code sandbox health
If you've enabled image generation (DALL-E, Stable Diffusion) or code execution sandboxes (e2b), add probes for those too:
// Extend your health route
async function checkImageGeneration(): Promise<{ ok: boolean }> {
const dalleKey = process.env.OPENAI_API_KEY;
if (!dalleKey) return { ok: true };
// A lightweight models endpoint check confirms key validity
try {
const res = await fetch('https://api.openai.com/v1/models', {
headers: { Authorization: `Bearer ${dalleKey}` },
signal: AbortSignal.timeout(5000),
});
return { ok: res.ok };
} catch {
return { ok: false };
}
}
async function checkCodeSandbox(): Promise<{ ok: boolean }> {
const e2bKey = process.env.E2B_API_KEY;
if (!e2bKey) return { ok: true };
try {
const res = await fetch('https://api.e2b.dev/health', {
headers: { 'X-API-Key': e2bKey },
signal: AbortSignal.timeout(5000),
});
return { ok: res.ok };
} catch {
return { ok: false };
}
}
Include these in your /api/health response. Soft failures (image gen or sandbox down) should return 200 with a degraded status field — users can still chat even without these features.
Step 7: Slack and email alerts
Configure alert delivery in Vigilmon:
For Slack:
- Create an incoming webhook
- Notifications → New Channel → Slack → paste URL
- Enable on your BigAGI monitors
For email: Notifications → New Channel → Email
Alert examples:
🔴 DOWN: bigagi.yourdomain.com/api/health
Status: 503 degraded — database unreachable
PostgreSQL: Connection refused (localhost:5432)
⚠️ SSL Certificate expiring in 14 days: bigagi.yourdomain.com
🔴 MISSED: bigagi-db-backup heartbeat
Expected every: 25 hours | Last ping: 31 hours ago
Step 8: Public status page
If you run BigAGI for a team, give them visibility:
- Status Pages → New Status Page in Vigilmon
- Name it
BigAGI Service Status - Add your health endpoint, root URL, and certificate monitors
- Share the URL with users
When an incident fires, your users can check the status page themselves instead of messaging you.
Deployment checklist
# 1. Add health route to your BigAGI deployment
# 2. Configure environment variables
DATABASE_URL=postgresql://user:pass@localhost:5432/bigagi
OPENAI_API_KEY=sk-...
BACKUP_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/...
# 3. Verify health endpoint responds
curl https://bigagi.yourdomain.com/api/health
# 4. Set up Vigilmon monitors:
# - HTTP: /api/health (5 min)
# - HTTP: / root (5 min, TLS cert check)
# - Heartbeat: db backup (25 hour)
# 5. Configure Slack or email notifications
Summary
| Monitor | What it catches |
|---|---|
| HTTP /api/health | Next.js crash, DB connectivity loss |
| HTTP root (/) | Reverse proxy failure, routing issues |
| TLS cert check | Certificate expiry before users notice |
| DB backup heartbeat | Silent backup failure |
| Slack/email alerts | Immediate incident notification |
Self-hosting BigAGI gives you control. External monitoring gives you confidence that what you're running is actually working.