Stronghold monitoring relies on three data sources:
- telemetry metrics: exposed at the
/v1/sys/metricsendpoint and, if configured, pushed to external systems (StatsD, DogStatsD, and so on); - the
/v1/sys/healthhealth check endpoint; - server logs and audit logs.
The full list of metrics with their types and labels is in the Metrics catalog.
Connection variants
Stronghold has a single metrics source (the telemetry subsystem) and several ways to deliver metrics to a monitoring system. The in-memory sink that backs the JSON response of sys/metrics is always on. The Prometheus sink is on by default (prometheus_retention_time = 24h), and the push sinks are enabled by their parameters in the telemetry block. All enabled sinks work at the same time.
| Variant | When to use | Where configured | Needs |
|---|---|---|---|
Prometheus pull from sys/metrics?format=prometheus (standalone) | A standalone installation with Prometheus (or a compatible scraper) | telemetry block (prometheus_retention_time) and, optionally, the listener telemetry block | Network access from Prometheus to the API port of every node; a token with the read capability on sys/metrics or unauthenticated_metrics_access = true on the listener |
| DP: built-in Prometheus (PodMonitor and kube-rbac-proxy) | Stronghold in DP with the prometheus module enabled: metrics are collected without additional setup | Managed by the module, no setup required | The prometheus module; the monitoring.coreos.com/v1 PodMonitor CRD |
| DP: external Prometheus | Metrics must be collected by a Prometheus outside the DP monitoring stack | Not configured by the module: you create the access yourself, see External Prometheus | Network access to port 9889 of the pods; a Kubernetes token with get on statefulsets/prometheus-metrics |
StatsD push (statsd_address) | You already have a StatsD-compatible collector and want push delivery over UDP | telemetry block | A collector address reachable from the node |
Statsite push (statsite_address) | You use Statsite and want delivery over TCP | telemetry block | A Statsite address reachable from the node |
DogStatsD push (dogstatsd_addr) | Datadog Agent (DogStatsD) is installed on the nodes; tags are needed | telemetry block | A DogStatsD address reachable from the node |
Circonus push (circonus_*) | Metrics must go to Circonus | telemetry block | A Circonus API token or a check submission URL |
Stackdriver push (stackdriver_*) | Metrics must go to Google Cloud Monitoring | telemetry block | A Google Cloud project; credentials available to the process (Application Default Credentials) |
Notes:
- All the listed sinks are available in all Stronghold editions.
- Push sinks can be combined with Prometheus and with each other. When more than one sink is enabled, set
disable_hostname = true: otherwise Stronghold prints a warning at startup because hostname-prefixed metric names are inconvenient for Prometheus. - Push sinks are configured only in the configuration file, so they are available for standalone installations only. In DP, the module generates the
telemetryblock and does not expose its parameters, see Monitoring in DP.
Enabling telemetry
This section and the next ones up to Monitoring in DP describe standalone installations. In DP, the configuration is managed by the module (the stronghold-config ConfigMap), see Monitoring in DP.
Telemetry settings are defined in the telemetry block of the server configuration file. Only one telemetry block is allowed. The full list of parameters is in Configuration parameters.
Prometheus
The Prometheus sink is on by default: the retention time defaults to 24h. Set it explicitly and disable the hostname prefix so that metric names match across all nodes:
telemetry {
prometheus_retention_time = "24h"
disable_hostname = true
}prometheus_retention_time— how long metrics are retained for Prometheus output. The value0disables the Prometheus format.disable_hostname— do not prefix metrics with the node hostname.metrics_prefix— metric name prefix. By default, metrics have thestrongholdprefix.
Then configure access to the endpoint, see Accessing sys/metrics.
Push sinks
Push sinks send metrics to an external collector. Each sink is enabled by the presence of its address (or credentials) in the telemetry block.
StatsD (UDP):
telemetry {
statsd_address = "127.0.0.1:8125"
disable_hostname = true
}Statsite (TCP):
telemetry {
statsite_address = "127.0.0.1:8125"
disable_hostname = true
}DogStatsD:
telemetry {
dogstatsd_addr = "127.0.0.1:8125"
dogstatsd_tags = ["env:prod", "service:stronghold"]
disable_hostname = true
}Circonus. The sink is enabled when circonus_api_token or circonus_submission_url is set:
telemetry {
circonus_api_token = "<API_TOKEN>"
circonus_api_app = "stronghold"
circonus_submission_interval = "10s"
circonus_check_tags = "env:prod"
circonus_broker_select_tag = "dc:dc1"
disable_hostname = true
}In this example, <API_TOKEN> is the Circonus API token.
Google Stackdriver (Cloud Monitoring). The sink is enabled when stackdriver_project_id is set; credentials are taken from the process environment (Application Default Credentials, for example the GOOGLE_APPLICATION_CREDENTIALS variable):
telemetry {
stackdriver_project_id = "my-project"
stackdriver_location = "europe-west1"
stackdriver_namespace = "stronghold-prod"
disable_hostname = true
}If you only need push delivery, you can disable the Prometheus sink with prometheus_retention_time = 0.
Restart the service after changing the configuration (standalone):
systemctl restart strongholdVerifying metrics collection
Standalone, on the node (use a token with the read capability on sys/metrics, or the listener with unauthenticated_metrics_access):
curl -s --header "X-Vault-Token: ${STRONGHOLD_TOKEN}" "${STRONGHOLD_ADDR}/v1/sys/metrics?format=prometheus" | grep '^stronghold_core_unsealed'Expected output:
stronghold_core_unsealed 1Check collection in Prometheus:
Open the Status → Targets page (or
/targets) and make sure that thestrongholdjob targets are in theUPstate.Run a query, for example:
stronghold_core_unsealedThe result must contain one series per node.
For push sinks, check the receiving side: the metrics must appear with the prefix set in metrics_prefix (stronghold by default). The server prints sink initialization errors at startup (Error initializing telemetry). To check DP, see Verifying collection in DP.
Accessing sys/metrics
The GET /sys/metrics endpoint returns metrics of the node that received the request. It is registered at the exact path /v1/sys/metrics of each listener.
Response formats
Rules for choosing the format:
- the
formatquery parameter:prometheusreturns the Prometheus text format, any other non-empty value returns400with the textmetric response format "..." unknown; - if
formatis not set, theAcceptheader is checked: if the firstAcceptvalue starts withapplication/openmetrics-text, or anyAcceptvalue containsprometheus/telemetry, the Prometheus format is returned; - otherwise the response is JSON.
| Format | How to request | Content-Type | Content |
|---|---|---|---|
| Prometheus | ?format=prometheus (or the Accept header, see above) | Prometheus text format | Metrics gathered over the retention period prometheus_retention_time |
| JSON | no format | application/json | A summary of the latest completed 10-second interval of the in-memory sink |
To get the metrics in the Prometheus format, run the following command:
curl --header "X-Vault-Token: ${STRONGHOLD_TOKEN}" "${STRONGHOLD_ADDR}/v1/sys/metrics?format=prometheus"Example output:
# HELP stronghold_core_handle_request stronghold_core_handle_request
# TYPE stronghold_core_handle_request summary
stronghold_core_handle_request{quantile="0.5"} 0.071
stronghold_core_handle_request{quantile="0.9"} 0.139
stronghold_core_handle_request{quantile="0.99"} 0.612
stronghold_core_handle_request_sum 1203.4
stronghold_core_handle_request_count 8417
# HELP stronghold_core_unsealed stronghold_core_unsealed
# TYPE stronghold_core_unsealed gauge
stronghold_core_unsealed 1To get the metrics in the JSON format, run the following command:
curl --header "X-Vault-Token: ${STRONGHOLD_TOKEN}" "${STRONGHOLD_ADDR}/v1/sys/metrics"Example output (the values are illustrative):
{
"Timestamp": "2026-01-15 10:21:30 +0000 UTC",
"Gauges": [
{ "Name": "stronghold.core.unsealed", "Value": 1, "Labels": {} },
{ "Name": "stronghold.token.count", "Value": 12, "Labels": { "namespace": "root" } }
],
"Points": [],
"Counters": [
{
"Name": "stronghold.audit.log_request_failure",
"Count": 0, "Rate": 0, "Sum": 0, "Min": 0, "Max": 0,
"Mean": 0, "Stddev": 0, "Labels": {}
}
],
"Samples": [
{
"Name": "stronghold.core.handle_request",
"Count": 42, "Rate": 4.2, "Sum": 3.1, "Min": 0.04, "Max": 0.3,
"Mean": 0.074, "Stddev": 0.05, "Labels": {}
}
]
}In the JSON response, names are separated by dots; in the Prometheus format, by underscores. Gauges hold gauges, Counters hold counters, and Samples hold timers and sampled values (the summary type in Prometheus).
Access requirements
- Token. By default, the request requires a token (the
X-Vault-TokenorAuthorization: Bearerheader) whose policy grants thereadcapability on thesys/metricspath. Thesudocapability is not required. Without a token, the response is403(permission denied). - Unsealed node. A request with a token to a sealed node returns
503. - Standby nodes. The request is not forwarded to the active node, because the token can only be validated on the active node. A standby node responds to a request with a token with the
307code and theLocationheader pointing to the active node. Prometheus follows redirects by default, so scraping a standby node by token returns the metrics of the active node. To get the metrics of each node, use unauthenticated access. - TLS. The endpoint is served by the listener over the protocol configured for it: if TLS is enabled for the listener, use HTTPS and specify the CA certificate (
--cacert). - Method.
GETonly for the unauthenticated variant: other methods return405. - Prometheus sink disabled. With
prometheus_retention_time = 0, a request withformat=prometheusreturns400with the textprometheus is not enabled. The JSON format keeps working.
Token-based access
Create a dedicated least-privilege policy for the metrics collector:
path "sys/metrics" {
capabilities = ["read"]
}Save the policy to the prometheus-metrics.hcl file, write it to Stronghold and create a token with this policy:
d8 stronghold policy write prometheus-metrics prometheus-metrics.hcl
d8 stronghold token create -policy=prometheus-metrics -orphan -period=24hUse a periodic token and set up its renewal, or issue the token through an auth method (for example, Kubernetes or AppRole).
Unauthenticated access
If metrics are collected from a trusted network, allow unauthenticated access at the listener level:
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/stronghold/tls/node-1-cert.pem"
tls_key_file = "/opt/stronghold/tls/node-1-key.pem"
telemetry {
unauthenticated_metrics_access = true
}
}Features of the unauthenticated_metrics_access parameter:
- the default value is
false; - it applies only to the listener in whose
telemetryblock it is set, and only to the/v1/sys/metricspath (othersys/paths still require a token); - the request is processed by the node itself without checking a token or forwarding, so standby nodes (and sealed ones) also return their own metrics;
- only the
GETmethod is allowed.
Metrics disclose information about the storage layout (mount paths, lease and token counts). Allow unauthenticated access only when network access to the listener is restricted, for example, with a separate listener bound to a management address.
Prometheus scrape job example
The following Prometheus scrape job collects metrics from three cluster nodes with token-based access:
scrape_configs:
- job_name: stronghold
metrics_path: /v1/sys/metrics
params:
format: ["prometheus"]
scheme: https
tls_config:
ca_file: /etc/prometheus/stronghold-ca.pem
authorization:
credentials_file: /etc/prometheus/stronghold-token
static_configs:
- targets:
- raft-node-1.demo.tld:8200
- raft-node-2.demo.tld:8200
- raft-node-3.demo.tld:8200Scrape every cluster node rather than only the load balancer address: some metrics (Raft, runtime) are node-specific. Take into account the standby node behavior described above: with token-based access, targets that are standby nodes are redirected to the active node.
Monitoring in DP
In DP, Stronghold runs in the d8-stronghold namespace, and its configuration (including telemetry) is managed by the module through the stronghold-config ConfigMap. Metrics are collected as follows:
- the telemetry settings set
metrics_prefix = "stronghold"(and alsoprometheus_retention_time = "1m"anddisable_hostname = true), so all metrics have thestronghold_prefix (for example,stronghold_core_unsealed); - metrics are served by a loopback listener
127.0.0.1:8400(without TLS, withunauthenticated_metrics_access = true) that is not reachable from outside the pod; - the
kube-rbac-proxysidecar publishes them on port9889(https-metrics, path/metrics; it forwards the request to/v1/sys/metrics?format=prometheusof the loopback listener); access requires thegetpermission onstatefulsets/prometheus-metrics(thestrongholdresource, theappsAPI group) in thed8-strongholdnamespace. The module creates theaccess-to-stronghold-metricsRole with this permission and, if theprometheusmodule is enabled, binds it to theprometheusServiceAccount ind8-monitoringand to thed8-monitoring:scraperuser.
The module creates the monitoring objects itself, no manual setup is required:
- the
strongholdPodMonitor in thed8-monitoringnamespace (created only if themonitoring.coreos.com/v1PodMonitor CRD is present in the cluster); - a PrometheusRule with the module alerts;
- three Grafana dashboards:
stronghold.json,replication.jsonandsnapshot_auto.json.
Use this variant when the DP prometheus module is enabled: nothing else is required.
Verifying collection in DP
To check that the metrics collection object exists, run the following command:
d8 k -n d8-monitoring get podmonitor strongholdTo check that Prometheus collects the metrics, open the Prometheus UI (Status → Targets): the d8-monitoring/stronghold job must have one target in the UP state per Stronghold pod. Then run a query:
stronghold_core_unsealed{job="d8-monitoring/stronghold"}Manual metrics request in DP
To get the metrics manually, send a request to port 9889 of a pod from inside the cluster with a ServiceAccount token that has the get permission on statefulsets/prometheus-metrics. Grant the permission by binding the identity to the module Role:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: access-to-stronghold-metrics-my-sa
namespace: d8-stronghold
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: access-to-stronghold-metrics
subjects:
- kind: ServiceAccount
name: my-sa
namespace: my-namespaceThen send the request from a pod that uses this ServiceAccount. The proxy uses its own certificate, so the command uses --insecure, like the module PodMonitor:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl --insecure --header "Authorization: Bearer ${TOKEN}" "https://<POD_IP>:9889/metrics"In this command, <POD_IP> is the IP address of a Stronghold pod. To get the pod IP addresses, run the following command:
d8 k -n d8-stronghold get pods -l app.kubernetes.io/name=stronghold,component=server -o wideExternal Prometheus in DP
The module configures metrics collection only for the Prometheus of the DP monitoring stack. An external Prometheus can collect metrics if:
- it can reach port
9889of the Stronghold pods over HTTPS; - it scrapes the
/metricspath and sends a Kubernetes bearer token of an identity with thegetpermission onstatefulsets/prometheus-metrics(see the RoleBinding above); - you create the scrape configuration yourself: the module PodMonitor is meant for the
mainPrometheus of the DP monitoring stack.
Pushing metrics to StatsD, DogStatsD, Circonus, and Stackdriver is not available in DP, because the telemetry block is generated by the module and cannot be changed through the module parameters.
Module alerts in DP
The alerts are defined in the module’s PrometheusRule and use the severity_level label, as DP alerts do:
| Alert | Condition | for | severity_level |
|---|---|---|---|
D8StrongholdNoReadyPod | No pod of the stronghold StatefulSet is in the Ready state | 3m | 4 |
D8StrongholdNoActiveNodes | stronghold_core_active is 0 on all nodes (or there are no metrics) | 1m | 3 |
D8StrongholdSealedNodesPresent | The number of nodes exporting metrics is greater than the number of unsealed nodes | 5m | 7 |
D8StrongholdClusterNotHealthy | The sum of stronghold_autopilot_healthy is 0 | 5m | 7 |
D8StrongholdQuorumInCriticalState | stronghold_autopilot_failure_tolerance is 0; created only if the cluster has more than 2 master nodes (clusterMasterCount > 2) | 3m | 4 |
D8StrongholdAbsentMetrics | The number of ready kube-rbac-proxy containers is greater than the number of nodes exporting metrics | 1m | 3 |
D8StrongholdAutoSnapshotFailed | stronghold_core_snapshot_auto_failed is 1 for an automatic snapshot configuration | 5m | 6 |
D8StrongholdAutoSnapshotRotationFailed | stronghold_autosnapshots_rotate_failed is 1: rotation of old snapshots failed after a successful snapshot | 15m | 8 |
Custom alerts in DP are defined with CustomPrometheusRules: move the spec.groups content from the examples below into it. For example, to get alerts on automatic snapshot failures, use the stronghold.snapshots group.
Module parameters affecting monitoring
The module has no dedicated monitoring parameters. These settings affect monitoring and alerts:
| Parameter | Effect |
|---|---|
global.discovery.clusterMasterCount (cluster parameter, not a module one) | The D8StrongholdQuorumInCriticalState alert is created only if the value is greater than 2 |
diskSizeGigabytes | The extended-monitoring module automatically monitors the used disk space percentage |
enableAuditLog | Enables the audit log (Stronghold EE only; requires management.mode = Automatic). Audit device failures are visible through the stronghold_audit_log_*_failure metrics |
The telemetry block of the DP configuration is generated by the module and contains only the following values: prometheus_retention_time = "1m", disable_hostname = true, metrics_prefix = "stronghold". The listener 127.0.0.1:8400 has unauthenticated_metrics_access = true. Other telemetry parameters are not available in DP.
Configuration parameters
The telemetry block
Parameters of the telemetry block of the server configuration file (standalone). In DP, these parameters cannot be changed, see Module parameters affecting monitoring.
| Parameter | Type | Default | Allowed values and meaning |
|---|---|---|---|
prometheus_retention_time | duration | 24h | How long metrics are retained for the Prometheus format. A duration string ("24h", "30m", "2d") or a number of seconds. 0 disables the Prometheus sink: a format=prometheus request returns 400 |
disable_hostname | boolean | false | true — do not add the hostname to the metric name, so that metric names are the same on all nodes. If a single sink is enabled, the hostname is not added regardless of the value |
enable_hostname_label | boolean | false | true — add the hostname as the host label instead of a name prefix |
metrics_prefix | string | stronghold | The prefix of all metric names |
usage_gauge_period | duration | 10m | The period of collecting usage gauges (tokens, leases, KV secrets, identity entities). "none" or 0 disables collection. The gauges are collected on the active node only (not on standby nodes and DR secondary clusters) |
maximum_gauge_cardinality | integer | 500 | The maximum number of label combinations that one usage gauge emits per collection; when exceeded, the combinations with the largest values are kept |
lease_metrics_epsilon | duration | 1h | The size of the time bucket used to aggregate leases by expiration time. "none" sets 0 |
num_lease_metrics_buckets | integer | 168 | The number of time buckets for lease aggregation |
add_lease_metrics_namespace_labels | boolean | false | true — add the namespace label to the lease expiration metrics |
filter_default | boolean | true | Whether to allow metrics that do not match any prefix_filter rule |
prefix_filter | list of strings | empty | Metric filter rules by name prefix. A rule starts with + (allow) or - (block), followed by a prefix. The prefix is matched against the full name with dots, including metrics_prefix, for example "-stronghold.expire". An empty rule or a rule without +/- is a configuration error |
statsd_address | string | empty | The StatsD address (host:port, UDP). Enables the StatsD sink |
statsite_address | string | empty | The Statsite address (host:port, TCP). Enables the Statsite sink |
dogstatsd_addr | string | empty | The DogStatsD address. Enables the DogStatsD sink |
dogstatsd_tags | list of strings | empty | Global tags for every DogStatsD packet, in the name:value format |
circonus_api_token | string | empty | The Circonus API token. The Circonus sink is enabled if this or circonus_submission_url is set |
circonus_submission_url | string | empty | The submission URL of a previously created HTTPTRAP check |
circonus_api_app | string | stronghold | The application name associated with the API token |
circonus_api_url | string | Circonus library default | The base URL of the Circonus API |
circonus_submission_interval | string | Circonus library default (10s) | The interval of sending metrics |
circonus_check_id | string | empty | The ID of a previously created check |
circonus_check_force_metric_activation | string | false | Force activation of metrics that already exist in the check |
circonus_check_instance_id | string | Circonus library default | An identifier of this instance in the check |
circonus_check_search_tag | string | service:stronghold | A tag used to search for an existing check |
circonus_check_tags | string | empty | A comma-separated list of check tags |
circonus_check_display_name | string | Stronghold | The check name in the Circonus UI |
circonus_broker_id | string | empty | The ID of the broker used to create a check |
circonus_broker_select_tag | string | empty | A tag used to select a broker if circonus_broker_id is not set |
stackdriver_project_id | string | empty | The Google Cloud project. Enables the Stackdriver sink |
stackdriver_location | string | empty | The GCP or AWS region of the monitored resource |
stackdriver_namespace | string | empty | The namespace identifier, for example, a cluster name |
stackdriver_debug_logs | boolean | false | Write additional Stackdriver debug logs to stderr |
Duration parameters accept a string with a unit suffix (s, m, h, and d for days) or an integer number of seconds.
The circonus_* parameters are described in more detail in the Circonus gometrics documentation.
Listener telemetry block
The telemetry block inside listener "tcp" has its own parameter:
| Parameter | Type | Default | Meaning |
|---|---|---|---|
unauthenticated_metrics_access | boolean | false | true — the /v1/sys/metrics endpoint of this listener is available without a token. See Unauthenticated access |
Key metrics
Metric names are given with the stronghold_ prefix: Stronghold uses it by default in both standalone installations and DP. You can change the prefix with the metrics_prefix parameter of the telemetry block. The full list of metrics is in the Metrics catalog.
The type is given as in the Prometheus format: the summary type includes the quantile label (0.5, 0.9, 0.99) and the _sum and _count series.
| Metric | Type | Labels | Meaning | What to watch |
|---|---|---|---|---|
stronghold_core_unsealed | gauge | none | Node is unsealed (1) or sealed (0) | Any 0 value |
stronghold_core_active | gauge | none | Node is active (1) | Cluster-wide sum must equal 1 |
stronghold_core_handle_request | summary | none | Request handling time, ms | Growth of the 99th percentile |
stronghold_core_handle_login_request | summary | none | Login request handling time, ms | Growth of the 99th percentile |
stronghold_expire_num_leases | gauge | none | Number of active leases | Steady growth without decrease |
stronghold_token_count | gauge | namespace | Number of tokens (emitted by the active node only, once per usage_gauge_period) | Steady growth without decrease |
stronghold_audit_log_request_failure | counter | none | Failures writing requests to audit devices | Any non-zero increase |
stronghold_audit_log_response_failure | counter | none | Failures writing responses to audit devices | Any non-zero increase |
stronghold_raft_leader_lastContact | summary | none | Time since the Raft leader last contacted a follower, ms. Emitted by the leader, a sample for each follower | Values above hundreds of milliseconds |
stronghold_raft_state_candidate | counter | none | Node transitions to the Raft candidate state | Frequent increases indicate unstable leader election |
stronghold_raft_commitTime | summary | none | Raft commit time on the leader, ms | Growth indicates slow disk or network |
stronghold_autopilot_healthy | gauge | none | Cluster health as reported by Autopilot (emitted by the leader) | Value 0 |
stronghold_autopilot_failure_tolerance | gauge | none | The number of nodes that can fail without losing quorum (emitted by the leader) | Value 0 |
stronghold_runtime_alloc_bytes | gauge | none | Memory used by the process | Approaching the memory limit |
stronghold_autosnapshots_save_errors | counter | snapshot_config_name | Failed automatic snapshot attempts (Stronghold EE) | Any non-zero increase |
stronghold_autosnapshots_last_success_time | gauge | snapshot_config_name | Time of the last successful automatic snapshot, Unix timestamp in seconds (Stronghold EE) | Older than the snapshot interval of the configuration |
stronghold_core_snapshot_auto_failed | gauge | config | Automatic snapshot failure flag, used by the D8StrongholdAutoSnapshotFailed alert (Stronghold EE) | Value 1 |
stronghold_autosnapshots_rotate_failed | gauge | snapshot_config_name | Snapshot rotation failure flag, used by the D8StrongholdAutoSnapshotRotationFailed alert (Stronghold EE) | Value 1 |
Alerting rule examples
A PrometheusRule example with a basic set of alerts. It is a template for standalone installations: it uses the severity label and standalone paths. For DP, see Module alerts in DP. Tune the thresholds to your workload.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: stronghold
spec:
groups:
- name: stronghold.availability
rules:
- alert: StrongholdSealed
expr: stronghold_core_unsealed == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Stronghold node {{ $labels.instance }} is sealed"
description: "The node does not serve requests. Unseal it or check auto unseal."
- alert: StrongholdNoActiveNode
expr: sum(stronghold_core_active) < 1
for: 2m
labels:
severity: critical
annotations:
summary: "The Stronghold cluster has no active node"
description: "Check Raft and quorum state with d8 stronghold operator raft list-peers."
- alert: StrongholdLeaderFlapping
expr: increase(stronghold_raft_state_candidate[15m]) > 3
labels:
severity: warning
annotations:
summary: "Frequent Raft leader elections on {{ $labels.instance }}"
description: "Check network connectivity between nodes and disk latency."
- alert: StrongholdAutopilotUnhealthy
expr: stronghold_autopilot_healthy == 0
for: 5m
labels:
severity: warning
annotations:
summary: "Autopilot reports the Stronghold cluster as unhealthy"
- name: stronghold.performance
rules:
- alert: StrongholdHighRequestLatency
expr: stronghold_core_handle_request{quantile="0.99"} > 500
for: 10m
labels:
severity: warning
annotations:
summary: "99th percentile request handling time is above 500 ms"
- alert: StrongholdLeaseCountGrowth
expr: delta(stronghold_expire_num_leases[1h]) > 10000
labels:
severity: warning
annotations:
summary: "Lease count grew by more than 10000 in an hour"
description: "Check lease TTLs and clients that do not reuse tokens."
- alert: StrongholdLeaseCountHigh
expr: stronghold_expire_num_leases > 250000
for: 15m
labels:
severity: warning
annotations:
summary: "Active lease count exceeds 250000"
- name: stronghold.audit
rules:
- alert: StrongholdAuditFailures
expr: increase(stronghold_audit_log_request_failure[5m]) > 0 or increase(stronghold_audit_log_response_failure[5m]) > 0
labels:
severity: critical
annotations:
summary: "Stronghold audit device write failures"
description: "If all audit devices fail, Stronghold stops serving requests."
- name: stronghold.snapshots
rules:
- alert: StrongholdAutoSnapshotFailed
expr: increase(stronghold_autosnapshots_save_errors[1h]) > 0
labels:
severity: warning
annotations:
summary: "Automatic snapshot {{ $labels.snapshot_config_name }} failed"
description: "Check the server logs and the snapshot storage."
- name: stronghold.certificates
rules:
- alert: StrongholdTLSCertificateExpiringSoon
expr: (probe_ssl_earliest_cert_expiry{job="stronghold-tls"} - time()) / 86400 < 21
for: 1h
labels:
severity: warning
annotations:
summary: "Stronghold TLS certificate on {{ $labels.instance }} expires in less than 21 days"
- name: stronghold.storage
rules:
- alert: StrongholdRaftDiskUsageHigh
expr: |
(1 - node_filesystem_avail_bytes{mountpoint="/opt/stronghold/data"}
/ node_filesystem_size_bytes{mountpoint="/opt/stronghold/data"}) > 0.8
for: 15m
labels:
severity: warning
annotations:
summary: "Raft data volume on {{ $labels.instance }} is more than 80% full"Notes on the example:
- The TLS expiry alert uses the
probe_ssl_earliest_cert_expirymetric from blackbox_exporter probing the Stronghold HTTPS address. Monitor root and intermediate certificates issued by the Stronghold PKI engine the same way, for example with a certificate exporter that checks files or service addresses. - The disk usage alert uses node-exporter metrics. Specify the mount point of the directory set in the
pathparameter of thestorage "raft"block. In DP, the data of each replica is stored in thedata-stronghold-NPVC, which is mounted at/stronghold/datainside the pod. If thestorageClassparameter is not set in the module configuration, the PVC is bound to a local PV in the/var/lib/deckhouse/stronghold/<LOCAL_PATH_UUID>directory on a master or arbiter node: specify the mount point of the file system that contains this directory. IfstorageClassis set, the volume is provisioned by that storage class, and the example’s mount point does not apply. - For DP, move
spec.groupsinto CustomPrometheusRules.
Stronghold does not export PKI certificate expiry metrics (only the tidy metrics secrets_pki_tidy_* exist). Monitor certificate expiry with external tools, for example blackbox-exporter.
Health checks via sys/health
The GET /sys/health endpoint requires no authentication and is suitable for load balancer and external monitoring checks:
curl -s -o /dev/null -w "%{http_code}\n" "${STRONGHOLD_ADDR}/v1/sys/health"Response codes:
| Code | State |
|---|---|
200 | Initialized, unsealed, active node |
429 | Unsealed, standby node |
472 | Node is a DR replication secondary |
473 | Performance standby node |
423 | The active node is not ready yet (only with isleaderreadyok=true) |
425 | Raft Autopilot is not ready yet (only with raftautopilotok=true) |
501 | Not initialized |
503 | Sealed |
Response codes can be overridden with query parameters:
standbyok=true— return200for standby nodes. Use it for a load balancer that distributes requests across all unsealed nodes;activecode,standbycode,sealedcode,uninitcode,drsecondarycode,performancestandbycode,isleaderreadycode,raftautopilotcode— set a custom code for the corresponding state;perfstandbyok=true— return200for performance standby nodes.
Example check for a load balancer that should route traffic to the active node only:
curl -s -o /dev/null -w "%{http_code}\n" "${STRONGHOLD_ADDR}/v1/sys/health?standbycode=503"To check Raft state, use:
d8 stronghold operator raft list-peers
d8 stronghold operator raft autopilot state