Pongo separates your configuration (what you customize) from the application code (the platform itself). This guide explains how to organize your monitoring setup.
Directory Overview
pongo/ # Your configuration (version-controlled)
├── monitors/ # Monitor definitions (*.ts)
│ ├── api.ts
│ ├── database.ts
│ ├── cdn.ts
│ └── index.ts # Register all monitors
├── dashboards/ # Dashboard configs (*.ts)
│ ├── production.ts
│ ├── staging.ts
│ └── index.ts # Register all dashboards
├── channels.ts # Webhook notification channels
├── announcements/ # Status announcements (*.md)
│ ├── maintenance.md
│ └── new-features.md
└── incidents/ # Incident reports (*.md)
├── outage-2024.md
└── degradation.md
src/ # Application code (don't modify unless contributing)
├── app/ # Next.js App Router
│ ├── page.tsx # Homepage
│ ├── monitors/ # Monitor pages
│ ├── dashboards/ # Dashboard pages
│ ├── alerts/ # Alert management
│ ├── shared/ # Public status pages
│ └── api/ # API routes
├── scheduler/ # Standalone monitor runner
│ ├── index.ts # Entry point
│ ├── engine.ts # Execution engine
│ └── alert.ts # Alert evaluation
├── archiver/ # S3 data archival service
├── components/ # React UI (Radix UI + Tailwind)
├── db/ # Drizzle ORM schema
│ ├── schema.ts # Database tables
│ └── index.ts # Database client
├── lib/ # Core business logic
│ ├── config-types.ts # Type definitions for monitors/dashboards
│ ├── loader.ts # Load configs from filesystem
│ ├── data.ts # Data queries
│ └── types.ts # Shared types
└── proxy.ts # iron-session auth middleware
The pongo/ Configuration Directory
This is where you define your monitoring setup. Everything in this directory is version-controlled and deployed with your application.
Monitors (pongo/monitors/)
Each TypeScript file exports a monitor configuration using the monitor() function.
Example structure:
pongo/monitors/
├── api.ts # API health check
├── database.ts # Database connectivity
├── cdn.ts # CDN performance
├── external-services.ts # Third-party dependencies
└── index.ts # Export all monitors
Basic monitor file:
import { monitor } from "../../src/lib/config-types" ;
export default monitor ({
name: "API Health" ,
interval: "5m" ,
timeout: "30s" ,
async handler () {
const start = Date . now ();
const res = await fetch ( "https://api.example.com/health" );
return {
status: res . ok ? "up" : "down" ,
responseTime: Date . now () - start ,
statusCode: res . status ,
};
} ,
}) ;
Register in index.ts:
import api from "./api" ;
import database from "./database" ;
import cdn from "./cdn" ;
export default {
api ,
database ,
cdn ,
} ;
The key you use when exporting (e.g., api, database) becomes the monitor ID referenced in dashboards and alerts.
Dashboards (pongo/dashboards/)
Dashboards group monitors and can be made public for status pages.
Example structure:
pongo/dashboards/
├── production.ts # Production services
├── staging.ts # Staging environment
├── external.ts # External dependencies
└── index.ts # Export all dashboards
Dashboard file:
pongo/dashboards/production.ts
import type { DashboardConfig } from "@/lib/config-types" ;
export default {
name: "Production" ,
slug: "production" ,
public: true ,
slaTarget: 99.9 ,
monitors: [ "api" , "database" , "cdn" ] ,
} satisfies DashboardConfig ;
Register in index.ts:
pongo/dashboards/index.ts
import production from "./production" ;
import staging from "./staging" ;
export default {
production ,
staging ,
} ;
Public dashboards are accessible at /shared/[slug] without authentication. Private dashboards are at /dashboards/[id] and require login if ACCESS_CODE is set.
Channels (pongo/channels.ts)
Define webhook endpoints for alert notifications.
import { channels } from "../src/lib/config-types" ;
export default channels ({
slack: {
type: "webhook" ,
url: process . env . SLACK_WEBHOOK_URL ! ,
} ,
pagerduty: {
type: "webhook" ,
url: "https://events.pagerduty.com/v2/enqueue" ,
headers: {
Authorization: `Token token= ${ process . env . PAGERDUTY_TOKEN } ` ,
},
} ,
discord: {
type: "webhook" ,
url: process . env . DISCORD_WEBHOOK_URL ! ,
} ,
}) ;
Store webhook URLs in environment variables, not directly in code. Add them to .env and reference them via process.env.
Announcements (pongo/announcements/)
Markdown files with frontmatter for scheduled maintenance, feature launches, or general updates.
File structure:
pongo/announcements/
├── scheduled-maintenance.md
├── new-features.md
└── performance-improvements.md
Example file:
pongo/announcements/scheduled-maintenance.md
---
dashboard : production
title : Scheduled Maintenance
type : maintenance
createdAt : "2026-03-10T00:00:00Z"
expiresAt : "2026-03-11T06:00:00Z"
---
We will be performing database maintenance on **March 10th, 2026** from 2:00 AM to 4:00 AM UTC.
During this window, you may experience brief interruptions to API requests. No action is required on your part.
Frontmatter fields:
Field Type Description dashboardstring Dashboard slug this announcement belongs to titlestring Announcement title type"info" | "maintenance" | "update"Type badge shown in UI createdAtISO 8601 date When the announcement was created expiresAtISO 8601 date (optional) When to automatically hide
Announcements are sorted by createdAt (newest first) and automatically hidden after expiresAt.
Incidents (pongo/incidents/)
Markdown files with frontmatter for incident reports and postmortems.
File structure:
pongo/incidents/
├── payment-gateway-outage.md
├── cdn-degradation.md
└── database-connectivity-issues.md
Example file:
pongo/incidents/payment-gateway-outage.md
---
dashboard : production
title : Payment Gateway Outage
severity : critical
status : resolved
affectedMonitors :
- payment-api
- checkout
createdAt : "2026-02-10T06:00:00Z"
resolvedAt : "2026-02-10T09:45:00Z"
---
## Investigating - Feb 10, 06:00 UTC
Payment gateway is completely unreachable. All payment requests are timing out.
## Identified - Feb 10, 07:15 UTC
The issue has been identified as a network partition between our datacenter and the payment provider.
## Monitoring - Feb 10, 08:30 UTC
Network connectivity restored. Payment gateway is coming back online.
## Resolved - Feb 10, 09:45 UTC
Payment gateway is fully operational. All pending transactions have been processed.
## Root Cause
Upstream network provider experienced a routing issue that affected connectivity to the payment gateway. Issue was resolved by the provider.
Frontmatter fields:
Field Type Description dashboardstring Dashboard slug this incident belongs to titlestring Incident title severity"critical" | "warning" | "info"Severity level status"investigating" | "identified" | "monitoring" | "resolved"Current status affectedMonitorsstring[] (optional) List of affected monitor IDs createdAtISO 8601 date When the incident started resolvedAtISO 8601 date (optional) When the incident was resolved
Use Markdown headings with timestamps to create an incident timeline. This provides a chronological view of events.
The src/ Application Directory
This contains the Pongo platform code. You typically don’t need to modify these files unless you’re contributing to the project.
Key Subdirectories
src/app/ - Next.js pages and API routes
Uses the App Router (React Server Components)
Authentication via iron-session middleware
Public routes: /, /shared/*, /login, /api/*
Protected routes: /monitors, /dashboards, /alerts, /settings
src/scheduler/ - Monitor execution engine
Standalone Bun process
Loads monitors from pongo/monitors/
Executes handlers on schedule
Evaluates alert conditions
Dispatches webhooks with retry logic
HTTP API (port 3001) for manual triggers
src/archiver/ - Data archival service
Archives old check results to S3 or local storage
Converts to Parquet format
Day-based partitioning (year=YYYY/month=MM/day=DD/)
Configurable retention and batch size
src/components/ - React UI components
Built with Radix UI primitives
Styled with Tailwind CSS
Charts with Recharts
Reusable across pages
src/db/ - Database layer
Drizzle ORM schema definitions
Auto-detects SQLite or PostgreSQL from connection string
Migrations in drizzle/ directory
src/lib/ - Core utilities
config-types.ts - Type definitions for monitor(), channels(), etc.
loader.ts - Loads configs from pongo/ directory
data.ts - Database queries for checks, alerts, monitors
types.ts - Shared TypeScript types
File Organization Best Practices
Organizing Monitors
Group monitors logically:
pongo/monitors/
├── internal/
│ ├── api.ts
│ ├── database.ts
│ └── index.ts
├── external/
│ ├── stripe.ts
│ ├── sendgrid.ts
│ └── index.ts
└── index.ts # Import from subdirectories
Root index.ts:
import internal from "./internal" ;
import external from "./external" ;
export default {
... internal ,
... external ,
} ;
Environment-Specific Configs
Use environment variables to switch behavior:
export default monitor ({
name: "API" ,
interval: process . env . NODE_ENV === "production" ? "1m" : "5m" ,
async handler () {
const url = process . env . API_URL || "https://api.example.com" ;
const res = await fetch ( url );
// ...
} ,
}) ;
Shared Monitor Logic
Extract common patterns:
pongo/monitors/_helpers.ts
export function httpCheck ( url : string ) {
return async () => {
const start = Date . now ();
const res = await fetch ( url );
return {
status: res . ok ? "up" : "down" ,
responseTime: Date . now () - start ,
statusCode: res . status ,
};
};
}
import { monitor } from "../../src/lib/config-types" ;
import { httpCheck } from "./_helpers" ;
export default monitor ({
name: "API" ,
interval: "5m" ,
handler: httpCheck ( "https://api.example.com" ) ,
}) ;
Incident Templates
Create templates for common incidents:
pongo/incidents/_template.md
---
dashboard :
title :
severity :
status : investigating
affectedMonitors : []
createdAt :
resolvedAt :
---
## Investigating - [ timestamp ]
[Description of the issue]
## Identified - [ timestamp ]
[Root cause identified]
## Monitoring - [ timestamp ]
[Fix applied, monitoring recovery]
## Resolved - [ timestamp ]
[Issue resolved, normal operations restored]
## Root Cause
[Detailed root cause analysis]
## Action Items
- [ ] [Follow-up task 1]
- [ ] [Follow-up task 2]
Configuration Loading
Pongo loads all configuration files at runtime:
Monitors - Imported from pongo/monitors/index.ts
Dashboards - Imported from pongo/dashboards/index.ts
Channels - Imported from pongo/channels.ts
Announcements - Read from pongo/announcements/*.md (parsed with gray-matter)
Incidents - Read from pongo/incidents/*.md (parsed with gray-matter)
Changes to monitors, dashboards, or channels require restarting the scheduler. Announcements and incidents are loaded on each request and don’t require a restart.
Next Steps
Monitors Learn how to write effective monitor handlers
Alerts Configure alerts with conditions and channels
Dashboards Create dashboards and public status pages
Environment Variables Explore all configuration options