Observability stack
April 28, 2026 · View on GitHub
observability_generate_bundle is the project's headline operator
tool. A single MCP call (or single CLI subcommand, when the CLI
catches up) takes a scenario + parameters and returns a deployment-
ready bundle:
- The OpenSIPS cfg with the
prometheusmodule injected. - Per-category Grafana dashboard JSON files (overview, dialogs, dispatcher, rtpengine, TLS/security, IMS — only the ones that match the modules your scenario actually loads).
- A Prometheus
scrape_configsYAML stanza pointing at every OpenSIPS instance you list. - Grafana provisioning files (datasource + dashboard provider).
- A
docker-compose.monitoring.ymlfor a side-car Prometheus + Grafana stack. - A README explaining how to deploy what you just received.
The point is to make "I deployed an OpenSIPS scenario" and "I can see what it's doing" the same operation — not two separate weeks of work.
Why this exists
The pre-bundle workflow was:
- Generate the cfg (
cfg_generate). - Manually add
loadmodule "prometheus.so"+loadmodule "httpd.so"to that cfg. - Stand up Prometheus separately. Configure scrape job by hand.
- Stand up Grafana separately. Create datasource by hand.
- Hand-pick dashboards from
docs/learn/grafana/. Import each. - Realise the dashboards reference modules your scenario doesn't load (empty panels everywhere).
- Spend a Tuesday afternoon trimming them.
The bundle generator collapses that to one call, and the dashboard
selection is driven by the cfg's loaded modules — so a config
that loads dispatcher.so + rtpengine.so gets the dispatcher and
media dashboards but not the IMS one. Empty panels go away.
The fast path
observability_generate_bundle(
scenario="residential_pbx",
params={
"db_url": "mysql://opensips:CHANGE_ME@db:3306/opensips",
"domain": "pbx.example.com"
},
targets=["10.0.0.10:8888", "10.0.0.11:8888"]
)
Returns a dict. Save it to disk:
observability_save_bundle(
bundle=<the dict>,
out_dir="/tmp/opensips-monitoring-build"
)
You now have a directory containing:
/tmp/opensips-monitoring-build/
├── README.md ← how to deploy what's in this directory
├── opensips.cfg ← scenario cfg with prometheus injected
├── docker-compose.monitoring.yml
├── prometheus/
│ └── prometheus.yml ← scrape config + global settings
├── grafana/
│ ├── provisioning/
│ │ ├── datasources/prometheus.yml
│ │ └── dashboards/opensips.yml
│ └── dashboards/
│ ├── 01-overview.json
│ ├── 02-dialogs-and-calls.json
│ ├── 03-routing-and-dispatch.json
│ └── ... ← only the categories your scenario triggered
cd /tmp/opensips-monitoring-build
docker compose -f docker-compose.monitoring.yml up -d
# Grafana on http://localhost:3000 (admin / admin by default — change it)
# Prometheus on http://localhost:9090
The OpenSIPS instances at the targets you specified become scrape
sources. As soon as their prometheus module is up on :8888/metrics,
panels populate.
When to reach for which tool
| You want... | Use |
|---|---|
| Everything (cfg + dashboards + Prometheus + Grafana + compose), in one call | observability_generate_bundle |
| Same, but starting from a cfg you already hand-edited | observability_generate_bundle_from_cfg |
| Just the dashboards (Grafana already running) | observability_generate_dashboards |
| Just the Prometheus scrape stanza | observability_generate_prometheus_scrape |
| Just the Grafana provisioning files | observability_generate_grafana_provisioning |
| Just the side-car monitoring docker-compose | observability_generate_monitoring_compose |
| Add prometheus + httpd to an existing cfg | observability_inject_prometheus |
| Preview which dashboards would fire for a module set | observability_categories_for_modules |
| List every available panel category | observability_list_panel_categories |
| Persist a bundle dict to disk | observability_save_bundle |
Why the dashboard set adapts to the scenario
Each panel category is bound to a set of OpenSIPS modules in
src/opensips_mcp/observability/panel_library.py.
The dashboard builder reads loadmodule directives from the cfg
under construction and picks every category whose triggers match.
Examples:
class4_sbcloadsdispatcher.so + dialog.so + tm.so + sl.so→ triggers overview, dialogs, dispatcher, security. No IMS, no RTPEngine, no TLS dashboards.webrtc_gatewayloadstls_mgm.so + rtpengine.so + dialog.so→ triggers overview, dialogs, rtpengine, tls/security.ims_cx_scscfloadsaaa_diameter.so + dialog.so + ...→ triggers overview, dialogs, ims.
The result: dashboards you import always have data. Empty panels are eliminated by construction, not by post-hoc cleanup.
Coexistence with the existing static dashboards
The five hand-curated JSON files under
docs/learn/grafana/ predate the generator. They are
still valid: import any of them directly into Grafana if you want
the canonical panel set without going through the bundle.
The generator's output is a superset — same panel taxonomy, but filtered to your scenario, plus the surrounding deployment glue. Use the static files as reference; use the generator for actual deployments.
Production refinements
The generated bundle is lab-grade. Before using it in production:
- Change Grafana's admin password. The default
docker-compose sets
admin/admin; rotate immediately. - Replace SQLite with a real Grafana DB if you want HA on the monitoring stack itself.
- Add TLS to Prometheus and Grafana. The generator emits HTTP; wrap with nginx / Caddy / your platform's TLS termination.
- Tighten Prometheus retention. Default 15-day retention is
fine for evaluation; for production tune
--storage.tsdb.retention.time. - Wire alerts. The bundle does not include alert rules. Pair
with
kube-prometheus-stack's alerts or the rules at docs/learn/grafana/ (when those land — currently only dashboards ship in static form). - Audit access to
/metrics. OpenSIPS' Prometheus exporter exposes everything; either bind it to localhost only and put a reverse proxy in front, or restrict via Kubernetes NetworkPolicy.
Limitations
- The generator does NOT add alert rules. Ship those separately
via
prometheus.ymlrule_files. - The generator's docker-compose is for the monitoring stack only
— it does not orchestrate OpenSIPS itself. Pair with the project's
Helm chart at
deploy/helm/opensips-mcp/or your existing Docker / systemd setup. - The Grafana dashboards reference standard OpenSIPS Prometheus
metric names. If you've added custom statistics via the
xstatsmodule or viascript_route, those won't appear unless you extend the panel library. - The bundle is idempotent in name but not idempotent on
disk — re-running
observability_save_bundlewill overwrite files in the target directory. If you've hand-tuned the generated Grafana dashboards, save your changes elsewhere before re-running.
Where to go next
- post-deploy-smoke-test.md — run after deploying the bundle to confirm the metrics endpoint is alive and Grafana can scrape it.
- grafana/ — the static dashboard reference set.
- ha-failover-playbook.md — once
observability is in place, set up alerts on
dispatcher_inactive_destinations,clusterer_state_changed,dialog:active_dialogsdeltas to catch failover events.