TempsTemps
  • Docs
  • Blog
  • Pricing
  • Enterprise
  • Security
  • Contact
Star—
TempsTemps

Open-source deployment platform with built-in error tracking, analytics, and monitoring. Runs on any VPS. No surprise bills, no data leaving your infrastructure.

  • Product
  • Features
  • Documentation
  • Changelog
  • Enterprise
  • Contact
  • Resources
  • Getting Started
  • Upgrade
  • GitHub
  • Reddit
  • Tools
  • VPS Security Scanner
  • PaaS Tax Calculator
  • Compare
  • vs Vercel
  • vs Netlify
  • vs Coolify
  • All Platforms
  • Deploy
  • Next.js
  • Node.js
  • Django
  • Laravel
  • Go
  • Rust
  • All Frameworks →
  • Legal & Compliance
  • Security & Trust
  • Data Ownership & Privacy
  • GDPR Compliance

© 2026 Temps. All rights reserved.

GitHubDocs
t
Temps

30-40% of Support Tickets During Outages Ask 'Is It Down?' — Add a Status Page

30-40% of Support Tickets During Outages Ask 'Is It Down?' — Add a Status Page

March 12, 2026 (4mo ago)

Temps Team

Written by Temps Team

Last updated March 12, 2026 (4mo ago)

Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

#status-page#incident-management#uptime#monitoring#self-hosted#status page without statuspage
Back to all posts

Adding a status page to your app takes less than an afternoon using open-source tools — and it immediately cuts support ticket volume by 30-40% during outages. Statuspage.io charges $29/month for a single page; this guide shows you the free paths: build it yourself in ~80 lines of code, use Gatus or Upptime, or deploy Temps and get a status page included automatically at no extra cost.

TL;DR: A status page reduces support tickets by 30-40% during outages. You can build one with health check endpoints, a polling aggregator, and a static HTML page — roughly 80 lines of code. Or use an open-source tool like Gatus or Upptime. Temps includes a status page per project with automatic health checks every 60 seconds, no extra setup needed.


How Do You Add a Status Page to Your App?

The fastest path is four steps: expose a /health endpoint, run a polling script every 60 seconds, write results to a JSON file, and serve a static HTML page from a separate host. The whole thing fits in about 80 lines of code across three files.

If you want something pre-built, Gatus and Upptime are the two open-source options worth reaching for. Both are actively maintained, free to self-host, and require no custom code.

If you want a status page that is automatically created for every deployment — with zero configuration — Temps includes one as part of its deployment platform. Deploy an app, and the status page exists.


Why Does Every App Need a Status Page?

Teams with public status pages resolve incidents 20% faster on average, according to PagerDuty's State of Digital Operations report. A status page isn't just about transparency — it directly reduces the operational cost of every outage you'll ever have.

It Kills Support Ticket Volume

When your app goes down, users panic. They can't tell if it's their network, their browser, or your server. So they file tickets. Every single one of those tickets costs you time — reading, triaging, responding with "we're aware of the issue."

A status page short-circuits that entire loop. Users check the page, see the incident banner, and wait. Your support queue stays manageable. Your on-call engineer stays focused on actually fixing the problem instead of answering emails.

Enterprise Customers Expect It

If you sell to businesses, you'll hit SLA requirements sooner than you think. SOC 2 audits ask about incident communication procedures. Enterprise procurement teams check whether you have a public status page before signing contracts.

According to Gartner, 78% of enterprise IT buyers consider vendor transparency during outages a key evaluation criterion. A status page is often the simplest way to check that box.

Users Check Status Before Filing Bugs

Have you ever received a bug report that was actually just a service outage? It happens constantly. Without a status page, users can't distinguish between "the app is broken for everyone" and "something is wrong on my end."

A visible status indicator saves both sides time. Users self-triage. Your engineering team gets fewer false bug reports. Everyone wins.


What Should a Good Status Page Show?

