Skip to content

GamenHaaDee/StatusGlass

Repository files navigation

StatusGlass

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.

CI self-hosted license node

Features

  • 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.

Quick start (Docker)

# 1. Edit your services
nano config/config.yaml

# 2. Build & run
docker compose up -d --build

# 3. Open the dashboard
#    http://localhost:3000

The status page is now running. Edit config/config.yaml and run docker compose restart statuspage whenever you change your monitors.

Configuration

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

Check types

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).

Monitoring Docker containers

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:ro

To monitor a remote Docker host instead, set DOCKER_HOST in the environment and remove the socket mount.

Local development

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 dev

Open http://localhost:5173.

On Windows, docker checks need a reachable Docker daemon. Set DOCKER_HOST (e.g. npm:pipe////./pipe/docker_engine) or just rely on the other check types during local dev.

Architecture

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)

API

Endpoint Description
GET /api/status Full dashboard payload (groups + monitors)
GET /api/meta Title/subtitle + supported check types
GET /api/healthz Liveness probe

Notifications (optional)

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 tapped

Subscribe 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.

Authentication (optional)

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.passwordHash

When StatusGlass handles login itself, don't also add basic-auth at your reverse proxy — that would prompt twice.

Putting it behind a reverse proxy

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.

Contributing

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.

License

MIT © GamenHaaDee

About

A clean, self-hosted status page for monitoring Docker containers, mail gateways, websites and TCP services — driven by a single YAML file. No database, no native dependencies, one container.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages