mTLS metrics are never emitted by the sidecar
In what area(s)?
/area runtime
What version of Dapr?
1.18.4 (also present on master at the time of writing)
Expected Behavior
The four mTLS metrics documented in docs/development/dapr-metrics.md are exported by the sidecar:
dapr_runtime_mtls_init_totaldapr_runtime_mtls_init_fail_totaldapr_runtime_mtls_workload_cert_rotated_totaldapr_runtime_mtls_workload_cert_rotated_fail_total
Actual Behavior
None of them are ever exported. On a healthy cluster with mTLS enabled and 7 app IDs exporting other runtime metrics normally, dapr_runtime_mtls_* matches zero series, and scraping a sidecar's :9090/metrics directly returns nothing for the prefix while dapr_runtime_component_* is present on the same endpoint.
Three of the four cannot be emitted at all:
| Metric | Recorder | Call sites outside service_monitoring.go |
|---|---|---|
mtls_init_total |
MTLSInitCompleted |
1, but unreachable — see below |
mtls_init_fail_total |
MTLSInitFailed |
0 |
mtls_workload_cert_rotated_total |
MTLSWorkLoadCertRotationCompleted |
0 |
mtls_workload_cert_rotated_fail_total |
MTLSWorkLoadCertRotationFailed |
8, all in pkg/security/sentry.go |
Why mtls_init_total never records
serviceMetrics is constructed with enabled: false, and every recorder is guarded by it:
func (s *serviceMetrics) MTLSInitCompleted() {
if s.enabled {
stats.RecordWithOptions(...)
}
}enabled is only set to true by serviceMetrics.Init, reached via diag.InitMetrics from runtime.FromConfig.
The single call site is in the security provider's run loop, pkg/security/security.go:
p.sec.id = id.ID
close(p.readyCh)
diagnostics.DefaultMonitoring.MTLSInitCompleted()
p.htarget.Ready()In cmd/daprd/app/app.go that runner is started before the runtime is built, and the runtime is only built after the security handler is available:
err = concurrency.NewRunnerManager(
secProvider.Run, // records MTLSInitCompleted
func(ctx context.Context) error {
sec, serr := secProvider.Handler(ctx) // blocks on readyCh
...
rt, rerr := runtime.FromConfig(ctx, ...) // -> InitMetrics -> enabled = trueMTLSInitCompleted is emitted immediately after close(p.readyCh), whereas InitMetrics is only reached once Handler has returned and the full runtime config has been loaded. enabled is therefore still false when the record is attempted and the call is a silent no-op. It is a race in principle, but the two are far enough apart that it resolves the same way every time — no sidecar in the sample exported the metric.
mtls_workload_cert_rotated_fail_total is reachable, but only on an actual rotation failure, and the first attempt happens in the same pre-InitMetrics window.
Steps to Reproduce the Problem
On a Kubernetes cluster with Dapr installed and mTLS enabled:
kubectl exec -n <ns> <any-dapr-pod> -c daprd -- wget -qO- localhost:9090/metrics | grep dapr_runtime_mtlsReturns nothing. grep dapr_runtime_component on the same endpoint returns series, confirming the exporter is working.
Release Note
RELEASE NOTE: FIX mTLS metrics are never emitted by the sidecar.
Source: dapr/dapr