Blackbox exporter and Prometheus Probes for endpoint monitoring
Published: 2026-05-06
Prometheus scrapes metrics from running pods. But it doesn't know if external URLs are reachable, if TLS certificates are about to expire, or if an HTTP endpoint returns 200. That's the job of the blackbox exporter combined with Probe objects from the Prometheus Operator.
Components
prometheus-blackbox-exporter— runs HTTP/HTTPS/TCP checks on demand and exposes the results as Prometheus metrics.ProbeCRD — tells the Prometheus Operator to scrape a list of URLs via the blackbox exporter on a schedule.
Both are deployed per-cluster. The blackbox exporter lives in base/monitoring/exporters/blackbox-exporter/. Probe objects are in spoke/monitoring/.
Blackbox exporter HelmRelease
yamlapiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: blackbox-exporter
namespace: observability
spec:
chart:
spec:
chart: prometheus-blackbox-exporter
version: ">=8.0.0"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
interval: 1h
values:
config:
modules:
http_2xx:
prober: http
timeout: 10s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: [200, 301, 302]
follow_redirects: true
tls_config:
insecure_skip_verify: false
http_2xx_insecure:
prober: http
timeout: 10s
http:
valid_status_codes: [200]
tls_config:
insecure_skip_verify: true
tcp_connect:
prober: tcp
timeout: 5s
icmp:
prober: icmp
timeout: 5s
serviceMonitor:
enabled: true
labels:
release: kube-prom-stack # must match Prometheus selector
Two HTTP modules: http_2xx for public-cert endpoints and http_2xx_insecure for internal services with self-signed certs.
Probe objects
Probe objects are in the spoke/monitoring/ layer — a separate Kustomization (dev-monitoring-spoke) that runs after dev-spoke because Probe CRDs come from kube-prom-stack.
yamlapiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
name: external-endpoints
namespace: observability
spec:
interval: 60s
module: http_2xx
prober:
url: blackbox-exporter.observability.svc.cluster.local:9115
targets:
staticConfig:
static:
- https://elasticsearch.dev.test.example.com
- https://prometheus.dev.test.example.com
- https://headlamp.dev.test.example.com
- https://links.dev.test.example.com
relabelingConfigs:
- sourceLabels: [__address__]
targetLabel: instance
yamlapiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
name: internal-endpoints
namespace: observability
spec:
interval: 30s
module: tcp_connect
prober:
url: blackbox-exporter.observability.svc.cluster.local:9115
targets:
staticConfig:
static:
- elasticsearch-master.elasticsearch.svc.cluster.local:9200
- prometheus-operated.observability.svc.cluster.local:9090
Key metrics
The blackbox exporter exposes:
| Metric | What it means |
|---|---|
probe_success |
1 if probe succeeded, 0 if failed |
probe_duration_seconds |
total time for the probe |
probe_http_status_code |
HTTP status code returned |
probe_ssl_earliest_cert_expiry |
Unix timestamp of earliest cert expiry |
probe_dns_lookup_time_seconds |
DNS resolution latency |
probe_http_content_length |
bytes in response body |
Query examples:
promql# Endpoints that are currently down
probe_success == 0
# Days until TLS cert expires
(probe_ssl_earliest_cert_expiry - time()) / 86400
# Average probe duration per endpoint
avg_over_time(probe_duration_seconds[1h])
Alerting rules
yamlapiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: blackbox-alerts
namespace: observability
labels:
release: kube-prom-stack
spec:
groups:
- name: blackbox
rules:
- alert: EndpointDown
expr: probe_success == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Endpoint {{ $labels.instance }} is down"
description: "Probe has been failing for 2 minutes"
- alert: TLSCertExpiringSoon
expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 14
for: 1h
labels:
severity: warning
annotations:
summary: "TLS cert for {{ $labels.instance }} expires in < 14 days"
description: "Days remaining: {{ $value | humanizeDuration }}"
- alert: SlowEndpoint
expr: probe_duration_seconds > 5
for: 5m
labels:
severity: warning
annotations:
summary: "Endpoint {{ $labels.instance }} responding slowly"
EndpointDown fires after 2 minutes of failed probes to avoid noise from transient failures. TLSCertExpiringSoon gives 14 days of warning before certificate expiry.
Why a separate Kustomization for monitoring
Probe CRDs are installed by kube-prom-stack. If dev-monitoring-spoke ran at the same time as dev-spoke, the Probe objects might apply before the CRD exists, causing a reconcile error.
dependsOn: [dev-spoke] ensures the monitoring layer waits for the spoke layer (including kube-prom-stack installation) before applying Probe and PrometheusRule objects.
yaml# dev-monitoring-spoke Kustomization
spec:
interval: 10m
path: ./fluxcd/projects/dev/kustomization/spoke/monitoring/
dependsOn:
- name: dev-spoke
namespace: dev
Debugging probe failures
Manually hit the blackbox exporter to see exactly what it's reporting:
bash# Check if a URL passes the http_2xx module
kubectl exec -n observability deploy/blackbox-exporter -- \
wget -qO- "http://localhost:9115/probe?module=http_2xx&target=https://example.com"
# Or from outside the cluster via port-forward
kubectl port-forward -n observability svc/blackbox-exporter 9115:9115
curl "http://localhost:9115/probe?module=http_2xx&target=https://example.com"
The output is raw Prometheus text format — probe_success 1 means the check passed, probe_success 0 means it failed, and the other metrics explain why.
Summary
- Blackbox exporter +
ProbeCRDs replace custom ping scripts with proper Prometheus-native endpoint monitoring - Two modules:
http_2xxfor public TLS endpoints,http_2xx_insecurefor internal services with self-signed certs probe_ssl_earliest_cert_expirygives automatic TLS expiry alerting — no manual tracking needed- Probe objects go in a separate
spoke/monitoring/Kustomization withdependsOn: [dev-spoke]to wait for kube-prom-stack CRDs - Debug probe failures by hitting
http://localhost:9115/probe?module=http_2xx&target=<url>directly on the blackbox exporter