Grafana Alloy is the OpenTelemetry-native observability collector that pipelines metrics, traces, and logs to your backends. When Alloy is healthy, it's invisible — data flows. When Alloy fails, the damage is invisible too: your dashboards go stale, your traces stop, your logs stop arriving, and your alerts stop firing. You need a monitoring layer outside your Alloy pipeline to watch the pipeline itself. Vigilmon is that external layer — HTTP health checks and heartbeats that tell you when Alloy goes dark before your dashboards do.
What You'll Set Up
- Alloy process health and readiness monitoring
- Component health status per pipeline stage
- Scrape success rate heartbeats
- OTLP ingestion throughput tracking
- Data export success rate monitoring
- WAL queue depth alerting
- Cluster health monitoring (if clustering enabled)
- Configuration reload success tracking
- Memory usage monitoring
Prerequisites
- Grafana Alloy installed (standalone, Docker, or Kubernetes)
- Alloy UI accessible (default port 12345)
- A free Vigilmon account
Why Monitoring Alloy Matters
Alloy occupies a uniquely dangerous position in your stack: it's the component responsible for all observability data collection. A crashed Alloy process creates a silent observability blind spot — your metrics dashboards go stale, your trace backends stop receiving spans, and your log aggregation platform stops receiving logs. Meanwhile, every downstream alert that depends on those signals stops firing. The failure is self-concealing.
The only reliable way to monitor an observability pipeline is with a monitoring layer that operates outside that pipeline. Vigilmon's HTTP checks and heartbeats are independent of Alloy — they'll alert you even when Alloy is completely down and your internal observability is dark.
Key Metrics to Monitor
| Metric | Why It Matters | Alert Threshold | |--------|---------------|-----------------| | Alloy HTTP liveness (/readyz) | Process is alive | Any failure | | Alloy readiness (/-/healthy) | Pipeline is ready | Any failure | | Component health per stage | Individual component failures | Any unhealthy | | Scrape success rate | Metrics collection coverage | < 95% | | OTLP received spans/min | Trace ingestion | Drop to zero | | OTLP received metrics/min | Metrics ingestion | Drop to zero | | Export success rate per exporter | Data reaching backends | < 99% | | WAL queue depth | Export backend backpressure | Growing trend | | Cluster member count | Horizontal scale coverage | Below expected | | Configuration reload success | Hot-reload failures | Any failure | | Process RSS memory | Cardinality explosion | Growing unboundedly |
Step 1: Monitor Alloy Liveness and Readiness
Alloy exposes two built-in HTTP endpoints on its UI port (default 12345):
/:12345/-/healthy— returns 200 when the Alloy process is healthy/:12345/readyz— returns 200 when Alloy is ready to serve traffic
Add two Vigilmon HTTP monitors:
Monitor 1 — Liveness:
- In Vigilmon, click Add Monitor → HTTP / HTTPS.
- URL:
http://your-alloy-host:12345/-/healthy - Check interval:
1 minute - Expected status:
200 - Click Save.
Monitor 2 — Readiness:
- Click Add Monitor → HTTP / HTTPS.
- URL:
http://your-alloy-host:12345/readyz - Check interval:
1 minute - Expected status:
200 - Click Save.
If Alloy runs on a private network (not publicly accessible), use a Vigilmon heartbeat instead: a probe script running on the same host sends periodic pings. See Step 2 for the heartbeat pattern.
Alert condition: immediate notification on any failure — an Alloy liveness failure means your entire observability pipeline is dark.
Step 2: Monitor Component Health Per Pipeline Stage
Alloy exposes component status via its API. Each component in your River/Alloy DSL pipeline has an individual health state. A single failed component (e.g., a Prometheus scrape job or an OTLP exporter) can silently drop data for that pipeline branch.
Query component health via Alloy's API and send a heartbeat to Vigilmon:
#!/bin/bash
# /usr/local/bin/alloy-component-check.sh
ALLOY_HOST="http://localhost:12345"
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_COMPONENT_HEARTBEAT_ID"
# Fetch component health from Alloy API
COMPONENTS=$(curl -s "$ALLOY_HOST/api/v0/web/components" 2>/dev/null)
if [ -z "$COMPONENTS" ]; then
echo "Failed to fetch component status — Alloy may be down"
exit 1
fi
# Check for any unhealthy components
UNHEALTHY=$(echo "$COMPONENTS" | python3 -c "
import sys, json
data = json.load(sys.stdin)
unhealthy = [c['id'] for c in data.get('components', []) if c.get('health', {}).get('state') not in ['healthy', 'exited']]
print(len(unhealthy))
if unhealthy:
print('Unhealthy:', unhealthy, file=sys.stderr)
" 2>&1)
UNHEALTHY_COUNT=$(echo "$UNHEALTHY" | head -1)
if [ "$UNHEALTHY_COUNT" -eq 0 ]; then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "All components healthy"
else
echo "ALERT: $UNHEALTHY_COUNT unhealthy components"
# Heartbeat NOT sent — Vigilmon alerts
fi
Add to cron (every 2 minutes):
*/2 * * * * /usr/local/bin/alloy-component-check.sh
Configure the Vigilmon heartbeat with a 5-minute grace period — if not received within 5 minutes, alert immediately.
Step 3: Monitor Scrape Success Rate
If Alloy is collecting Prometheus metrics via prometheus.scrape components, scrape failures silently drop data for affected targets. Monitor scrape success rate:
#!/bin/bash
# /usr/local/bin/alloy-scrape-check.sh
ALLOY_HOST="http://localhost:12345"
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_SCRAPE_HEARTBEAT_ID"
# Alloy exposes Prometheus metrics including scrape stats
# Query the self-metrics endpoint
METRICS=$(curl -s "$ALLOY_HOST/metrics" 2>/dev/null)
# Extract scrape success/failure counts
SUCCESS=$(echo "$METRICS" | grep 'prometheus_target_scrape_pool_reloads_total' | tail -1 | awk '{print $2}' || echo "0")
FAILED=$(echo "$METRICS" | grep 'prometheus_target_scrape_pool_reloads_failed_total' | tail -1 | awk '{print $2}' || echo "0")
# Calculate failure rate (simplified — check last-scrape success)
LAST_SCRAPE_ERRORS=$(echo "$METRICS" | grep 'up{' | grep ' 0$' | wc -l)
LAST_SCRAPE_TOTAL=$(echo "$METRICS" | grep 'up{' | wc -l)
if [ "$LAST_SCRAPE_TOTAL" -gt 0 ]; then
FAILURE_RATE=$(( LAST_SCRAPE_ERRORS * 100 / LAST_SCRAPE_TOTAL ))
if [ "$FAILURE_RATE" -lt 5 ]; then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "Scrape success rate OK: ${FAILURE_RATE}% failure"
else
echo "ALERT: Scrape failure rate ${FAILURE_RATE}% (threshold: 5%)"
fi
else
# No scrape targets configured — send heartbeat (not an error state)
curl -s "$HEARTBEAT_URL" > /dev/null
fi
Alert threshold: scrape failure rate > 5% — some targets are consistently unreachable.
Step 4: Monitor OTLP Ingestion Throughput
If Alloy receives OTLP data (spans, metrics, logs) from your services, a drop in ingestion throughput is a critical signal: your applications may have stopped exporting data, or Alloy's OTLP receiver is failing.
#!/bin/bash
# /usr/local/bin/alloy-otlp-check.sh
ALLOY_HOST="http://localhost:12345"
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_OTLP_HEARTBEAT_ID"
STATE_FILE="/tmp/alloy-otlp-last-count"
# Read current OTLP received count from Alloy self-metrics
METRICS=$(curl -s "$ALLOY_HOST/metrics" 2>/dev/null)
# Extract received spans count (otelcol metrics)
CURRENT_SPANS=$(echo "$METRICS" | grep 'otelcol_receiver_accepted_spans_total' | \
awk '{sum += $2} END {print sum}')
CURRENT_SPANS=${CURRENT_SPANS:-0}
# Compare with last reading
LAST_SPANS=$(cat "$STATE_FILE" 2>/dev/null || echo "0")
echo "$CURRENT_SPANS" > "$STATE_FILE"
DELTA=$(echo "$CURRENT_SPANS - $LAST_SPANS" | bc)
# If we received any new spans in the last check period, all is well
# Adjust threshold based on your expected OTLP ingestion rate
if [ "$DELTA" -gt 0 ] || [ "$LAST_SPANS" -eq 0 ]; then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "OTLP ingestion OK: +${DELTA} spans"
else
echo "ALERT: No new OTLP spans received (throughput dropped to zero)"
# Heartbeat NOT sent
fi
Note: Adjust this check based on your actual traffic pattern. If you have expected quiet periods (e.g., nightly maintenance windows), exclude them from the alerting window.
Step 5: Monitor Data Export Success Rate
Alloy's export components (prometheus.remote_write, loki.write, otelcol.exporter.otlp) can fail independently of the ingestion side. A failed exporter means data is being received but not delivered to your backends. Alloy buffers in its WAL (Write-Ahead Log) but eventually drops data if the backend is unavailable for too long.
#!/bin/bash
# /usr/local/bin/alloy-export-check.sh
ALLOY_HOST="http://localhost:12345"
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_EXPORT_HEARTBEAT_ID"
METRICS=$(curl -s "$ALLOY_HOST/metrics" 2>/dev/null)
# Check Prometheus remote_write failures
RW_FAILED=$(echo "$METRICS" | grep 'prometheus_remote_storage_samples_failed_total' | \
awk '{sum += $2} END {print sum}')
RW_SENT=$(echo "$METRICS" | grep 'prometheus_remote_storage_samples_total' | \
awk '{sum += $2} END {print sum}')
# Check OTLP export failures
OTLP_FAILED=$(echo "$METRICS" | grep 'otelcol_exporter_send_failed_metric_points_total' | \
awk '{sum += $2} END {print sum}')
OTLP_SENT=$(echo "$METRICS" | grep 'otelcol_exporter_sent_metric_points_total' | \
awk '{sum += $2} END {print sum}')
RW_FAILED=${RW_FAILED:-0}
RW_SENT=${RW_SENT:-1}
OTLP_FAILED=${OTLP_FAILED:-0}
OTLP_SENT=${OTLP_SENT:-1}
RW_FAIL_RATE=$(echo "scale=2; $RW_FAILED * 100 / ($RW_SENT + 1)" | bc)
OTLP_FAIL_RATE=$(echo "scale=2; $OTLP_FAILED * 100 / ($OTLP_SENT + 1)" | bc)
# Alert if either export failure rate exceeds 1%
if (( $(echo "$RW_FAIL_RATE < 1" | bc -l) )) && (( $(echo "$OTLP_FAIL_RATE < 1" | bc -l) )); then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "Export OK — RW fail: ${RW_FAIL_RATE}%, OTLP fail: ${OTLP_FAIL_RATE}%"
else
echo "ALERT: Export failures — RW: ${RW_FAIL_RATE}%, OTLP: ${OTLP_FAIL_RATE}%"
fi
Step 6: Monitor WAL Queue Depth
Alloy uses a Write-Ahead Log (WAL) to buffer data when export backends are temporarily unavailable. A growing WAL queue depth indicates that the export backend is unavailable and Alloy is accumulating data it cannot deliver.
#!/bin/bash
# /usr/local/bin/alloy-wal-check.sh
ALLOY_HOST="http://localhost:12345"
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_WAL_HEARTBEAT_ID"
WAL_WARN_THRESHOLD=50000 # samples
METRICS=$(curl -s "$ALLOY_HOST/metrics" 2>/dev/null)
# WAL queue depth
WAL_QUEUE=$(echo "$METRICS" | grep 'prometheus_remote_storage_queue_highest_sent_timestamp_seconds' | \
awk '{print $2}')
WAL_PENDING=$(echo "$METRICS" | grep 'prometheus_remote_storage_pending_samples' | \
awk '{sum += $2} END {print sum}')
WAL_PENDING=${WAL_PENDING:-0}
if [ "$WAL_PENDING" -lt "$WAL_WARN_THRESHOLD" ]; then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "WAL queue OK: $WAL_PENDING pending samples"
else
echo "ALERT: WAL queue depth high: $WAL_PENDING pending samples (threshold: $WAL_WARN_THRESHOLD)"
fi
A growing WAL is a leading indicator of data loss — Alloy will eventually drop the oldest WAL segments to bound disk usage.
Step 7: Monitor Alloy Cluster Health
If you run Alloy in clustering mode (multiple Alloy instances sharing workload), cluster membership changes indicate coverage gaps. A leaving member means that member's scrape targets are unmonitored until another member picks them up.
#!/bin/bash
# /usr/local/bin/alloy-cluster-check.sh
ALLOY_HOST="http://localhost:12345"
EXPECTED_MEMBERS=3 # adjust to your cluster size
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_CLUSTER_HEARTBEAT_ID"
# Fetch cluster peers from Alloy API
PEERS=$(curl -s "$ALLOY_HOST/api/v0/web/peers" 2>/dev/null)
MEMBER_COUNT=$(echo "$PEERS" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(len(data.get('peers', [])) + 1) # +1 for self
" 2>/dev/null || echo "0")
if [ "$MEMBER_COUNT" -ge "$EXPECTED_MEMBERS" ]; then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "Cluster OK: $MEMBER_COUNT members"
else
echo "ALERT: Cluster degraded — expected $EXPECTED_MEMBERS, got $MEMBER_COUNT"
fi
Skip this check if you run Alloy in standalone (non-clustered) mode.
Step 8: Monitor Configuration Reload Success
Alloy supports hot-reloading configuration without restart. A failed configuration reload leaves Alloy running on the previous (potentially stale) configuration. Monitor reload success:
#!/bin/bash
# /usr/local/bin/alloy-reload-check.sh
ALLOY_HOST="http://localhost:12345"
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_RELOAD_HEARTBEAT_ID"
METRICS=$(curl -s "$ALLOY_HOST/metrics" 2>/dev/null)
RELOAD_SUCCESS=$(echo "$METRICS" | grep 'alloy_config_last_reload_successful' | awk '{print $2}')
RELOAD_SUCCESS=${RELOAD_SUCCESS:-1} # Default to 1 if metric not present (no reload attempted)
if [ "$RELOAD_SUCCESS" = "1" ] || [ -z "$RELOAD_SUCCESS" ]; then
curl -s "$HEARTBEAT_URL" > /dev/null
echo "Config reload status: OK"
else
echo "ALERT: Last configuration reload failed"
fi
Run every 5 minutes. Alert on any reload failure — Alloy may be operating on outdated pipeline configuration.
Step 9: Configure Alerting
Set up your Vigilmon notification channels:
- Go to Settings → Notifications.
- Add Slack, email, PagerDuty, or OpsGenie.
- Route by severity.
Recommended routing for Alloy:
| Monitor | Severity | Notification | |---------|----------|-------------| | Alloy process down | Critical | Immediate: Slack + PagerDuty | | Any component unhealthy | High | Immediate: Slack | | OTLP throughput → zero | Critical | Immediate: Slack + PagerDuty | | Export failure > 1% | High | Slack | | WAL queue growing | High | Slack | | Cluster member missing | High | Slack | | Scrape failure > 5% | Medium | Email | | Config reload failure | Medium | Slack | | Memory growing | Medium | Email |
Step 10: Test Your Monitors
Before relying on these monitors:
# Test Alloy liveness alert
sudo systemctl stop alloy
# → Vigilmon HTTP monitor alerts within 1-2 minutes
sudo systemctl start alloy
# Test component failure detection
# Edit alloy config to add a bad scrape target, then reload
# → Component check script should detect unhealthy component
# Test WAL alert
# Stop your Prometheus/Mimir remote_write target temporarily
# → WAL pending samples will grow; heartbeat withheld
# Verify scrape check
# Take a target offline temporarily
# → up{} metric becomes 0 for that target; failure rate increases
Conclusion
Grafana Alloy is the foundation of your observability stack — which makes it uniquely important to monitor with an external tool. When Alloy fails, your internal dashboards, alerts, and traces all fail with it. Vigilmon's external HTTP checks and heartbeat monitors give you visibility into Alloy's health that doesn't depend on Alloy being healthy to work.
Start with the liveness and readiness checks (Step 1) — they catch the most impactful failure (complete Alloy crash) immediately. Then add component health monitoring (Step 2) to catch per-pipeline-branch failures. Export success rate and WAL depth monitoring (Steps 5-6) catch the silent data loss scenarios that uptime checks miss.