GitHub Β· Telegram Β· LinkedIn Β· Live Demo Β· Quick Start
PulseWatch is a self-hosted full-stack uptime monitoring platform for developers who want to know when their services break before users notice.
It continuously probes your endpoints, tracks uptime history, detects repeated failures, automatically opens and resolves incidents, sends alerts through multiple channels, and provides a public status page you can share with users.
PulseWatch also uses OpenRouter-powered AI to analyze the monitoring evidence collected when an incident occurs and generate a concise, plain-English explanation of the observed failure pattern.
| π Instant Alerts | π Live Dashboard | π Public Status | π€ AI Analysis |
|---|---|---|---|
| Telegram, Email, Discord, Slack, Webhooks | Real-time KPIs, uptime %, response time | Shareable status board with 3 themes | OpenRouter-powered incident analysis |
| Notifications β | Dashboard β | Status Pages β | AI Explanations β |
git clone https://github.com/Robibiruk/PulseWatch.git
cd PulseWatchcd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
nano .envAt minimum, configure:
DATABASE_URL=postgresql+asyncpg://...
SECRET_KEY=<long-random-secret>
CORS_ORIGINS=http://localhost:5173Start FastAPI:
uvicorn main:app --reload --port 8000The API runs at:
http://localhost:8000
Interactive API documentation:
http://localhost:8000/docs
Open another terminal:
cd frontend
npm install
npm run devOpen:
http://localhost:5173
Register an account and add your first monitor.
Login JWT authentication + GitHub OAuth |
Dashboard Real-time KPIs + monitor fleet |
Public Status Page Auth-free shareable board with neon, light, and minimal themes |
|
| Category | What you get |
|---|---|
| HTTP Monitoring | HTTP/HTTPS checks, configurable intervals, timeouts, redirects, HTTP methods, authentication, and status-code rules |
| Heartbeat Monitoring | Push-based monitoring for cron jobs, workers, and background services |
| SSL Monitoring | SSL/TLS error detection and certificate expiry monitoring |
| Domain Monitoring | Domain expiry reminders |
| Alerts | Telegram, Email, Discord, Slack, and generic JSON webhooks |
| Dashboard | Real-time KPIs, uptime percentage, response times, filters, and monitor fleet |
| Incidents | Automatic incident creation, recovery detection, duration tracking, and incident history |
| AI Analysis | OpenRouter-powered incident explanations based on actual monitoring evidence |
| Status Pages | Public, authentication-free status pages with three visual themes |
| Telegram Bot | /status, /monitors, /incidents, /pause, /resume, and /help |
| Authentication | Email/password, JWT authentication, bcrypt password hashing, GitHub OAuth, and API tokens |
| Monitor Configuration | Timeout, redirects, IP version, authentication, HTTP method, accepted status codes, and tags |
PulseWatch currently runs as a self-hosted production deployment on an Azure Linux VM.
The frontend is deployed separately and communicates with the FastAPI backend running on the Azure VM.
The Azure VM provides the persistent compute required for:
- FastAPI
- The continuous monitoring worker
- Telegram long-polling
- Incident processing
- Notification dispatch
The production deployment uses a fresh PostgreSQL database for persistent application and monitoring data.
Neon is not used by the current production deployment.
OpenRouter is connected to the backend for AI-powered incident analysis.
@startuml
skinparam backgroundColor #0a0a0a skinparam componentStyle rectangle
rectangle "Vercel\nReact + Vite + TypeScript" as FE
rectangle "Azure Linux VM" { component "FastAPI API" as API component "Monitoring Worker" as Worker database "PostgreSQL\nProduction Database" as DB component "Telegram Bot" as TG }
component "OpenRouter\nGPT-OSS-20B" as AI component "Notifications" as N
FE --> API : REST + JWT API --> DB Worker --> DB Worker --> TG Worker --> AI Worker --> N
@enduml
The Azure VM is the persistent compute layer of the current production deployment.
Unlike a sleeping application instance, the VM provides a continuously running environment for the monitoring engine.
The VM currently hosts:
Azure Linux VM
βββ FastAPI backend
βββ Continuous monitoring worker
βββ Telegram bot
βββ PostgreSQL
This allows PulseWatch to continuously monitor external services without depending on a sleeping free-tier worker.
Scheduler
β
Find due monitors
β
Claim monitor
β
HTTP / HTTPS / Heartbeat probe
β
Save check result
β
Update monitor state
β
Failure threshold reached?
βββ No β schedule confirmation check
β
βββ Yes
β
Create incident
β
Analyze monitoring evidence
β
OpenRouter AI explanation
β
Send notifications
The monitoring scheduler uses database-backed monitor claims.
For PostgreSQL deployments, due monitors use row-level locking with SKIP LOCKED. A short lease prevents another worker from processing the same monitor simultaneously.
This provides a foundation for horizontal worker scaling without producing duplicate checks or duplicate alerts.
| Component | Host | Purpose |
|---|---|---|
| Frontend | Vercel | React + Vite web application |
| Backend API | Azure Linux VM | FastAPI REST API |
| Monitoring Worker | Azure Linux VM | Continuous uptime checks and incident detection |
| Telegram Bot | Azure Linux VM | Long-polling commands and Telegram alerts |
| Database | PostgreSQL | Persistent application and monitoring data |
| AI | OpenRouter | Incident explanation and analysis |
| Resend | Email notifications | |
| Notifications | Discord / Slack / Webhooks | Additional alert channels |
The production backend is hosted on an Azure Linux virtual machine.
Azure provides the persistent compute environment for PulseWatch's API, monitoring worker, Telegram bot, and production database.
The VM is particularly important for PulseWatch because uptime monitoring requires a process that stays alive continuously.
The architecture therefore avoids depending on a sleeping application instance for the core monitoring loop.
The current deployment uses a fresh PostgreSQL database.
It is not connected to Neon.
The database stores:
- User accounts
- Monitors
- Historical checks
- Incidents
- AI explanations
- Telegram connections
- Notification settings
- Public status pages
- API tokens
The React frontend is deployed separately and communicates with the Azure-hosted API through:
VITE_API_BASE=https://your-api-domainThe Azure VM runs the backend services required for PulseWatch:
Azure VM
β
βββ FastAPI API
βββ Monitoring Worker
βββ Telegram Bot
βββ PostgreSQL
A production process manager such as systemd can be used to keep services alive and automatically restart them after failures or reboots.
PulseWatch uses OpenRouter to analyze the evidence collected when a monitor enters an incident state.
The AI is not responsible for detecting outages. The monitoring engine detects the outage first.
The flow is:
Monitor failure
β
Failure threshold reached
β
Incident created
β
Monitoring evidence collected
β
OpenRouter
β
AI explanation
β
Incident alert
openai/gpt-oss-20b
The model is configured through:
OPENROUTER_MODEL=openai/gpt-oss-20bWhen an incident is created, PulseWatch can provide evidence such as:
- Current HTTP status code
- Current error
- Recent HTTP status codes
- Failure pattern
For example:
Current HTTP status: 503
Current error: Service unavailable
Recent HTTP status codes: [200, 200, 503, 503, 503]
The model can recognize the observed transition:
200 β 200 β 503 β 503 β 503
and explain that the monitored service was previously responding successfully before returning consecutive 503 responses.
The AI explanation is evidence-based, not a magical root-cause detector.
PulseWatch does not currently provide the model with:
- Application logs from the monitored service
- CPU metrics from the monitored service
- Memory metrics
- Database internals
- Server process information
- Cloud infrastructure metrics
Therefore, the AI should not claim that it knows the exact root cause.
For example, a sequence of 503 responses may be consistent with:
- Application failure
- Resource exhaustion
- Dependency failure
- Temporary service unavailability
These are possible explanations, not confirmed facts unless the monitoring evidence supports them.
For:
HTTP 503
Error: Service unavailable
Recent: [200, 200, 503, 503, 503]
the AI may produce an explanation identifying:
- The currently observed
503failure. - The transition from successful responses to consecutive failures.
- Plausible underlying causes.
- What evidence is missing.
- A practical recovery estimate.
This makes the AI output useful while avoiding false certainty.
If OpenRouter is unavailable, misconfigured, or no API key is provided, PulseWatch falls back to deterministic rule-based explanations.
Supported fallback conditions include:
- HTTP 500
- HTTP 502
- HTTP 503
- HTTP 504
- Connection failures
- DNS failures
- Request timeouts
The monitoring engine and incident system therefore continue functioning even when the AI service is unavailable.
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
(required) | PostgreSQL async URL or SQLite development URL |
SECRET_KEY |
dev-insecure-change-me |
JWT signing secret |
CORS_ORIGINS |
http://localhost:5173 |
Comma-separated allowed browser origins |
| Variable | Default | Description |
|---|---|---|
POLL_INTERVAL |
15 |
Scheduler tick interval in seconds |
WORKER_CONCURRENCY |
20 |
Maximum concurrent monitor checks |
NO_WORKER |
false |
Disable the built-in worker |
FAILURE_THRESHOLD |
3 |
Consecutive failures required before opening an incident |
CONFIRMATION_DELAY |
10 |
Delay before confirmation/recovery checks |
| Variable | Default | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
"" |
Telegram bot token from BotFather |
NO_TELEGRAM_BOT |
false |
Disable the Telegram bot |
| Variable | Default | Description |
|---|---|---|
RESEND_API_KEY |
"" |
Resend API key |
ALERT_FROM_EMAIL |
alerts@yourdomain.com |
Verified sender address |
| Variable | Default | Description |
|---|---|---|
OPENROUTER_API_KEY |
"" |
OpenRouter API key |
OPENROUTER_MODEL |
openai/gpt-oss-20b |
Model used for incident analysis |
| Variable | Default | Description |
|---|---|---|
VITE_API_BASE |
http://localhost:8000 |
Backend API base URL |
Full configuration is available in
backend/.env.example.
All authenticated routes require:
Authorization: Bearer <jwt>| Method | Path | Description |
|---|---|---|
POST |
/auth/register |
Register account |
POST |
/auth/token |
Login and obtain JWT |
GET |
/auth/me |
Get current user |
GET |
/monitors |
List monitors |
POST |
/monitors |
Create monitor |
PATCH |
/monitors/:id |
Update monitor |
DELETE |
/monitors/:id |
Delete monitor |
GET |
/monitors/summary |
Monitor fleet KPIs |
GET |
/monitors/incidents |
Recent incidents |
GET |
/status/:userId |
Public status board |
POST |
/api/heartbeat/:token |
Send heartbeat |
GET |
/status/health |
API health check |
POST |
/api/platform/tokens |
Create API token |
POST |
/api/platform/account/password |
Change password |
Interactive Swagger documentation:
http://localhost:8000/docs
PulseWatch's Telegram bot uses long-polling, so no webhook configuration is required.
| Command | Description |
|---|---|
/start <token> |
Connect Telegram to your PulseWatch account |
/status |
Show monitor status |
/monitors |
List monitors |
/incidents |
Show recent incidents |
/pause |
Pause alerts |
/resume |
Resume alerts |
/help |
Show available commands |
Dashboard
β
Settings
β
Connect Telegram
β
Open Telegram deep link
β
Press Start
β
Account linked
PulseWatch can send incident and recovery notifications through:
- Telegram
- Discord
- Slack
- Generic JSON webhooks
When a monitor reaches the failure threshold:
Monitor failure
β
Failure threshold reached
β
Incident created
β
AI explanation generated
β
Notification dispatched
When the monitor recovers:
Service recovers
β
Incident resolved
β
Recovery duration calculated
β
Recovery notification sent
HTTP monitors support:
- Configurable check interval
- Request timeout
- HTTP methods
- Redirect following
- IPv4 / IPv6 / automatic selection
- Basic authentication
- Bearer authentication
- Accepted status-code groups
- SSL/TLS checks
- SSL certificate expiry reminders
- Domain expiry reminders
- Response-time tracking
Heartbeat monitoring is designed for services that cannot be continuously probed through a normal HTTP endpoint.
Your application sends a request to:
POST /api/heartbeat/:token
If PulseWatch does not receive a heartbeat within the expected interval, the monitor is considered down and an incident can be opened.
This is useful for:
- Cron jobs
- Background workers
- Scheduled scripts
- Data pipelines
- Internal services
PulseWatch uses a failure threshold to reduce false alarms.
With the default configuration:
Failure #1 β no incident
Failure #2 β no incident
Failure #3 β incident opened
A temporary one-request failure therefore does not immediately trigger an outage alert.
When the service starts responding again:
Service recovers
β
Incident resolved
β
Recovery duration calculated
β
Recovery notification sent
Before exposing a deployment publicly:
- Set
SECRET_KEYto a long random value - Set
CORS_ORIGINSto the real frontend domain - Never use
CORS_ORIGINS=*in production - Use PostgreSQL for production
- Keep
.envout of Git - Rotate API keys if they are accidentally exposed
- Keep
TELEGRAM_BOT_TOKENprivate - Keep
OPENROUTER_API_KEYprivate - Put the API behind HTTPS
- Use a reverse proxy such as Nginx or Caddy
- Add rate limiting to authentication and public endpoints
- Keep the Azure VM and system packages updated
- Configure automatic service restart with
systemd - Restrict unnecessary Azure VM inbound ports
- Configure PostgreSQL backups
- Monitor the PulseWatch worker itself
Generate a strong secret with:
python3 -c "import secrets; print(secrets.token_urlsafe(48))"PulseWatch/
βββ backend/
β βββ main.py # FastAPI app + lifespan
β βββ worker.py # Monitoring scheduler and incident engine
β βββ checker.py # HTTP, SSL and domain checks
β βββ telegram_bot.py # Telegram bot + account linking
β βββ notifications.py # Alert dispatch and notification formatting
β βββ emailer.py # HTML email templates
β βββ ai_explain.py # OpenRouter AI incident analysis
β βββ models.py # SQLAlchemy ORM models
β βββ schemas.py # Pydantic schemas
β βββ database.py # Database engine and initialization
β βββ config.py # Application configuration
β βββ requirements.txt # Python dependencies
β βββ routers/
β βββ auth.py
β βββ monitors.py
β βββ status.py
β βββ telegram.py
β βββ heartbeat.py
β βββ statuspage.py
β βββ notifications.py
β βββ platform.py
β
βββ frontend/
β βββ src/
β β βββ App.tsx
β β βββ api.ts
β β βββ auth.tsx
β β βββ components/
β β βββ pages/
β βββ vite.config.ts
β
βββ docs-site/
β βββ docs/
β βββ blog/
β
βββ docs/
β βββ diagrams/
β βββ screenshots/
β
βββ README.md
- Alembic migrations
- Durable job queue
- Second-region confirmation
- Rate limiting
- Better worker supervision
- Monitoring worker health
- Automated PostgreSQL backups
- Incident acknowledgement
- Maintenance windows
- Escalation policies
- Monitor tags and groups
- Longer-term SLA charts
- Team accounts and RBAC
- More notification integrations
- Docker / Docker Compose deployment
- Automated Azure deployment
- Automated PostgreSQL backup and recovery
- Multi-region monitoring
- Distributed workers
Current
β
βββ Azure Linux VM
βββ PostgreSQL
βββ 1 monitoring worker
βββ Continuous monitoring
β
βΌ
Next
β
βββ Durable queue
βββ Multiple workers
βββ Better failure isolation
β
βΌ
Future
β
βββ Multiple regions
βββ Distributed workers
βββ Large-scale monitoring


