Skip to main content

Guide

OpenStatus vs Better Stack vs Instatus 2026

Compare OpenStatus, Better Stack, and Instatus for status pages and uptime monitoring. Incident management, synthetic monitoring, API checks, and which.

·PkgPulse Team·
0

TL;DR

OpenStatus is the open-source status page and monitoring platform — synthetic monitoring, status pages, incident management, API checks, self-hostable, built with Next.js. Better Stack (formerly Better Uptime) is the all-in-one observability platform — uptime monitoring, status pages, incident management, on-call scheduling, log management, built for modern DevOps teams. Instatus is the fast, beautiful status page — quick setup, incident communication, integrations, focused purely on status communication. In 2026: OpenStatus for open-source status monitoring, Better Stack for full observability with uptime, Instatus for the fastest beautiful status page setup.

Key Takeaways

  • OpenStatus: Open-source, synthetic monitoring, status pages, self-hostable
  • Better Stack: All-in-one — uptime + logs + on-call + status pages
  • Instatus: Fastest setup — beautiful status pages in minutes
  • OpenStatus is free and open-source (Next.js, Cloudflare Workers)
  • Better Stack combines uptime monitoring with log management and incident response
  • Instatus has the most polished status page design out of the box

OpenStatus

OpenStatus — open-source monitoring:

Setup monitoring

// OpenStatus API — create a monitor:
const response = await fetch("https://api.openstatus.dev/v1/monitor", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse API",
    url: "https://api.pkgpulse.com/health",
    periodicity: "5m",  // Check every 5 minutes
    regions: ["iad", "sfo", "ams", "hkg"],  // Multi-region
    method: "GET",
    headers: [{ key: "Accept", value: "application/json" }],
    assertions: [
      { type: "status", compare: "eq", target: 200 },
      { type: "latency", compare: "lt", target: 2000 },  // < 2s
    ],
  }),
})

const monitor = await response.json()
console.log(`Monitor created: ${monitor.id}`)

Status page configuration

// Create/update status page:
const statusPage = await fetch("https://api.openstatus.dev/v1/page", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "PkgPulse Status",
    slug: "pkgpulse",
    description: "Current status of PkgPulse services",
    monitors: [1, 2, 3],  // Monitor IDs to display
    customDomain: "status.pkgpulse.com",
    icon: "https://pkgpulse.com/favicon.ico",
  }),
})

// Report an incident:
await fetch("https://api.openstatus.dev/v1/incident", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "API Degraded Performance",
    status: "investigating",  // investigating | identified | monitoring | resolved
    monitors: [1],
    message: "We're investigating increased latency on the API.",
  }),
})

Synthetic monitoring checks

// Advanced monitor with assertions:
await fetch("https://api.openstatus.dev/v1/monitor", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse Search API",
    url: "https://api.pkgpulse.com/search?q=react",
    periodicity: "10m",
    regions: ["iad", "ams"],
    method: "GET",
    assertions: [
      { type: "status", compare: "eq", target: 200 },
      { type: "latency", compare: "lt", target: 1000 },
      { type: "header", key: "content-type", compare: "contains", target: "application/json" },
    ],
    notifications: [
      { type: "email", target: "ops@pkgpulse.com" },
      { type: "slack", target: "https://hooks.slack.com/..." },
    ],
    degradedAfter: 3,   // Mark degraded after 3 failures
    timeout: 10000,      // 10s timeout
  }),
})

// Query monitor results:
const results = await fetch(
  "https://api.openstatus.dev/v1/monitor/1/result?limit=50",
  { headers: { "x-openstatus-key": process.env.OPENSTATUS_API_KEY! } }
)

const data = await results.json()
data.forEach((result: any) => {
  console.log(`${result.region}: ${result.latency}ms — ${result.statusCode}`)
})

Better Stack

Better Stack — all-in-one observability:

Uptime monitoring

// Better Stack Uptime API — create a monitor:
const response = await fetch("https://uptime.betterstack.com/api/v2/monitors", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    monitor_type: "status",
    url: "https://api.pkgpulse.com/health",
    pronounceable_name: "PkgPulse API",
    check_frequency: 180,  // Every 3 minutes (seconds)
    regions: ["us", "eu", "as", "au"],
    request_timeout: 15,
    confirmation_period: 3,  // Confirm down for 3 checks
    http_method: "get",
    expected_status_codes: [200],
    request_headers: [
      { name: "Accept", value: "application/json" },
    ],
    // Alert policies:
    policy_id: "policy-123",
    follow_redirects: true,
    verify_ssl: true,
    ssl_expiration: 30,  // Alert 30 days before SSL expiry
  }),
})

