If you’re running self-hosted services, a status page is essential for communicating uptime and incidents to your users — or just monitoring your own infrastructure at a glance. Commercial status page services like StatusPage.io or Better Uptime charge premium prices, but several excellent self-hosted alternatives give you full control without recurring costs.

💡 This article contains affiliate links. If you buy through them, we earn a small commission at no extra cost to you. Learn more.

In this comprehensive guide, we’ll compare the three most popular self-hosted status page solutions in 2026: Cachet, Statusfy, and Gatus. Each takes a different approach to status page management, from full-featured incident tracking to minimalist static pages. We’ll cover features, setup, resource requirements, and help you choose the right solution for your needs.

What is a Status Page?

A status page is a public or private web page that displays the current operational status of your services. It typically shows:

  • Current status of each monitored service (operational, degraded, down)
  • Historical uptime percentages
  • Incident history with timelines and updates
  • Scheduled maintenance windows
  • Response time metrics (optional)
  • Subscribe options for email/RSS notifications

Status pages serve two distinct use cases:

Public-facing: For SaaS products, APIs, or any service with external users. Transparent communication during outages builds trust and reduces support tickets.

Internal/homelab: For monitoring your own infrastructure. A well-designed status page gives you immediate visibility into what’s working and what’s not, especially useful when accessed from mobile devices during troubleshooting.

Quick Comparison Table

FeatureCachetStatusfyGatus
TypeFull web appStatic site generatorWeb app
DatabaseMySQL/PostgreSQL/SQLiteNone (files)SQLite/PostgreSQL
Incident ManagementFull (create, update, resolve)Manual (edit markdown)Automatic (from monitoring)
Built-in MonitoringNo (displays data from external sources)NoYes (HTTP/TCP/DNS checks)
UI CustomizationHigh (themes, CSS)High (Vue.js components)Medium (config-based)
Resource UsageMedium-HighLow (just HTTP server)Low-Medium
Maintenance OverheadHigh (database, updates)Low (static files)Low (single binary)
Best ForPublic status pages with full incident workflowDeveloper-friendly static deploymentsIntegrated monitoring + status display
Active DevelopmentIntermittentStalled (2021)Very active

Cachet is a Laravel-based status page system that’s been around since 2015. It provides a complete incident management workflow and is the closest open-source equivalent to StatusPage.io.

Key Features

  • Incident Timeline: Create incidents with multiple status levels (investigating, identified, watching, fixed)
  • Components: Organize services into hierarchical component groups
  • Metrics: Display custom graphs (response times, error rates, etc.)
  • Subscriber Notifications: Email alerts for incidents and scheduled maintenance
  • API: Full REST API for automation and integrations
  • Multi-language: Translated into 20+ languages
  • Customizable Themes: Multiple built-in themes or custom CSS

Architecture

Cachet is a traditional PHP web application:

  • Backend: Laravel 8 framework
  • Database: MySQL, PostgreSQL, or SQLite
  • Frontend: Vue.js components
  • Web Server: Requires nginx or Apache with PHP-FPM

Setup with Docker

The easiest deployment method is using Docker Compose:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
version: '3.8'

services:
  cachet:
    image: cachethq/docker:latest
    container_name: cachet
    restart: unless-stopped
    ports:
      - "8000:8000"
    environment:
      - DB_DRIVER=pgsql
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_DATABASE=cachet
      - DB_USERNAME=cachet
      - DB_PASSWORD=your_secure_password
      - APP_KEY=base64:your_generated_key_here
      - APP_URL=https://status.yourdomain.com
      - APP_LOG=errorlog
      - APP_ENV=production
      - APP_DEBUG=false
    depends_on:
      - postgres
    volumes:
      - ./cachet-data:/var/www/html/storage

  postgres:
    image: postgres:15-alpine
    container_name: cachet-db
    restart: unless-stopped
    environment:
      - POSTGRES_DB=cachet
      - POSTGRES_USER=cachet
      - POSTGRES_PASSWORD=your_secure_password
    volumes:
      - ./postgres-data:/var/lib/postgresql/data

Generate an application key:

1
docker run --rm cachethq/docker:latest php artisan key:generate --show

After starting the containers, access http://localhost:8000 to complete the web-based setup wizard.

Integration with Monitoring Tools

Cachet doesn’t include built-in monitoring — it’s a status page display layer. You need to push data to it from external monitoring tools:

With Uptime Kuma:

  1. Install the Uptime Kuma Cachet plugin
  2. Configure Cachet API URL and token in Uptime Kuma
  3. Uptime Kuma will automatically update component status and create incidents

