A self-hosted status page for the services you run yourself.
Uptime graphs, incidents and maintenance for your visitors. Monitors, alerts, software updates and an MCP server for you. One container, one YAML file.
For your visitors
- 📈 Uptime at a glance: a graph per service over 7, 30, 90 days or any date range, with outages in red and degraded time in amber. A timeline view shows one bar per day instead.
- 📣 Incidents and maintenance: open incidents on top, a history with every update, scheduled windows, an RSS feed and CSV reports.
- 🎨 Five colour themes, dark and light, on desktop and phone.
For you
- 🩺 Pull-based checks: HTTP, TCP, DNS, TLS certificates, ICMP and exec plugins for anything else (backup age, queue length, a scripted login). Your services are never modified.
- 🌐 Third-party services: follow Atlassian, GitHub, Google Workspace, Slack, Azure or your Microsoft 365 tenant from their own status pages, shown apart from your services and linked to them with
dependsOn. - 🛠️ One sidebar for everything once you sign in: incidents, monitors, maintenance, notifications, API tokens and users, next to every service.
- 🔔 Alerts to Microsoft Teams, e-mail and webhooks, with recovery notices and reminders.
- 🛡️ Software inventory: the running version of every service, the latest release, end of life and known vulnerabilities (OSV.dev, NVD, CISA KEV), scored from none to critical.
- 🔑 Sign-in with local accounts or Gitea / any OIDC provider, roles from groups, scoped API tokens.
- 🤖 MCP server at
/mcp: ask your assistant what is down, declare incidents, schedule maintenance. - 🗄️ SQLite or Postgres; with Postgres, run several replicas and one leader runs the checks.
![]() |
![]() |
| Every service: uptime, response times, incidents | Incidents with public updates |
docker run -d --name heartline -p 3000:3000 \
-e BASE_URL=http://localhost:3000 \
-e AUTH_SECRET="$(openssl rand -hex 24)" \
-e ADMIN_PASSWORD=choose-a-long-password \
-v heartline-data:/data \
ghcr.io/bircni/heartline:latestOpen http://localhost:3000. Without a config file heartline monitors itself; sign in at /login as
admin@example.com (or ADMIN_EMAIL) with your ADMIN_PASSWORD, see
deploy/docker/status.yaml.
Then describe your own services in a status.yaml and mount it with
-v "$PWD/status.yaml:/config/status.yaml:ro". It reloads on every change:
site: { title: Homelab status, timezone: Europe/Berlin }
groups:
- name: Code
monitors:
- { id: gitea, name: Gitea, type: http, url: https://git.example.com/api/healthz }
- { id: postgres, name: Database, type: tcp, host: db.example.com, port: 5432 }
notifiers:
- { id: teams, type: teams, webhookUrl: '${TEAMS_WEBHOOK_URL}' }
auth:
users:
- { email: you@example.com, name: You, password: '${ADMIN_PASSWORD}', role: admin }status.example.yaml shows every option.
kubectl apply -k deploy/kustomize/overlays/sqlite # one replica, SQLite on a PVC
kubectl apply -k deploy/kustomize/overlays/postgres # two replicas, DATABASE_URL from the secretCreate the heartline Secret first (example). Run more
than one replica only with Postgres. Ping checks as non-root need the pod sysctl
net.ipv4.ping_group_range: "0 2147483647". The container runs as UID 1000 with a read-only root
filesystem.
claude mcp add --transport http status https://status.example.com/mcpThe client signs in through heartline (Gitea or a local account) and gets at most your role.
| Topic | What's inside |
|---|---|
| Configuration | status.yaml: site, monitors and their check types, notifiers, env vars |
| Software versions and vulnerabilities | Version probes, release sources, data sources, urgency scoring, privacy |
| Subscribers | E-mail sign-up, double opt-in, what is sent, feeds |
| Sign-in, roles and MCP | Local accounts, Gitea/OIDC with role mapping, MCP tools by scope |
| HTTP API | Public, software and admin routes; OpenAPI at /api/v1/openapi.json |
| Plugins | Write a check in any language: exit code, JSON line, arguments |
| Development and releasing | Local setup, tests, project layout, cutting a release with pnpm release |
Requires Node 26 and pnpm 12.
pnpm install
pnpm dev # API on :3000 + UI on :5173, admin: admin@status.local / change-me-please
pnpm check # lint, format, typecheck, test and buildMore in docs/development.md.
MIT, see LICENSE.


