Skip to content

Commit afaf276

Browse files
committed
release: v7.1.0 — observability stack (Prometheus + Grafana opt-in profile)
Opt-in. `docker compose --profile observability up -d` deploys Prometheus (scrape /api/metrics every 15s, 7-day retention) + Grafana 11.3 (pre-provisioned datasource + 8-panel dashboard). Standalone users: zero impact. Default `docker compose up -d` remains unchanged. What ships: docker/observability/ ├── prometheus.yml — scrape config └── grafana/ ├── provisioning/ │ ├── datasources/prometheus.yml — auto-wired DS │ └── dashboards/docker-dash.yml — provider config └── dashboards/ └── docker-dash-overview.json — 8-panel dashboard docker-compose.yml prometheus (v3.0.1) + grafana (11.3.0) services behind observability profile. no-new-privileges, named volumes, Prometheus not exposed to host by default. docs/features/observability.md (~3,200 words) Panel reference, 6 recommended PromQL alerts, integration with existing Prometheus/Grafana, security hardening (7 items), deployment recommendations (resource limits, retention, HA scrape via static targets or Docker SD). Dashboard works in both modes. HA-specific panels (cluster role, Redis connected) show meaningful values in HA, "down / N/A" in standalone. All 8 panel types: stat with value mapping, timeseries line, timeseries bars, timeseries stacked. Staging soak: - prometheus UP scraping app:8101/api/metrics - Grafana 11.3.0 health ok - Datasource auto-registered: Prometheus uid=docker-dash-prom - Dashboard auto-imported: "Docker Dash — Overview" in Docker Dash folder, 8 panels present - Live query verified: cluster_role=0 (standalone), HTTP rate 0.025 req/s GET/2xx v7.2.0 roadmap: in-app wizard to detect existing Prometheus/Grafana and offer 3 paths (integrate existing / deploy ours / point to external). Compose profile + dashboard JSON become wizard primitives. No app code changes. No dependencies added. npm audit unchanged. Tests 879/4/57 (unchanged).
1 parent 059a999 commit afaf276

11 files changed

Lines changed: 842 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,115 @@
22

33
All notable changes to Docker Dash are documented here.
44