With Prometheus/Grafana: Use cachet-monitor to bridge Prometheus alerts to Cachet incidents.

Manual API Updates:

1
2
3
4
5
# Update component status
curl -X PUT https://status.yourdomain.com/api/v1/components/1 \
  -H "X-Cachet-Token: YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": 1}'  # 1=Operational, 2=Performance Issues, 3=Partial Outage, 4=Major Outage

Pros and Cons

Pros:

  • Most feature-complete open-source status page
  • Professional appearance suitable for business use
  • Excellent incident management workflow
  • Strong API for automation
  • Good documentation and community

Cons:

  • Higher resource requirements (PHP + database)
  • More complex to deploy and maintain
  • Development has been sporadic (long gaps between updates)
  • Some reported bugs in newer PHP versions
  • Overkill for simple homelab use

Best For: Public-facing status pages for businesses, SaaS products, or anyone needing full incident management features.

Statusfy: The Static Site Approach

Statusfy takes a unique approach: it’s a static site generator specifically for status pages. You define your system components and incidents in markdown files, then build the site into static HTML/CSS/JS that can be served from any web server or CDN.

Key Features

  • Static Generation: No database or backend required in production
  • JAMstack Architecture: Excellent performance and security
  • Multi-language Support: Built-in internationalization
  • Markdown-Based: All content in version-controllable markdown files
  • Automated Builds: Integrate with CI/CD pipelines
  • PWA Support: Can be installed as a progressive web app
  • Modern UI: Clean, responsive design

Architecture

Statusfy is built with:

  • Framework: Vue.js + Nuxt.js
  • Build Tool: Node.js-based generator
  • Runtime: Static files served by any HTTP server (nginx, Apache, Netlify, Vercel, etc.)
  • Storage: Markdown files in Git repository

Setup

Install Statusfy globally:

1
npm install -g statusfy

Create a new status page project:

1
2
statusfy init
cd my-statuspage

This generates a project structure:

my-statuspage/
├── .statusfy/          # Build output
├── content/            # Markdown content
│   └── incidents/      # Incident files
├── locales/            # Translations
├── public/             # Static assets
└── config.yml          # Configuration

Edit config.yml to define your components:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
title: "My Services Status"
description: "Status page for our self-hosted infrastructure"
baseUrl: "https://status.yourdomain.com"

content:
  frontMatterFormat: yaml
  systems:
    - id: web-services
      name: Web Services
    - id: api
      name: API Endpoints
    - id: databases
      name: Databases

Create an incident in content/incidents/2026-04-01-database-maintenance.md:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
---
title: Scheduled Database Maintenance
date: 2026-04-01T22:00:00.000Z
resolved: true
resolvedAt: 2026-04-01T23:30:00.000Z
severity: maintenance
affected:
  - databases
section: issue
---

We will be performing routine database maintenance tonight.

## Update - 23:30 UTC
Maintenance completed successfully. All services restored.

Build the static site:

1
statusfy generate

This creates production-ready static files in .statusfy/dist/. Deploy them to any web server:

1
2
3
4
5
6
# Simple nginx Docker deployment
docker run -d \
  --name status-page \
  -p 8080:80 \
  -v $(pwd)/.statusfy/dist:/usr/share/nginx/html:ro \
  nginx:alpine

Automation with CI/CD

The static nature makes Statusfy perfect for Git-based workflows:

  1. Commit incident markdown files to Git
  2. Push to GitHub/GitLab
  3. CI/CD pipeline rebuilds and deploys the site automatically

Example GitHub Actions workflow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
name: Deploy Status Page
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm install -g statusfy
      - run: statusfy generate
      - name: Deploy to hosting
        run: rsync -avz .statusfy/dist/ user@server:/var/www/status/

Pros and Cons

Pros:

  • Extremely lightweight in production (static files)
  • No database or backend security concerns
  • Fast page loads and excellent SEO
  • Version control for incidents (Git history)
  • Can be hosted on CDN for global performance
  • Minimal maintenance overhead

Cons:

  • Requires build step for every update (not real-time)
  • No built-in monitoring — purely display layer
  • Development stalled since 2021 (last release: v0.6.0)
  • Manual incident creation (no automatic detection)
  • Dependency updates may require migration work

Best For: Developers comfortable with Git workflows, projects prioritizing performance and security, or anyone who wants infrastructure-as-code for their status page.

Gatus: The Monitoring-First Solution

