A self-hosted, professional status page for monitoring the health of your Docker containers, mail gateway, websites/APIs, and any TCP service — driven by a single YAML file. No database, no native dependencies, one container.
- Multiple check types — HTTP/HTTPS, Docker containers, SMTP/IMAP/POP3 mail, and raw TCP/ping.
- Clean professional dashboard — overall status banner, grouped service rows, live heartbeat bars, response-time sparklines, and 24h/30d uptime.
- Config-driven — add or remove services by editing a single YAML file. No code changes, no rebuild.
- Persistent history — uptime survives restarts (stored as JSON, no database or native dependencies).
- Single container — the Node backend serves the built React frontend.
- Dark / light theme, auto-refreshing every 10 seconds.
- Optional built-in login page — a clean branded sign-in screen with session cookies, instead of an ugly browser basic-auth popup.
# 1. Edit your services
nano config/config.yaml
# 2. Build & run
docker compose up -d --build
# 3. Open the dashboard
# http://localhost:3000The status page is now running. Edit config/config.yaml and run
docker compose restart statuspage whenever you change your monitors.
All monitors live in config/config.yaml. Each monitor
needs a name, a group (used as a section heading), and a type.
settings:
title: "Infrastructure Status"
subtitle: "Live health of our services"
defaultInterval: 60 # seconds between checks
defaultTimeout: 5000 # ms before a check fails
monitors:
- name: "Company Website"
group: "Web Services"
type: http
url: "https://example.com"
expectStatus: [200, 301, 302] # optional
expectKeyword: "Welcome" # optional: text that must be in the body
- name: "Mail Gateway (SMTP)"
group: "Mail"
type: smtp
host: "mail.example.com"
port: 587
- name: "Reverse Proxy"
group: "Docker Containers"
type: docker
container: "nginx" # container name or id
- name: "PostgreSQL"
group: "Databases"
type: tcp
host: "10.0.0.5"
port: 5432| Type | Required fields | Notes |
|---|---|---|
http / https |
url |
expectStatus (array), expectKeyword, method, headers |
tcp / ping |
host, port |
Generic port reachability (databases, caches, custom services) |
smtp / imap / pop3 |
host, port |
Reads the server banner; TLS auto-detected for 465/993/995 |
docker |
container |
Needs the Docker socket mounted; reads running + health status |
Per-monitor overrides: interval (seconds) and timeout (ms).
The docker check talks to the Docker daemon via the socket mounted in
docker-compose.yml:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:roTo monitor a remote Docker host instead, set DOCKER_HOST in the
environment and remove the socket mount.
Run the backend and frontend separately with hot reload:
# Terminal 1 — backend (API on :3000)
cd backend && npm install && npm run dev
# Terminal 2 — frontend (UI on :5173, proxies /api to :3000)
cd frontend && npm install && npm run devOpen http://localhost:5173.
On Windows,
dockerchecks need a reachable Docker daemon. SetDOCKER_HOST(e.g.npm:pipe////./pipe/docker_engine) or just rely on the other check types during local dev.
backend/ Node.js + Express
src/
index.js HTTP server + /api routes + static serving
config.js loads & normalizes config.yaml
scheduler.js runs each monitor on its interval
store.js in-memory state + JSON persistence + uptime aggregation
checks/ http · tcp · smtp · docker
frontend/ React + Vite
src/
App.jsx dashboard, polling, theme
components/ MonitorRow · Heartbeat · Sparkline
config/config.yaml your services
data/history.json persisted uptime (auto-created)
| Endpoint | Description |
|---|---|
GET /api/status |
Full dashboard payload (groups + monitors) |
GET /api/meta |
Title/subtitle + supported check types |
GET /api/healthz |
Liveness probe |
StatusGlass can push an alert via ntfy whenever a monitor
changes state — down, recovered, or degraded. Add a notifications block to
config/config.yaml:
notifications:
ntfy:
enabled: true
server: "https://ntfy.example.com" # your ntfy server (or https://ntfy.sh)
topic: "statusglass"
token: "" # optional bearer token for protected topics
priority: high # priority for down/degraded alerts
notifyOn: [down, degraded, up] # which target states trigger a message
clickUrl: "https://status.example.com" # optional: opens when the alert is tappedSubscribe to the topic in the ntfy app (or ntfy subscribe <topic>) to receive
alerts. The first check after startup never notifies (no false "down" on boot).
Values can also be set via NTFY_ENABLED, NTFY_SERVER, NTFY_TOPIC,
NTFY_TOKEN environment variables.
By default the dashboard is public. To require a login, add an auth block to
config/config.yaml:
auth:
enabled: true
username: admin
password: "change-me" # plaintext, or use passwordHash below
# passwordHash: "scrypt$..." # generated with: node src/hashpw.js 'pw'
sessionTtlHours: 720 # session lifetime (default 30 days)This serves a branded login page; on success the browser gets a signed,
HttpOnly session cookie and /api/status is gated behind it. Credentials can
also be supplied via environment variables (AUTH_ENABLED, AUTH_USERNAME,
AUTH_PASSWORD, AUTH_PASSWORD_HASH, SESSION_SECRET).
To store a hashed password instead of plaintext:
docker compose exec statusglass node src/hashpw.js 'my-secret-password'
# copy the scrypt$... value into auth.passwordHashWhen StatusGlass handles login itself, don't also add basic-auth at your reverse proxy — that would prompt twice.
StatusGlass listens on port 3000. To serve it on a domain with HTTPS and optional auth, point your existing reverse proxy at it. Example for Caddy:
status.example.com {
basic_auth {
admin <bcrypt-hash> # generate with: caddy hash-password
}
reverse_proxy statusglass:3000
}When fronted by a proxy, drop the ports: mapping from docker-compose.yml
and put the container on the proxy's network instead.
Issues and pull requests are welcome. The codebase is intentionally small —
new check types live in backend/src/checks/ and register in
backend/src/checks/index.js.
MIT © GamenHaaDee