A well-designed status page covers five elements. According to Pingdom's downtime survey, 92% of users expect real-time status updates during outages. Meeting that expectation requires more than a green checkmark.

Component-Level Status

Don't show a single "up" or "down" indicator for your entire app. Break it into components: API, web app, database, authentication, CDN, background jobs. Users need to know which parts are affected.

Three states work well: Operational, Degraded Performance, and Major Outage. Some teams add a fourth — Partial Outage — for situations where only some regions or user segments are impacted.

Incident Timeline with Updates

Every incident needs a timeline. When did it start? What's being done? When was it resolved? Post updates every 20-30 minutes during active incidents, even if the update is "still investigating."

The worst thing you can do is post "we're investigating" and go silent for two hours. Users assume you've forgotten about them. Frequent updates — even without new information — signal that the team is actively working.

Uptime Percentage Over 90 Days

Show a rolling 90-day uptime percentage per component. This gives users context. Is this a rare blip on a 99.99% track record, or is it the third outage this month on a service struggling to hit 99.5%?

A simple bar chart with daily uptime works well. Green bars for clean days, yellow for degraded, red for outages. GitHub's status page does this effectively.

Scheduled Maintenance Windows

Proactive communication matters as much as reactive. Post maintenance windows at least 48 hours in advance. Include the expected duration, which components are affected, and whether users will experience downtime.

Response Time Graph

A latency graph for the past 24-72 hours shows users whether performance has been degrading. If your API response time crept from 100ms to 800ms over four hours before the outage, that context helps users understand what happened.


Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

How Does the Architecture Work?

A status page system has five parts: health check endpoints, an aggregator, status computation, a public page, and a notification system. According to the Stack Overflow Developer Survey, 62% of professional developers manage some form of infrastructure monitoring. Yet many still cobble together ad-hoc solutions instead of building proper health check infrastructure.

Health Check Endpoints

Every service in your stack needs a /health endpoint. This isn't a simple return 200 — a good health check verifies that the service can actually do its job.

For an API server, that means checking database connectivity, cache availability, and external service reachability. For a background worker, it means verifying the job queue is connected and processing. A health endpoint that always returns 200 is worse than no health endpoint — it gives you false confidence.

// Good health check response
{
  "status": "healthy",
  "checks": {
    "database": { "status": "up", "latency_ms": 3 },
    "redis": { "status": "up", "latency_ms": 1 },
    "storage": { "status": "up", "latency_ms": 12 }
  },
  "version": "2.4.1",
  "uptime_seconds": 847293
}

The Aggregator

The aggregator is a service (or cron job) that polls each health endpoint at regular intervals — typically every 30-60 seconds. It records the response status, latency, and any error details.

Keep the polling interval consistent. Irregular checks create noisy data. And always poll from outside your infrastructure — checking health from the same server that runs the service doesn't tell you much about actual user reachability.

Status Computation

Raw health check data needs processing before it becomes useful status information. A single failed check shouldn't flip a component to "Major Outage" — network blips happen. Most systems use a threshold: repeated consecutive failures trigger a status change, and recovery requires multiple consecutive successes to prevent flapping.

The Public Page

The public-facing status page should be hosted separately from your main application. If your app is down, your status page needs to still be accessible. Many teams host the status page on a separate subdomain (status.yourapp.com) with a different hosting provider or CDN.

Notification System

Subscribers should be able to opt in for email, webhook, or Slack notifications. When a component status changes, the system fires notifications to all subscribers for that component. Keep it simple — status change events in, notifications out.


Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

How Do You Build a Status Page from Scratch?

Building a minimal status page takes roughly 80 lines of code and an afternoon. The Node.js ecosystem alone has over 2,000 packages related to health checks on npm. But you don't need any of them for a basic setup.

We've found that the simplest status page implementations — a polling script, a JSON file, and a static HTML page — tend to be the most reliable. Every dependency you add is another thing that can break during an outage, which is exactly when you need the status page to work.