Gatus is a health dashboard and status page combined into one tool. Unlike Cachet and Statusfy, Gatus includes built-in monitoring — it actively checks your services and automatically updates the status page.

Key Features

  • Built-in Monitoring: HTTP, TCP, DNS, ICMP checks with extensive conditions
  • Real-Time Updates: WebSocket-powered live status display
  • Uptime Badges: Embeddable SVG badges for README files
  • Flexible Alerting: Slack, Discord, email, PagerDuty, webhook notifications
  • Metrics Storage: SQLite or PostgreSQL for historical data
  • Response Time Graphs: Visualize performance trends
  • Lightweight: Single Go binary, minimal resource usage
  • Dark Mode: Built-in theme switcher

Architecture

Gatus is impressively simple:

  • Language: Go (single static binary)
  • Database: Optional (SQLite for persistence, PostgreSQL for scale)
  • Frontend: Embedded in binary, served by built-in HTTP server
  • Configuration: Single YAML file

Setup with Docker

Create config.yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
storage:
  type: sqlite
  path: /data/gatus.db

web:
  port: 8080

alerting:
  discord:
    webhook-url: "https://discord.com/api/webhooks/..."
    default-alert:
      enabled: true
      description: "Status change detected"
      send-on-resolved: true

endpoints:
  - name: Main Website
    group: web
    url: "https://selfhostwise.com"
    interval: 60s
    conditions:
      - "[STATUS] == 200"
      - "[RESPONSE_TIME] < 1000"
    alerts:
      - type: discord

  - name: Nextcloud
    group: cloud
    url: "https://cloud.example.com/status.php"
    interval: 120s
    conditions:
      - "[STATUS] == 200"
      - "[BODY].installed == true"

  - name: PostgreSQL
    group: databases
    url: "tcp://postgres.local:5432"
    interval: 60s
    conditions:
      - "[CONNECTED] == true"

  - name: DNS Resolution
    group: infrastructure
    url: "8.8.8.8"
    dns:
      query-name: "selfhostwise.com"
      query-type: "A"
    conditions:
      - "[DNS_RCODE] == NOERROR"

Docker Compose deployment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
version: '3.8'

services:
  gatus:
    image: twinproduction/gatus:latest
    container_name: gatus
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - ./config.yaml:/config/config.yaml
      - ./gatus-data:/data

Start it:

1
docker-compose up -d

Access the status page at http://localhost:8080.

Advanced Monitoring Conditions

Gatus supports sophisticated health checks:

Check JSON response body:

1
2
3
4
5
6
- name: API Health
  url: "https://api.example.com/health"
  conditions:
    - "[STATUS] == 200"
    - "[BODY].status == healthy"
    - "[BODY].database.connected == true"

TLS certificate expiry:

1
2
3
4
- name: Certificate Validity
  url: "https://secure.example.com"
  conditions:
    - "[CERTIFICATE_EXPIRATION] > 720h"  # Alert if < 30 days

Custom headers and authentication:

1
2
3
4
5
6
- name: Authenticated Endpoint
  url: "https://api.example.com/private"
  headers:
    Authorization: "Bearer YOUR_TOKEN"
  conditions:
    - "[STATUS] == 200"

Public vs Private Status Page

Gatus supports a readonly mode for public access:

1
2
3
4
5
6
7
security:
  oidc:
    issuer-url: "https://auth.example.com"
    redirect-url: "https://status.example.com/authorization-code/callback"
    client-id: "gatus"
    client-secret: "your-secret"
    scopes: ["openid", "email"]

Or use a reverse proxy (Traefik, nginx) to:

  • Serve / publicly (status page)
  • Require authentication for /admin or /config

Pros and Cons

Pros:

  • All-in-one solution (monitoring + status page)
  • Real-time updates without external services
  • Extremely lightweight (single <20MB binary)
  • No complex setup or dependencies
  • Active development (regular releases)
  • Excellent for homelab use
  • Beautiful, modern UI

Cons:

  • Less suitable for public business status pages (simpler design)
  • No rich incident management (just status + timeline)
  • Configuration-only (no web UI for editing)
  • Fewer notification channels than dedicated monitoring tools
  • Limited historical data visualization compared to Grafana

Best For: Homelabs, internal dashboards, developers wanting simple monitoring + status in one tool, anyone prioritizing ease of setup and low maintenance.

Choosing the Right Solution

Here’s a decision framework based on your needs:

