Monitoring
Monitor your Gryt instance with Prometheus and Grafana
Both the Server and the SFU expose a /metrics endpoint in Prometheus
exposition format. You can scrape these with any Prometheus-compatible system, or
use the built-in monitoring profile to spin up Prometheus + Grafana alongside the
rest of the stack.
Metrics are served on port 9091, not on the port clients connect to, and the
Compose files do not publish it. That is on purpose: a reverse proxy or a tunnel
in front of the server only ever sees the main port, so it cannot pass /metrics
through to the internet.
Prometheus reaches it as server:9091 and sfu:9091 over the Compose network,
which needs no published port. Do not publish 9091, and be aware that host
networking bypasses this and puts metrics back on the public internet.
Quick start
The docker-compose.yml includes an optional monitoring profile.
Enable it to spin up Prometheus and Grafana alongside the rest of the stack:
docker compose --profile monitoring up -dIf you downloaded only the compose file (as in the Docker Compose quick start), you also need the monitoring config directory. Grab it with:
# Run from the same directory as your docker-compose.yml
curl -L https://github.com/Gryt-chat/gryt/tarball/main \
| tar xz --strip-components=4 --include='*/ops/deploy/compose/monitoring'This starts two extra containers:
| Service | URL | Default login |
|---|---|---|
| Prometheus | http://localhost:9090 | (none) |
| Grafana | http://localhost:3000 | admin / admin |
Grafana ships with a Gryt Overview dashboard as the home page — it covers all custom metrics from the server and SFU, plus process-level health for both services. No manual setup needed.
Configuration
| Variable | Default | Description |
|---|---|---|
PROMETHEUS_PORT | 9090 | Host port for Prometheus UI |
GRAFANA_PORT | 3000 | Host port for Grafana UI |
GRAFANA_ADMIN_USER | admin | Grafana admin username |
GRAFANA_ADMIN_PASSWORD | admin | Grafana admin password |
Change the Grafana password
The default admin/admin credentials are fine for local/dev use. For
production, set GRAFANA_ADMIN_PASSWORD to a strong value in your .env file.
Available metrics
Server (Node.js)
Exposed at http://server:9091/metrics, on the Compose network. Set METRICS_PORT
to move it, or METRICS_PORT=0 to stop serving it at all.
| Metric | Type | Description |
|---|---|---|
gryt_http_requests_total | Counter | HTTP requests by method, route, status |
gryt_http_request_duration_seconds | Histogram | Request latency by method, route |
gryt_socketio_connections_active | Gauge | Live Socket.IO connections |
nodejs_* / process_* | Various | Default Node.js metrics (event loop lag, heap, GC, file descriptors) |
SFU (Go)
Exposed at http://sfu:9091/metrics, on the Compose network. Set SFU_METRICS_PORT
to move it, or SFU_METRICS_PORT=0 to stop serving it at all.
| Metric | Type | Description |
|---|---|---|
gryt_sfu_rooms_active | Gauge | Number of active voice rooms |
gryt_sfu_peers_active | Gauge | Total connected peers across all rooms |
gryt_sfu_websocket_connections_active | Gauge | Active WebSocket connections |
gryt_sfu_tracks_active | Gauge | Media tracks being forwarded |
go_* / process_* | Various | Default Go runtime metrics (goroutines, memory, GC) |
Useful queries
Here are some PromQL queries to get started in Prometheus or Grafana:
# Request rate (per second) across all routes
rate(gryt_http_requests_total[5m])
# 95th-percentile request latency
histogram_quantile(0.95, rate(gryt_http_request_duration_seconds_bucket[5m]))
# Active voice users
gryt_sfu_peers_active
# SFU memory usage (bytes)
go_memstats_alloc_bytes{job="gryt-sfu"}
# Server event loop lag (seconds)
nodejs_eventloop_lag_seconds{quantile="0.99"}Built-in dashboard
The Gryt Overview dashboard is provisioned automatically and set as the Grafana home page. It includes:
| Row | Panels |
|---|---|
| Overview | Server / SFU up status, Socket.IO connections, SFU peers, rooms, media tracks, SFU WebSockets |
| HTTP | Request rate by route, request rate by status, latency percentiles (P50 / P95 / P99), error rate (4xx / 5xx) |
| Server Process (Node.js) | Memory (RSS, heap used, heap total), CPU usage, event loop lag |
| SFU Process (Go) | Memory (RSS, heap alloc, heap in-use), CPU usage, goroutines |
The dashboard JSON lives at monitoring/grafana/provisioning/dashboards/gryt-overview.json
relative to the compose file. You can edit it or add more JSON files to the same
directory — Grafana picks them up automatically.
Community dashboards
Grafana has a large library of community dashboards you can import by ID for deeper runtime visibility:
| Dashboard | Grafana ID | Covers |
|---|---|---|
| Node.js Application | 11159 | Event loop, heap, GC, HTTP |
| Go Processes | 6671 | Goroutines, memory, GC |
To import: Grafana → Dashboards → New → Import → paste the ID → select Prometheus as the datasource.
Using an external Prometheus
If you already run a Prometheus instance, skip the monitoring profile and just
add the Gryt targets to your existing prometheus.yml:
scrape_configs:
- job_name: gryt-server
static_configs:
- targets: ["your-gryt-host:5000"]
- job_name: gryt-sfu
static_configs:
- targets: ["your-gryt-host:5005"]Both endpoints are unauthenticated, which is why they are not on the port the world talks to. Anything that can reach port 9091 can read them, so keep it inside the Compose network.
Disabling monitoring
The monitoring profile is entirely opt-in. If you don't pass --profile monitoring,
no monitoring containers are created and the /metrics endpoints simply go
unscraped. The overhead of the endpoints themselves is negligible (a few KB of
in-memory counters).
Because most deployments never turn monitoring on, the metrics port is closed to
the outside by default rather than by configuration — there is nothing to set and
nothing to forget. To stop serving them altogether, set METRICS_PORT=0 and
SFU_METRICS_PORT=0.
To stop the monitoring stack without affecting the rest of the services:
docker compose --profile monitoring stop prometheus grafana