const monitor = await response.json()
console.log(`Monitor: ${monitor.data.id}${monitor.data.attributes.url}`)

Incident management

// Create an incident:
const incident = await fetch("https://uptime.betterstack.com/api/v2/incidents", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    requester_email: "ops@pkgpulse.com",
    name: "Database Connection Issues",
    summary: "PostgreSQL connections timing out, causing API errors",
    description: "Investigating connection pool exhaustion on the primary database",
    call: true,     // Trigger phone call to on-call
    sms: true,      // Send SMS
    email: true,    // Send email
    push: true,     // Push notification
  }),
})

// Update incident timeline:
await fetch(`https://uptime.betterstack.com/api/v2/incidents/${incident.data.id}/timeline`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Identified root cause: connection pool limit too low. Scaling up.",
  }),
})

// Resolve incident:
await fetch(`https://uptime.betterstack.com/api/v2/incidents/${incident.data.id}/resolve`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
})

Status page and on-call

// Create status page:
await fetch("https://uptime.betterstack.com/api/v2/status-pages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    company_name: "PkgPulse",
    company_url: "https://pkgpulse.com",
    subdomain: "pkgpulse",  // pkgpulse.betteruptime.com
    custom_domain: "status.pkgpulse.com",
    timezone: "America/New_York",
    subscribable: true,  // Allow email subscriptions
    hide_from_search_engines: false,
  }),
})

// Set up on-call schedule:
await fetch("https://uptime.betterstack.com/api/v2/on-call-calendars", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse Engineering On-Call",
    default_calendar_attributes: {
      on_call_users: [
        { id: "user-1", weekday_from: "monday", weekday_to: "friday" },
        { id: "user-2", weekday_from: "saturday", weekday_to: "sunday" },
      ],
    },
  }),
})

// Heartbeat monitor (for cron jobs):
// POST https://uptime.betterstack.com/api/v1/heartbeat/{heartbeat_id}
// If no ping received within the expected period → alert
await fetch(`https://uptime.betterstack.com/api/v1/heartbeat/${HEARTBEAT_ID}`)

Log management

// Send logs to Better Stack (Logtail):
import Logtail from "@logtail/node"

const logtail = new Logtail(process.env.LOGTAIL_TOKEN!)

// Structured logging:
logtail.info("Package data synced", {
  packages_count: 150,
  duration_ms: 2340,
  source: "npm-registry",
})

logtail.error("API request failed", {
  url: "/api/packages/react",
  status: 500,
  error: "Database connection timeout",
  request_id: "req-abc-123",
})

// Flush logs (important before process exit):
await logtail.flush()

// Express middleware for request logging:
import { LogtailTransport } from "@logtail/winston"
import winston from "winston"

const logger = winston.createLogger({
  transports: [new LogtailTransport(logtail)],
})

app.use((req, res, next) => {
  const start = Date.now()
  res.on("finish", () => {
    logger.info("HTTP Request", {
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration_ms: Date.now() - start,
    })
  })
  next()
})

Instatus

Instatus — beautiful status pages:

Status page setup

// Instatus API — create a status page:
const response = await fetch("https://api.instatus.com/v1/pages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse",
    subdomain: "pkgpulse",  // pkgpulse.instatus.com
    status: "UP",
    logoUrl: "https://pkgpulse.com/logo.png",
    faviconUrl: "https://pkgpulse.com/favicon.ico",
    websiteUrl: "https://pkgpulse.com",
    language: "en",
    useLargeHeader: true,
    subscribable: true,
  }),
})

const page = await response.json()
const pageId = page.id

Components and incidents

// Add components (services to monitor):
const component = await fetch(`https://api.instatus.com/v1/${pageId}/components`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "API",
    description: "PkgPulse REST API",
    status: "OPERATIONAL",  // OPERATIONAL | DEGRADEDPERFORMANCE | PARTIALOUTAGE | MAJOROUTAGE | UNDERMAINTENANCE
    order: 1,
    showUptime: true,
    grouped: false,
  }),
})

// Add more components:
const components = [
  { name: "Website", description: "Main website", order: 2 },
  { name: "Search", description: "Package search engine", order: 3 },
  { name: "CDN", description: "Static asset delivery", order: 4 },
]