Choose Cachet if you:

  • Need a public-facing status page for a business or SaaS product
  • Want comprehensive incident management with updates and timelines
  • Already have monitoring tools (Uptime Kuma, Prometheus) and need a status display
  • Have users who expect professional incident communication
  • Don’t mind managing a PHP application and database

Choose Statusfy if you:

  • Prefer Git-based workflows and infrastructure-as-code
  • Want maximum performance and security (static files)
  • Have a CI/CD pipeline you can integrate with
  • Don’t need real-time updates (scheduled builds are fine)
  • Value simplicity in production (just static files + web server)

Choose Gatus if you:

  • Want monitoring and status page in a single tool
  • Prioritize ease of setup and minimal maintenance
  • Are building a homelab or internal infrastructure dashboard
  • Need real-time status updates
  • Don’t require elaborate incident management workflows
  • Want something lightweight and actively maintained

Hybrid Approaches

You’re not limited to one tool. Many self-hosters combine solutions:

Gatus + Cachet:

  • Use Gatus for monitoring and alerting
  • Push status updates to Cachet’s API for public-facing display
  • Best of both: Gatus’s simplicity + Cachet’s professional incident management

Statusfy + Uptime Kuma:

  • Uptime Kuma for real-time monitoring and alerts
  • Statusfy for a polished public status page
  • Manual workflow: When incidents occur, create markdown files and rebuild Statusfy

Gatus + Grafana:

  • Gatus for status page and basic health checks
  • Grafana for deep metrics and visualization
  • Complementary: Status at a glance + detailed analytics when needed

Hardware Requirements

All three solutions are relatively lightweight, but here’s what to expect:

Cachet:

  • CPU: 1-2 cores (PHP processing)
  • RAM: 512MB-1GB (Laravel + database)
  • Storage: 2GB+ (application + database growth)
  • Best on: Mid-range mini PCs or VPS with 2GB+ RAM

Statusfy:

  • Build time CPU: 2-4 cores (faster builds)
  • Build time RAM: 512MB-1GB (Node.js compilation)
  • Production CPU: Negligible (static files)
  • Production RAM: Negligible (static files)
  • Storage: 50-100MB (static assets)
  • Best on: Any web server, even a Raspberry Pi 4

Gatus:

  • CPU: <1 core (efficient Go runtime)
  • RAM: 50-200MB (depends on endpoint count)
  • Storage: 100MB-1GB (binary + SQLite data)
  • Best on: Raspberry Pi, low-end VPS, or any existing server with spare resources

My Recommendation

After running status pages for both public projects and homelab infrastructure, here’s what I recommend:

For homelabs: Start with Gatus. You get monitoring and status display in one tool, it’s trivially easy to set up, and maintenance is minimal. The configuration-as-code approach fits well with typical homelab workflows.

For public SaaS/business: Use Cachet if you need full incident management features and don’t mind the operational overhead. Alternatively, if you have developers who are comfortable with Git workflows, Statusfy offers a modern, performant alternative with zero runtime dependencies.

For developers: Statusfy is elegant if you embrace the static site philosophy. Pair it with GitHub Actions for automatic deployments, and you’ve got a status page that’s essentially infrastructure-as-code.

Alternative: Uptime Kuma’s Status Page

Worth mentioning: Uptime Kuma (covered in detail in a previous guide) also includes a basic status page feature. It’s not as polished as dedicated solutions, but if you’re already running Uptime Kuma for monitoring, it might be sufficient for internal use.

Uptime Kuma Status Page:

  • Public-facing URL with shareable token
  • Shows current status and uptime percentages
  • No incident management
  • Automatically updated by monitoring checks
  • Good for simple use cases, but lacks customization

For most users, if Uptime Kuma’s built-in status page meets your needs, there’s no reason to add another tool. Upgrade to a dedicated solution when you need more features or better design.

Conclusion

Self-hosted status pages have matured significantly. Whether you choose the full-featured approach of Cachet, the modern static architecture of Statusfy, or the integrated monitoring of Gatus, you can build a professional status page without monthly SaaS fees.

For most self-hosters in 2026, Gatus offers the best balance of features, simplicity, and maintainability. It’s what I run for my own infrastructure, and I recommend starting there unless you have specific needs that require Cachet’s incident management or Statusfy’s static architecture.

The key is to actually use your status page. Configure monitoring for your critical services, set up alerting, and make checking the status page a habit. A well-maintained status page is invaluable when troubleshooting issues or planning maintenance windows.

Have questions about status page setup? Run into issues with any of these tools? Drop a comment below — I’m happy to help troubleshoot.