Step 1: Add Health Check Endpoints

Start with a /health route in your application. Here's a Node.js/Express example that checks database and Redis connectivity:

app.get('/health', async (req, res) => {
  const checks = {};

  // Check database
  try {
    const start = Date.now();
    await db.query('SELECT 1');
    checks.database = { status: 'up', latency_ms: Date.now() - start };
  } catch (err) {
    checks.database = { status: 'down', error: err.message };
  }

  // Check Redis
  try {
    const start = Date.now();
    await redis.ping();
    checks.redis = { status: 'up', latency_ms: Date.now() - start };
  } catch (err) {
    checks.redis = { status: 'down', error: err.message };
  }

  const allUp = Object.values(checks).every(c => c.status === 'up');
  res.status(allUp ? 200 : 503).json({
    status: allUp ? 'healthy' : 'unhealthy',
    checks,
    timestamp: new Date().toISOString()
  });
});

Step 2: Build the Aggregator

A simple polling script runs on a schedule, checks each endpoint, and writes results to a JSON file or database:

const SERVICES = [
  { name: 'API', url: 'https://api.yourapp.com/health' },
  { name: 'Web App', url: 'https://yourapp.com/health' },
  { name: 'Worker', url: 'https://worker.yourapp.com/health' },
];

async function pollServices() {
  const results = [];

  for (const service of SERVICES) {
    try {
      const start = Date.now();
      const res = await fetch(service.url, { signal: AbortSignal.timeout(5000) });
      results.push({
        name: service.name,
        status: res.ok ? 'operational' : 'degraded',
        latency_ms: Date.now() - start,
        checked_at: new Date().toISOString()
      });
    } catch (err) {
      results.push({
        name: service.name,
        status: 'down',
        error: err.message,
        checked_at: new Date().toISOString()
      });
    }
  }

  // Write to JSON file (or insert into database)
  await fs.writeFile('./status.json', JSON.stringify(results, null, 2));
}

// Run every 60 seconds
setInterval(pollServices, 60_000);
pollServices();

Step 3: Serve the Status Page

The simplest approach is a static HTML file that fetches the JSON data and renders it. Host this on a separate server or CDN — it needs to work even when your main app is down.