for (const comp of components) {
  await fetch(`https://api.instatus.com/v1/${pageId}/components`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ ...comp, status: "OPERATIONAL", showUptime: true }),
  })
}

// Create an incident:
const incident = await fetch(`https://api.instatus.com/v1/${pageId}/incidents`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Search Index Rebuild Delay",
    message: "Search results may be stale while the index rebuilds.",
    components: [component.id],
    started: new Date().toISOString(),
    status: "INVESTIGATING",  // INVESTIGATING | IDENTIFIED | MONITORING | RESOLVED
    notify: true,  // Notify subscribers
    statuses: [
      {
        id: component.id,
        status: "DEGRADEDPERFORMANCE",
      },
    ],
  }),
})

Incident updates and integrations

// Update incident with progress:
await fetch(`https://api.instatus.com/v1/${pageId}/incidents/${incident.id}/incident-updates`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    message: "Root cause identified — index shard allocation issue. Rebalancing in progress.",
    status: "IDENTIFIED",
    started: new Date().toISOString(),
    notify: true,
    statuses: [
      { id: component.id, status: "DEGRADEDPERFORMANCE" },
    ],
  }),
})

// Resolve incident:
await fetch(`https://api.instatus.com/v1/${pageId}/incidents/${incident.id}/incident-updates`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    message: "Search index rebuild complete. All services operational.",
    status: "RESOLVED",
    started: new Date().toISOString(),
    notify: true,
    statuses: [
      { id: component.id, status: "OPERATIONAL" },
    ],
  }),
})

// Schedule maintenance:
await fetch(`https://api.instatus.com/v1/${pageId}/incidents`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Scheduled Database Maintenance",
    message: "We'll be performing routine database maintenance. Brief downtime expected.",
    status: "NOTSTARTEDYET",
    notify: true,
    started: "2026-03-15T02:00:00Z",
    components: [component.id],
    statuses: [{ id: component.id, status: "UNDERMAINTENANCE" }],
  }),
})

// Webhook integration (receive Instatus events):
// POST /api/webhooks/instatus
app.post("/api/webhooks/instatus", (req, res) => {
  const { incident, page } = req.body

  if (incident.status === "RESOLVED") {
    // Post to Slack, update internal dashboards, etc.
    notifySlack(`✅ ${incident.name} resolved on ${page.name}`)
  }

  res.sendStatus(200)
})

Feature Comparison

FeatureOpenStatusBetter StackInstatus
Open-source
Self-hosted
Status pages
Uptime monitoring✅ (synthetic)✅ (multi-protocol)❌ (3rd party)
Incident management
On-call scheduling
Log management✅ (Logtail)
Heartbeat monitors
Multi-region checks
Custom domains
Subscriber notifications
Scheduled maintenance
API access
IntegrationsSlack, Discord, email100+ (Slack, PagerDuty, etc.)Slack, Discord, email
Free tierOpen-source (unlimited)10 monitors1 status page
PricingFree / Cloud plansFrom $29/monthFrom $20/month

When to Use Each

Use OpenStatus if:

  • Want an open-source, self-hostable monitoring solution
  • Need synthetic monitoring with multi-region checks
  • Prefer owning your monitoring infrastructure
  • Building on Cloudflare Workers / Next.js ecosystem

Use Better Stack if:

  • Need all-in-one observability (uptime + logs + on-call + status page)
  • Want on-call scheduling and escalation policies
  • Need log management alongside uptime monitoring
  • Building a DevOps workflow with incident response

Use Instatus if:

  • Need the fastest, most beautiful status page setup
  • Want a focused status communication tool (not full monitoring)
  • Need subscriber notifications and maintenance windows
  • Prefer simplicity over feature breadth

Incident Response Workflow and Mean Time to Resolution

The effectiveness of a status page platform is ultimately measured by how quickly your team detects and communicates incidents, and how the tooling supports that process. Better Stack has the most complete incident response workflow among the three: automatic incident creation from failed monitor checks, configurable escalation paths (phone call, SMS, email in sequence), on-call schedule management, and incident timeline tracking with status updates pushed to subscribers and integrated communication channels. The combination of uptime monitoring triggering an on-call alert, which triggers an incident, which auto-updates the status page, reduces the manual coordination load during a crisis significantly. OpenStatus and Instatus both require more manual intervention to connect monitoring alerts to status page incidents, though OpenStatus's API makes it straightforward to automate this connection from your existing alerting pipeline.

