-
Notifications
You must be signed in to change notification settings - Fork 0
Overview
Piro is an open-source uptime monitoring and status page platform. It continuously runs checks against your services, computes their health, and publishes the results to a public-facing status page — giving your users real-time visibility into your infrastructure.
Most monitoring tools are either too heavy (full observability stacks) or too simple (single-URL ping services). Piro is designed to sit in between: a self-hosted, production-grade uptime monitor with a clean public status page, multi-region support, and flexible alerting — without requiring a complex setup.
The core idea is simple: define what you're monitoring, let Piro run the checks, and trust that your users are seeing accurate status.
A Service represents anything you want to monitor — a web app, an API, a database, a third-party dependency, or any infrastructure component. Services are what your users see on the public status page.
A service doesn't have a fixed status. Its status is computed from the checks attached to it — you never set it manually. If all checks are passing, the service is UP. If any check fails, the service reflects that accordingly.
Services can also depend on each other. If Service A depends on Service B and B goes down, A's status can automatically reflect that degradation.
A Check is a single monitoring probe attached to a service. It runs on a configurable cron schedule and tests one specific thing: can we reach this URL? Does this DNS record resolve correctly? Is this TCP port open?
Each check has a type that determines how it runs:
| Type | What it tests |
|---|---|
| HTTP | HTTP/HTTPS endpoint availability and response |
| DNS | DNS record resolution (A, AAAA, CNAME) |
| TCP | TCP port connectivity |
| Ping | ICMP reachability |
| SSL | TLS certificate validity and expiry |
| Heartbeat | Passive — waits for your service to ping Piro |
| GCP Cloud Run Job | Last execution status of a GCP Cloud Run Job |
A service can have multiple checks. For example, you might have an HTTP check on your API's /health endpoint and a DNS check on your domain — both feeding into the same service's status.
Every check execution produces one of the following statuses:
| Status | Meaning |
|---|---|
UP |
The check passed — the service is healthy |
DEGRADED |
The check passed, but with elevated latency or partial failure |
DOWN |
The check failed — the service is unreachable or returning errors |
MAINTENANCE |
The service is in a scheduled maintenance window |
NO_DATA |
No data available yet (new check, or default status while waiting for first result) |
FAILURE |
The check executor itself crashed (misconfiguration, internal error) — not a service outage; only visible in the admin panel |
FAILURE is intentionally hidden from the public status page. If a check is misconfigured (e.g. an empty host, invalid name server, missing integration), it shows as NO_DATA to your users rather than falsely reporting a service outage.
A service's status is automatically computed from all its checks using a priority order:
MAINTENANCE > DOWN > DEGRADED > UP > NO_DATA
The highest-priority status among all checks wins. So if one check is DOWN and three others are UP, the service is DOWN.
Services can declare dependencies on each other, forming a directed acyclic graph (DAG). When an upstream service degrades, the dependent service's status can automatically reflect that — without having to configure duplicate checks.
There are three propagation modes:
| Mode | Behavior |
|---|---|
| Blocking | Full status cascades — if upstream is DOWN, dependent becomes DOWN
|
| SoftBlocking | Status cascades but caps at DEGRADED — downstream never goes DOWN due to this dependency alone |
| Advisory | Visual indicator only — no status impact |
An Alert Config defines a threshold rule for a check: "fire a notification if this check has been DOWN for 3 consecutive minutes." You configure:
-
What metric to watch:
Status,Latency, orUptime - The threshold value: a status name, a latency in ms, or an uptime percentage
- Failure threshold: how many consecutive failures before the alert fires
- Recovery threshold: how many consecutive successes before the alert auto-resolves
Alerts are stateful — once fired, they don't re-fire until they recover and trigger again. Alerts are also suppressed when a check returns FAILURE (executor error), since that's not a real outage.
A Notification Channel is a delivery endpoint for alerts. When an alert fires or recovers, Piro dispatches a message to all channels linked to that alert config.
Supported channel types:
Email · Telegram · Twilio SMS · Opsgenie · Pushover · Ntfy
Channels can be marked as global (auto-linked to all new alert configs) or locked (cannot be removed by regular users).
⚠️ Not currently supported: Webhook, Slack, Discord, Google Chat, and Microsoft Teams exist as trigger types in the codebase but are not registered as active dispatchers — selecting them will not deliver any notification. See Triggers for details.
An Incident is a manually declared service disruption. When something goes wrong that checks alone don't fully capture, you create an incident to communicate the situation to your users via the status page.
Incidents track an investigation state machine:
Investigating → Identified → Monitoring → Resolved
Each state transition is logged as a comment, creating a timestamped audit trail. Incidents are private by default — nothing is shown on the public status page until someone explicitly publishes it.
Incidents can affect specific services or be marked as global (affecting all services). Each affected service gets an impact level (DOWN, DEGRADED, etc.) that influences what users see.
Alerts never auto-create or auto-attach to incidents. Linking an Alert to an incident (new or existing) is always an explicit action taken from the Alerts panel. See Concepts-Alerts and Concepts-Incidents for details.
A Maintenance window declares planned downtime for one or more services. Maintenance windows:
- Suppress alerts during the window (no false notifications)
- Inject synthetic
MAINTENANCEdata points into the time series (preserving uptime history accuracy) - Support recurring schedules via iCalendar RRULE (e.g. "every Monday at 02:00")
- Show up on the public status page so users know downtime is expected
By default, Piro runs checks in-process within the API. For multi-region setups, you can connect additional Worker instances in different geographic locations.
Workers connect to the API via SignalR. When a check has multi-region enabled, it dispatches simultaneously to all connected workers. Results from each worker are stored independently (per-worker, per-minute) and then aggregated to compute the final check status.
This lets you detect regional outages: if your API is reachable from Europe but not from the US, Piro can reflect that as DEGRADED rather than ignoring the partial failure.
Piro has two distinct interfaces:
Public Status Page (apps/web) — The Next.js status page your users see. It shows service health, uptime history, active incidents, and upcoming maintenance. It is fully public and requires no authentication. FAILURE statuses are hidden; check-level detail is optional per service.
Admin Panel (apps/admin) — The React SPA for your team. It exposes the full configuration: services, checks, alert configs, notification channels, incidents, maintenance windows, workers, integrations, and users. All internal statuses including FAILURE are visible here.