<!DOCTYPE html>
<html>
<head>
  <title>Status - YourApp</title>
  <style>
    .status-item { padding: 12px; margin: 8px 0; border-radius: 6px; }
    .operational { background: #d4edda; color: #155724; }
    .degraded { background: #fff3cd; color: #856404; }
    .down { background: #f8d7da; color: #721c24; }
  </style>
</head>
<body>
  <h1>System Status</h1>
  <div id="services"></div>
  <script>
    fetch('/status.json')
      .then(r => r.json())
      .then(services => {
        const el = document.getElementById('services');
        el.innerHTML = services.map(s =>
          `<div class="status-item ${s.status}">
            <strong>${s.name}</strong>: ${s.status}
            ${s.latency_ms ? `(${s.latency_ms}ms)` : ''}
          </div>`
        ).join('');
      });
  </script>
</body>
</html>

That's roughly 80 lines across three files. It won't win any design awards, but it works. Run the aggregator as a systemd service or cron job, deploy the HTML to a CDN, and you've got a functional status page.


What Open-Source Status Page Tools Exist?

The open-source ecosystem offers several mature alternatives to building from scratch. GitHub Topics lists over 300 repositories tagged "status-page". Here are the ones actually worth using.

Upptime — GitHub-Powered Monitoring

Upptime runs entirely on GitHub Actions. No server required. It stores uptime data in the repository itself, uses GitHub Issues for incidents, and deploys a static status page via GitHub Pages.

Why would you want this? Zero infrastructure cost. GitHub Actions gives you the compute, GitHub Pages gives you the hosting, and the repository itself becomes your database. The tradeoff is that you're limited by GitHub Actions quotas and can't customize beyond what the templating system offers.

Gatus — The Developer's Choice

Gatus is a single Go binary configured entirely through YAML. Define your endpoints, set alerting conditions, and run it. It supports HTTP, TCP, DNS, ICMP, and even SSH health checks out of the box.

What sets Gatus apart is its condition language. You can write checks like [STATUS] == 200 && [BODY].status == 'healthy' && [RESPONSE_TIME] < 500. That's more expressive than most monitoring tools at any price point.

Statping-ng — Full-Featured Web UI

Statping-ng is the successor to Statping (which, like Cachet, went unmaintained for a while). It includes a web dashboard, multiple notification integrations (Slack, Discord, email, Telegram), and a REST API.

It's heavier than Gatus — it needs a database (SQLite, PostgreSQL, or MySQL) — but gives you more out of the box. If you want a polished web interface without building one, Statping-ng is the closest to a drop-in Statuspage.io replacement.

Cstate — Hugo-Based Static Pages

Cstate generates a static status page using Hugo. You define incidents and component status in Markdown files, and Hugo builds a fast, CDN-friendly static site. It's the lightest option — no backend, no database, no running service.

The downside: incident updates are manual. You create a Markdown file for each incident, which is fine for small teams but doesn't scale if you want automated status changes based on health checks.

Most teams start with a simple tool like Upptime or Cstate and graduate to something like Gatus once they hit 10+ services. The mistake is starting with a heavy tool before you know what you actually need to monitor. Pick the lightest option that covers your current requirements.

Comparison: Temps vs Statuspage.io vs Gatus

FeatureTempsStatuspage.ioGatus
PriceFree (self-host) or ~$6/mo cloud$29+/moFree (self-host)
LicenseApache 2.0Proprietary SaaSApache 2.0
SetupZero config — auto-created per deploymentManual configurationYAML config file
Check intervalEvery 60 secondsEvery 1-5 minutesConfigurable (30s+ typical)
Retry logicUp to 3 retries with exponential backoffN/AConfigurable retries
Incident trackingAutomatic + manualManualNo built-in incidents
Deployment platformBuilt-in (git push)NoneNone
Analytics, error trackingBuilt-inNoneNone
Separate hosting for status pageYes (Temps infrastructure)Yes (Atlassian CDN)Requires separate deploy

Open-Source Tool Comparison

ToolLanguageDatabaseAuto-DetectionUISetup Time
UpptimeTypeScriptGitHub repoYes (Actions)Static (Pages)~15 min
GatusGoSQLite/PostgresYes (polling)Built-in web~10 min
Statping-ngGoSQLite/Postgres/MySQLYes (polling)Full dashboard~30 min
CstateHugoNone (Markdown)No (manual)Static site~20 min
DIYAnyAnyCustomCustom~2 hours

How Does Temps Handle Status Pages?

Temps includes a built-in status page for every project — no extra services, no configuration files, no separate hosting. According to Datadog's State of Monitoring report, the average organization uses 7.4 monitoring tools. Temps collapses the status page into your existing deployment platform so you don't add yet another tool to that number.

Automatic Monitor Creation per Environment

When you create a new environment in Temps, a monitor is automatically created for it. No configuration required. The StatusPagePlugin listens for EnvironmentCreated events on the job queue and calls ensure_monitor_for_environment — so by the time your first deployment completes, monitoring is already running.

Monitors are automatically removed when environments are deleted. There's no manual cleanup.

Health Checks Every 60 Seconds

The HealthCheckService scheduler runs all active monitors in parallel (up to 10 concurrent checks) every 60 seconds. Each check uses the public-facing URL of your deployment — not the internal container network — so the check reflects what real users experience.

If your .temps.yaml config specifies a health_check_path, Temps uses that path on successful deployments. Otherwise it falls back to /health for monitors configured as type health, or the root URL for web monitors.

Failed checks retry up to 3 times with exponential backoff (100ms → 200ms → 400ms) before recording a failure. Timeout-only failures retry; connection-refused failures record immediately (retrying on a crashed container adds noise without any chance of success).

HTTP Status Code Mapping

Temps maps HTTP response codes to four status levels:

  • Operational: 2xx, 404, or 405 — the server is running (404 and 405 are common on API backends that return those for GET /)
  • Degraded: 4xx client errors (excluding 404/405)
  • Major Outage: 5xx server errors or connection failure
  • Partial Outage: Triggered by active major incidents

Incident Tracking

Temps creates incident records automatically when health checks detect a problem. Each incident logs the start time, affected components, status transitions, and resolution time. You can also create manual incidents for planned maintenance or partial outages that automated checks don't catch.

Incidents are surfaced on the public status page immediately. Users see what happened, when it was detected, and the current resolution status.

Public Status URL

Every project gets a status page that shows component health, uptime percentage, active incidents, and recent check history. The status page runs on Temps infrastructure — independent of your application containers — so it stays available even when your app is down. That's the key requirement most DIY solutions get wrong: hosting your status page on the same server as your app defeats the purpose.

To install Temps:

curl -fsSL https://temps.sh/install.sh | bash

Temps is free to self-host (Apache 2.0). Temps Cloud runs on Hetzner at ~$6/month with no per-seat fees.


Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

FAQ

How often should health checks run?

Every 30-60 seconds is the sweet spot for most applications. More frequent checks (every 5-10 seconds) generate excessive load and noisy data without meaningfully faster detection. The Uptime Institute found that 76% of outages are detected within 2 minutes with 30-second polling intervals. Temps checks every 60 seconds, which strikes the balance between responsiveness and load. If you need sub-minute detection, you're looking at push-based health checks instead of polling.

Should the status page be hosted separately from the main app?

Yes, always. If your application server goes down and your status page lives on the same server, users see nothing — which is worse than showing a "Major Outage" banner. Host your status page on a separate provider, CDN, or static hosting service. GitHub Pages, Cloudflare Pages, or a separate VPS in a different region all work. Temps handles this automatically: the status page runs on Temps infrastructure, separate from your application containers.

Do I need a status page if I already have uptime monitoring?

Uptime monitoring tells you when something breaks. A status page tells your users. They serve different audiences. Your monitoring tool alerts your on-call engineer at 3am. Your status page prevents 200 support tickets from landing in your inbox while that engineer is fixing the problem. You need both.

What's the minimum viable status page?

A static HTML page that shows component status and updates manually via git commits. That's it. Cstate does exactly this with Hugo. You don't need automated health checks, subscriber notifications, or historical charts on day one. Start with manual updates, add automation when the manual process becomes painful. Most teams hit that point around 5-10 incidents per quarter.

How does Temps handle connection failures vs HTTP errors?

Temps distinguishes between connection failures and HTTP errors. A connection refused error records a failure immediately — there's no point retrying a crashed container. A timeout retries up to 3 times. HTTP 5xx errors retry up to 3 times with exponential backoff starting at 100ms. This avoids false alarms from transient network blips while still catching real outages quickly.


Stop Leaving Your Users in the Dark

A status page is one of those things that seems optional until your first major outage. Then it becomes the difference between "we handled that well" and "why didn't anyone tell us the app was down?"

You've got three paths. Build it yourself with the code examples above — it's an afternoon of work and you'll understand exactly how it fits together. Pick an open-source tool like Gatus or Upptime if you want something battle-tested without the maintenance burden. Or use a platform that includes status pages alongside your deployment pipeline, so monitoring isn't another tool in your stack.

Temps gives you automatic status pages for every environment, health checks running every 60 seconds from a separate infrastructure layer, and incident tracking built in — all as part of a deployment platform that replaces Vercel, Sentry, PostHog, and Pingdom with a single Rust binary.

curl -fsSL https://temps.sh/install.sh | bash