Self-Hosting and Data Sovereignty

OpenStatus's open-source architecture is its most distinctive production advantage for organizations with data sovereignty requirements or strict firewall policies. The project is built on Next.js and Cloudflare Workers, and the entire platform can be deployed to your own Cloudflare account with the infrastructure-as-code configuration provided in the repository. This means monitoring checks run from your own Cloudflare Workers deployment, and all incident data is stored in your own database, with no data leaving your infrastructure boundary. For regulated industries (healthcare, finance, government) where vendor data processing agreements are difficult to negotiate quickly, self-hosting OpenStatus is often the only viable path. Better Stack and Instatus are both pure SaaS with no self-hosting option, meaning all monitoring data and incident history is processed on their infrastructure under their standard data processing terms.

Synthetic Monitoring Depth and Check Types

OpenStatus's monitoring capabilities focus on HTTP endpoint checks with assertion-based pass/fail logic — you define what a healthy response looks like (status code, response time, header values) and OpenStatus continuously verifies it from multiple regions. This is sufficient for most web API and website monitoring use cases. Better Stack extends monitoring significantly beyond HTTP: TCP port checks, ping checks, keyword presence/absence in responses, and heartbeat monitors for cron job validation are all supported, making it appropriate for monitoring the full stack including non-HTTP services like databases and message queues (monitored at the TCP level). Instatus has no built-in synthetic monitoring and relies on third-party integrations (PagerDuty, Datadog, UptimeRobot) to trigger incidents, which works well if you already have a monitoring stack but adds complexity for teams that want a single-vendor solution for monitoring and status communication.

Custom Domain Configuration and SSL

Serving your status page on a custom domain (e.g., status.yourcompany.com) rather than a subdomain of the provider's platform is standard practice for enterprise products because it maintains brand consistency during incidents when customers are most anxious. All three platforms support custom domains, but the configuration process differs. OpenStatus's cloud version supports custom domains through CNAME configuration pointing to OpenStatus's edge network. For self-hosted deployments, the domain is simply where you deploy the application. Fathom — in the context of Instatus — the platform supports custom domains through a simple CNAME record in your DNS, and SSL certificate provisioning is handled automatically via Let's Encrypt without any manual certificate management. Better Stack's custom domain support includes automatic SSL provisioning and works with any DNS provider. For organizations with strict SSL requirements (specific certificate authorities, extended validation certificates, or wildcard certificate requirements), verifying that the platform's SSL provisioning approach meets those requirements should be part of the evaluation process.

Subscriber Notification and Communication

All three platforms support subscriber notifications for incidents and maintenance windows, but the notification options differ. Better Stack and Instatus both offer email subscriptions with opt-in pages where users can select which components they care about, reducing notification fatigue for users who only depend on a subset of your services. OpenStatus (in the cloud version) supports email notifications, and the self-hosted version's notification configuration depends on your email provider setup. Better Stack also supports SMS and Slack notifications to subscribers, and its enterprise tier includes custom webhook endpoints so subscribers can integrate status notifications into their own monitoring systems. For high-visibility public APIs where users rely on your status page as a source of truth during incidents, the quality of subscriber notifications and the reliability of notification delivery are often the deciding factors between platforms.

Integration Ecosystem and Alerting Context

The value of a status page platform scales with how well it integrates into your existing operational toolchain. Better Stack has the most extensive integration library — Datadog, New Relic, PagerDuty, Grafana, and 100+ other monitoring and communication tools can be connected as alert sources or notification destinations. This makes Better Stack particularly valuable for teams that already have Datadog or Grafana dashboards showing application metrics, because incidents on the status page can be automatically linked to the monitoring context that explains the root cause. OpenStatus integrates with Slack and Discord for notifications, and its API-first design means any monitoring tool that can make HTTP requests can trigger incident creation. Instatus similarly focuses on the core status communication use case and partners with monitoring vendors rather than competing with them, which keeps the product focused but limits the depth of automation possible without custom integration work.


Methodology

Feature comparison based on OpenStatus, Better Stack, and Instatus platforms and pricing as of March 2026.

Compare DevOps tooling and monitoring libraries on PkgPulse →

See also: AVA vs Jest and Pulumi vs SST vs CDKTF 2026, Coolify vs CapRover vs Dokku (2026).

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.