5+
## [7.1.0] - 2026-04-22 — "Observability stack — Prometheus + Grafana opt-in profile"
6+
7+
Opt-in observability: `docker compose --profile observability up -d` adds Prometheus (scraping `/api/metrics` every 15s) + Grafana (pre-provisioned data source + 8-panel overview dashboard) alongside the app. Zero UI config — the dashboard populates within 30s of first scrape.
8+
9+
**Standalone users: zero impact.** Default `docker compose up -d` is unchanged; the observability stack only comes up when explicitly requested via `--profile observability`.
10+
11+
### Added — `docker/observability/` directory
12+
13+
Four files that drive the entire stack:
14+
15+
- **[`docker/observability/prometheus.yml`](docker/observability/prometheus.yml)** — scrape config: `app:8101/api/metrics` every 15s, 10s timeout. Includes Prometheus self-scrape.
16+
- **[`docker/observability/grafana/provisioning/datasources/prometheus.yml`](docker/observability/grafana/provisioning/datasources/prometheus.yml)** — auto-registers Prometheus at `http://prometheus:9090` with UID `docker-dash-prom`, proxy access, 15s time interval, POST method (handles long queries).
17+
- **[`docker/observability/grafana/provisioning/dashboards/docker-dash.yml`](docker/observability/grafana/provisioning/dashboards/docker-dash.yml)** — dashboard provider: watches `/etc/grafana/dashboards/` and auto-imports `.json` files every 30s. Puts them in a "Docker Dash" folder.
18+
- **[`docker/observability/grafana/dashboards/docker-dash-overview.json`](docker/observability/grafana/dashboards/docker-dash-overview.json)** — the overview dashboard (below).
19+
20+
### Added — Overview dashboard (8 panels)
21+
22+
Works in both standalone and HA mode. HA-specific panels show "down / N/A" in standalone (intentional — mode is detectable from the cluster role panel).
23+
24+
| # | Panel | Type | Query |
25+
|:-:|-------|------|-------|
26+
| 1 | Cluster role | Stat (value mapping) | `docker_dash_cluster_role` — maps 0/1/2/-1 → standalone/leader/reader/unknown |
27+
| 2 | Redis (HA only) | Stat (value mapping) | `docker_dash_cluster_redis_connected` — red/green |
28+
| 3 | Active WS connections | Stat (area sparkline) | `sum(docker_dash_ws_connections_active)` |
29+
| 4 | Containers managed | Stat (area) | `docker_dash_containers_total` |
30+
| 5 | HTTP request rate | Timeseries (line, legend table) | `sum by(method,status) (rate(docker_dash_http_requests_total[5m]))` |
31+
| 6 | Avg HTTP latency | Timeseries (thresholded 500/2000ms) | `sum(rate(...duration_ms)) / sum(rate(...requests_total))` per method × status class |
32+
| 7 | Background job runs | Timeseries (bars) | `sum by(job) (rate(docker_dash_background_job_runs_total[15m]))` |
33+
| 8 | HTTP errors by status | Timeseries (stacked area) | `sum by(status) (rate(docker_dash_http_errors_total[5m]))` — shows 429/5xx spikes |
34+
35+
Default refresh 30s, time range `now-1h`. Grafana version target: 11.x (schema 39).
36+
37+
### Added — `docker-compose.yml` `--profile observability`
38+
39+
Two new services behind the `observability` profile:
40+
41+
```yaml
42+
prometheus:
43+
image: prom/prometheus:v3.0.1
44+
command: --storage.tsdb.retention.time=7d # ...
45+
# Not exposed to host by default — Grafana reaches it internally.
46+
47+
grafana:
48+
image: grafana/grafana:11.3.0
49+
ports: ["${GRAFANA_PORT:-3001}:3000"]
50+
environment:
51+
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} # override for automation
52+
GF_AUTH_ANONYMOUS_ENABLED: false
53+
GF_USERS_ALLOW_SIGN_UP: false
54+
```
55+
56+
Both with `no-new-privileges:true`, named volumes for data persistence (`docker-dash-prometheus-data`, `docker-dash-grafana-data`). Prometheus **not exposed to host** by default — defense in depth; operators who need external scrape can uncomment the `ports:` block.
57+
58+
### Added — [`docs/features/observability.md`](docs/features/observability.md) (3,200 words)
59+
60+
Operator reference covering:
61+
62+
1. What's in the stack (service table, resource expectations)
63+
2. Enabling (quick + with custom credentials + with custom port)
64+
3. Dashboard panels reference (what each shows, why it matters)
65+
4. Recommended alerts (6 PromQL-ready alert expressions)
66+
5. **Integrating with existing Prometheus/Grafana** (two paths: scrape config append + dashboard JSON import via UI or API)
67+
6. Security hardening checklist (7 items — change default password, don't expose Prometheus, HTTPS, disable anonymous access, SSO integration, data-source proxy mode)
68+
7. **Common-sense deployment recommendations** — persistent storage, resource limits, retention vs disk trade-off, scaling considerations (single-instance Prometheus caveat, HA replica scrape via static targets OR Docker SD)
69+
8. Teardown (keep vs drop data volumes)
70+
9. Known limitations (no histograms — counters + gauges only; no per-container rollup; per-replica scrape cardinality bound)
71+
10. See also (source files + related docs)
72+
73+
### Staging soak
74+
75+
Deployed on staging, verified end-to-end:
76+
77+
1. ✓ `docker compose --profile observability up -d` starts both services cleanly
78+
2. ✓ Prometheus scrapes `app:8101/api/metrics` with `up=1`
79+
3. ✓ Grafana health check returns `{"database":"ok","version":"11.3.0"}`
80+
4. ✓ Data source auto-registered: `Prometheus (prometheus) url=http://prometheus:9090 uid=docker-dash-prom`
81+
5. ✓ Dashboard auto-imported: `Docker Dash — Overview uid=docker-dash-overview folder=Docker Dash`
82+
6. ✓ All 8 panels present by title and type
83+
7. ✓ Live queries return real data: `docker_dash_cluster_role = 0` (standalone), HTTP rate ~0.025 req/s GET/2xx with GET/3xx + GET/4xx active
84+
85+
### Changed — README
86+
87+
- Feature Reference section adds "Observability Stack" link
88+
- Version badge 7.0.0 → 7.1.0
89+
90+
### No dependency changes
91+
92+
Prometheus + Grafana are Docker images pulled by the compose profile. `package.json` unchanged. `npm audit` remains clean.
93+
94+
### Tests
95+
96+
- **879 passing + 4 skipped / 57 suites** (unchanged — observability is Docker-side, no JS code added to the main app)
97+
- Lint: 0 warnings / 0 errors
98+
99+
### Files touched
100+
101+
- `docker/observability/` — 4 files (new)
102+
- `docker/observability/grafana/dashboards/docker-dash-overview.json` — 8-panel dashboard JSON
103+
- `docker-compose.yml` — `prometheus` + `grafana` services behind `observability` profile, 2 new named volumes
104+
- `docs/features/observability.md` — 3,200-word operator guide (new)
105+
- `README.md` — feature reference + version
106+
- `package.json`, `src/version.js` — 7.1.0
107+
108+
### v7.2.0 roadmap (next)
109+
110+
In-app wizard: detect existing Prometheus / Grafana in the Docker environment → offer three paths (integrate existing · deploy ours · point to external). The compose profile + dashboard JSON shipped here become the primitives the wizard executes. See `plans/deep-spec-observability-wizard.md` (to be written when scoped).
111+
112+
---
113+
5114
## [7.0.0] - 2026-04-22 — "HA mode production-ready — observability + runbook + LB configs"
6115

7116
**Major release.** HA mode (shipped incrementally across v6.17.0 → v6.17.2) is now **production-ready**. v7.0.0 adds the operational layer: cluster introspection endpoints, Prometheus metrics, a detailed failover runbook, and copy-paste LB configs for the 4 most common load balancers.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<a href="https://github.com/bogdanpricop/docker-dash/releases/latest"><img src="https://img.shields.io/github/v/release/bogdanpricop/docker-dash?color=blue" alt="Release"></a>
1111
<a href="LICENSE"><img src="https://img.shields.io/github/license/bogdanpricop/docker-dash" alt="License"></a>
1212
<a href="https://github.com/bogdanpricop/docker-dash/actions/workflows/ci.yml"><img src="https://img.shields.io/badge/tests-879%20passing%20(100%25)-brightgreen" alt="Tests"></a>
13-
<img src="https://img.shields.io/badge/version-7.0.0-blue" alt="Version">
13+
<img src="https://img.shields.io/badge/version-7.1.0-blue" alt="Version">
1414
<a href="SECURITY.md#security-audit-history"><img src="https://img.shields.io/badge/production%20readiness-9.8%2F10-brightgreen" alt="Production Readiness"></a>
1515
<a href="SECURITY.md"><img src="https://img.shields.io/badge/security-audited-brightgreen" alt="Security Audited"></a>
1616
<img src="https://img.shields.io/badge/Docker-~80MB-blue" alt="Image Size">
@@ -269,6 +269,7 @@ Dedicated reference docs for the deeper features, in [docs/features/](docs/featu
269269
- **[HA Mode](docs/features/ha-mode.md)** — optional Redis-backed redundancy (production-ready in v7.0.0); architecture, trade-offs, when NOT to use it
270270
- **[HA Failover Runbook](docs/features/ha-failover-runbook.md)** — operator procedures: leader death, rolling restart, Redis failure, split-brain detection, recovery checklist
271271
- **[HA Load Balancer Configs](docs/features/ha-lb-configs.md)** — copy-paste examples for Caddy + Traefik + HAProxy + nginx with sticky-session + WS upgrade + health checks
272+
- **[Observability Stack (v7.1.0)](docs/features/observability.md)** — opt-in Prometheus + Grafana via `docker compose --profile observability up -d`, 8-panel dashboard auto-provisioned, recommended alerts, integration with existing Prometheus/Grafana
272273

273274
## Where to start
274275

docker-compose.yml

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ services:
44
context: .
55
dockerfile: Dockerfile
66
args:
7-
APP_VERSION: "${APP_VERSION:-7.0.0}"
8-
image: docker-dash:${APP_VERSION:-7.0.0}
7+
APP_VERSION: "${APP_VERSION:-7.1.0}"
8+
image: docker-dash:${APP_VERSION:-7.1.0}
99
container_name: docker-dash
1010
restart: unless-stopped
1111
env_file:
@@ -54,7 +54,7 @@ services:
5454
dd-egress-filter:
5555
build:
5656
context: ./docker/egress-filter
57-
image: docker-dash-egress-filter:${APP_VERSION:-7.0.0}
57+
image: docker-dash-egress-filter:${APP_VERSION:-7.1.0}
5858
container_name: dd-egress-filter
5959
restart: unless-stopped
6060
# Uses the default bridge so target containers on the default bridge can
@@ -83,14 +83,14 @@ services:
8383
# DD_MODE=ha
8484
# REDIS_URL=redis://redis:6379
8585
#
86-
# v7.0.0 ships the foundation (Redis-backed rate limiter + cluster abstraction).
86+
# v7.1.0 ships the foundation (Redis-backed rate limiter + cluster abstraction).
8787
# DO NOT run multi-replica in HA mode yet — WS pub/sub + cron leader election
88-
# land in v7.0.0-alpha.1 / v7.0.0-rc.1. Running v7.0.0 HA with 2+ replicas
88+
# land in v7.1.0-alpha.1 / v7.1.0-rc.1. Running v7.1.0 HA with 2+ replicas
8989
# causes duplicate cron execution (duplicate backups, concurrent VACUUM).
9090
#
9191
# Single-instance HA (1 replica + Redis) is useful for warming up operational
9292
# tooling (Prometheus scrape of Redis, sticky-session LB config drill, etc.)
93-
# before rolling out true multi-replica in v7.0.0.
93+
# before rolling out true multi-replica in v7.1.0.
9494
redis:
9595
image: redis:7-alpine
9696
container_name: docker-dash-redis
@@ -108,6 +108,68 @@ services:
108108
profiles:
109109
- ha
110110

111+
# Optional observability stack (v7.1.0) — enable with:
112+
# docker compose --profile observability up -d
113+
#
114+
# Ships Prometheus (scrape /api/metrics every 15s) + Grafana (auto-provisioned
115+
# datasource + "Docker Dash — Overview" dashboard). Works in BOTH standalone
116+
# and HA mode — the dashboard panels gate HA-only metrics (cluster role,
117+
# Redis connected) via value mappings.
118+
#
119+
# Grafana default: admin / admin (forced change on first login).
120+
# Grafana exposed on :${GRAFANA_PORT:-3001}; Prometheus NOT exposed externally.
121+
# See docs/features/observability.md for hardening + integration with
122+
# existing Prometheus / Grafana installs.
123+
prometheus:
124+
image: prom/prometheus:v3.0.1
125+
container_name: docker-dash-prometheus
126+
restart: unless-stopped
127+
command:
128+
- --config.file=/etc/prometheus/prometheus.yml
129+
- --storage.tsdb.path=/prometheus
130+
- --storage.tsdb.retention.time=7d
131+
- --web.enable-lifecycle
132+
volumes:
133+
- ./docker/observability/prometheus.yml:/etc/prometheus/prometheus.yml:ro
134+
- prometheus-data:/prometheus
135+
# Not exposed to host by default — Grafana reaches it on the internal
136+
# network. Uncomment to expose for external scraping.
137+
# ports:
138+
# - "${PROMETHEUS_PORT:-9091}:9090"
139+
security_opt:
140+
- no-new-privileges:true
141+
profiles:
142+
- observability
143+
144+
grafana:
145+
image: grafana/grafana:11.3.0
146+
container_name: docker-dash-grafana
147+
restart: unless-stopped
148+
ports:
149+
- "${GRAFANA_PORT:-3001}:3000"
150+
volumes:
151+
- ./docker/observability/grafana/provisioning:/etc/grafana/provisioning:ro
152+
- ./docker/observability/grafana/dashboards:/etc/grafana/dashboards:ro
153+
- grafana-data:/var/lib/grafana
154+
environment:
155+
# Default admin / admin — Grafana FORCES change on first login.
156+
# For automated provisioning, set GF_SECURITY_ADMIN_PASSWORD in .env
157+
# so the first-login password change is pre-applied.
158+
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin}
159+
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
160+
# Disable anonymous access — operators must log in. Flip to true +
161+
# set Viewer role if you want the dashboard publicly visible.
162+
- GF_AUTH_ANONYMOUS_ENABLED=false
163+
# Disable user sign-up / email invites on this self-hosted install.
164+
- GF_USERS_ALLOW_SIGN_UP=false
165+
- GF_USERS_ALLOW_ORG_CREATE=false
166+
depends_on:
167+
- prometheus
168+
security_opt:
169+
- no-new-privileges:true
170+
profiles:
171+
- observability
172+
111173
# Optional HTTPS reverse proxy — enable with: docker compose --profile tls up -d
112174
# Configure via Docker Dash UI: System → SSL/TLS → Enable HTTPS
113175
caddy:
@@ -143,3 +205,7 @@ volumes:
143205
name: docker-dash-egress-logs
144206
redis-data:
145207
name: docker-dash-redis-data
208+
prometheus-data:
209+
name: docker-dash-prometheus-data
210+
grafana-data:
211+
name: docker-dash-grafana-data

0 commit comments

Comments
 (0)