Brigade is a Kubernetes-native event-driven scripting platform that lets you write JavaScript or TypeScript event handlers that run as ephemeral worker pods in response to events — GitHub webhooks, cron schedules, CloudEvents, or any custom trigger. Teams use Brigade for CI/CD automation, GitOps workflows, scheduled batch jobs, and anything that needs event-driven execution on Kubernetes without standing up a full CI server.
But Brigade's distributed, ephemeral architecture makes monitoring non-trivial. A Brigade gateway can silently stop accepting events, a worker pod queue can back up under load, and a long-running script job can stall without any visible signal. In this tutorial you'll set up comprehensive monitoring for your Brigade deployment using Vigilmon — free tier, no credit card required.
Why Brigade needs external monitoring
Brigade consists of multiple components — the API server, event gateways, the scheduler, and ephemeral worker pods — each of which can fail independently. Brigade's own observability surface is limited to brig CLI queries and Kubernetes events.
The failure modes that external monitoring catches:
- Event gateway down — the GitHub gateway or custom gateway stops accepting webhook payloads; GitHub shows
200 OKfor delivery attempts but Brigade never processes the event - Worker queue depth spiking — a burst of events creates a backlog of pending worker pods; if the Kubernetes scheduler can't place them, they queue indefinitely
- API server unreachable — the Brigade API server is the control plane for all event and job management; if it goes down, no new events are processed and the
brigCLI fails - Worker pods stuck in Pending — resource constraints, node taints, or misconfigured resource requests cause worker pods to queue without ever starting
- Substrate connectivity broken — Brigade's substrate (the Kubernetes cluster it targets) may become temporarily inaccessible; new events queue but workers can't launch
- Script execution timeout — a Brigade script with a slow external dependency stalls the worker pod; it eventually times out, but the timeout window (default 24h) is too long for operational awareness
What you'll need
- Brigade v2 deployed in a Kubernetes cluster (
brigCLI configured) - At least one gateway configured (GitHub gateway, generic gateway, or cron gateway)
- A free Vigilmon account — sign up takes 30 seconds
Step 1: Monitor the Brigade API server
The Brigade API server is the control plane. Without it, no events are processed and no workers can be scheduled.
- Go to Monitors → New Monitor
- Choose HTTP / HTTPS
- URL:
https://brigade.example.com/healthz - Interval: 1 minute
- Expected status:
200 - Name:
Brigade API Server Health
If your Brigade API server is exposed internally only, use a Vigilmon agent or probe script:
#!/usr/bin/env bash
# brigade-api-probe.sh — check Brigade API server health
BRIGADE_API="https://brigade.internal.example.com"
HEARTBEAT_URL="$VIGILMON_API_HEARTBEAT_URL"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BRIGADE_API/healthz")
if [ "$STATUS" != "200" ]; then
echo "Brigade API server unhealthy: HTTP $STATUS"
exit 1
fi
curl -s "$HEARTBEAT_URL"
echo "Brigade API server healthy"
Schedule every 2 minutes with a 5-minute heartbeat grace period.
Step 2: Monitor the event gateway
Brigade's event gateways are the ingestion points for external events. Monitor each gateway you operate.
GitHub gateway
- New monitor → HTTP / HTTPS
- URL:
https://brigade-github-gateway.example.com/healthz - Interval: 1 minute
- Expected status:
200 - Name:
Brigade GitHub Gateway Health
Generic/CloudEvents gateway
- New monitor → HTTP / HTTPS
- URL:
https://brigade-gateway.example.com/healthz - Interval: 1 minute
- Expected status:
200 - Name:
Brigade Generic Gateway Health
If your gateway doesn't expose a /healthz endpoint, monitor the TCP port the gateway listens on:
- New monitor → TCP Port
- Host:
brigade-gateway.example.com, Port:443 - Name:
Brigade Gateway TCP
Test webhook delivery end-to-end
The most reliable test for gateway health is whether a test webhook payload is accepted and processed. Set up a synthetic event probe:
#!/usr/bin/env bash
# brigade-gateway-probe.sh — send a test event and verify Brigade processes it
BRIGADE_API="https://brigade.internal.example.com"
BRIGADE_TOKEN="$BRIGADE_API_TOKEN"
PROJECT_ID="my-test-project"
HEARTBEAT_URL="$VIGILMON_GATEWAY_HEARTBEAT_URL"
TIMEOUT_SECONDS=120
# Create a test event via the Brigade API
EVENT_ID=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
-H "Content-Type: application/json" \
-X POST \
"$BRIGADE_API/v2/events" \
-d "{
\"projectID\": \"$PROJECT_ID\",
\"source\": \"monitoring.probe\",
\"type\": \"health.check\",
\"payload\": \"{\\\"probe\\\": true}\"
}" | jq -r '.id')
if [ -z "$EVENT_ID" ] || [ "$EVENT_ID" = "null" ]; then
echo "Failed to create Brigade test event"
exit 1
fi
echo "Created Brigade event: $EVENT_ID"
# Poll until the event's worker reaches a terminal state
START=$(date +%s)
while true; do
NOW=$(date +%s)
ELAPSED=$(( NOW - START ))
if [ $ELAPSED -gt $TIMEOUT_SECONDS ]; then
echo "TIMEOUT: Brigade worker did not complete within ${TIMEOUT_SECONDS}s"
exit 1
fi
WORKER_PHASE=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
"$BRIGADE_API/v2/events/$EVENT_ID/worker" \
| jq -r '.status.phase')
echo "Worker phase: $WORKER_PHASE (${ELAPSED}s elapsed)"
case "$WORKER_PHASE" in
"SUCCEEDED")
echo "Brigade probe event succeeded"
curl -s "$HEARTBEAT_URL"
exit 0
;;
"FAILED"|"ABORTED"|"TIMED_OUT")
echo "Brigade probe event FAILED with phase: $WORKER_PHASE"
exit 1
;;
esac
sleep 5
done
Schedule every 10 minutes with a Vigilmon heartbeat grace period of 20 minutes. This probe exercises the complete Brigade event path: gateway → API server → scheduler → worker pod → script execution → result.
Step 3: Monitor worker queue depth
When Brigade's worker queue backs up, event processing latency grows and eventually events start failing. Monitor queue depth directly from the Brigade API:
#!/usr/bin/env bash
# brigade-queue-probe.sh — monitor pending worker count
BRIGADE_API="https://brigade.internal.example.com"
BRIGADE_TOKEN="$BRIGADE_API_TOKEN"
MAX_PENDING=10
HEARTBEAT_URL="$VIGILMON_QUEUE_HEARTBEAT_URL"
# Count workers in PENDING phase across all projects
PENDING=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
"$BRIGADE_API/v2/events?workerPhase=PENDING&limit=100" \
| jq '.metadata.itemCount // 0')
RUNNING=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
"$BRIGADE_API/v2/events?workerPhase=RUNNING&limit=100" \
| jq '.metadata.itemCount // 0')
echo "Brigade workers — Pending: $PENDING, Running: $RUNNING"
if [ "$PENDING" -gt "$MAX_PENDING" ]; then
echo "QUEUE BACKLOG: $PENDING workers pending (threshold: $MAX_PENDING)"
exit 1
fi
curl -s "$HEARTBEAT_URL"
Schedule every 3 minutes with a 10-minute heartbeat grace period.
For Kubernetes-side visibility into pending pods:
#!/usr/bin/env bash
# brigade-pods-probe.sh — check for brigade worker pods stuck in Pending
NAMESPACE="brigade"
MAX_PENDING_AGE_MINUTES=10
HEARTBEAT_URL="$VIGILMON_PODS_HEARTBEAT_URL"
# Find worker pods in Pending state older than threshold
STUCK_PODS=$(kubectl get pods \
-n "$NAMESPACE" \
-l "brigade.sh/role=worker" \
--field-selector="status.phase=Pending" \
-o json | jq --argjson max_age $(( MAX_PENDING_AGE_MINUTES * 60 )) '
[.items[] |
select(
(now - (.metadata.creationTimestamp | fromdateiso8601)) > $max_age
) | .metadata.name
] | length
')
if [ "$STUCK_PODS" -gt 0 ]; then
echo "STUCK: $STUCK_PODS Brigade worker pod(s) Pending for >${MAX_PENDING_AGE_MINUTES} minutes"
kubectl get pods -n "$NAMESPACE" -l "brigade.sh/role=worker" \
--field-selector="status.phase=Pending"
exit 1
fi
echo "No stuck Brigade worker pods"
curl -s "$HEARTBEAT_URL"
Step 4: Monitor script execution latency
Brigade script execution latency is how long it takes from event received to worker completion. When latency spikes, downstream systems waiting on Brigade (deployment pipelines, notification systems) accumulate delays.
Instrument your Brigade project's worker script to report timing:
// brigade.js — add timing instrumentation
const { events } = require("@brigadecore/brigadier");
events.on("monitoring.probe", "health.check", async (event) => {
const start = Date.now();
// ... your actual work ...
await doWork(event);
const elapsed = Date.now() - start;
console.log(`[METRICS] script_duration_ms=${elapsed}`);
// Report heartbeat with timing
const heartbeatUrl = event.project.secrets.VIGILMON_HEARTBEAT_URL;
await fetch(heartbeatUrl);
});
events.process();
Then scrape the execution time from Brigade worker logs:
#!/usr/bin/env bash
# brigade-latency-probe.sh — check recent worker execution times
BRIGADE_API="https://brigade.internal.example.com"
BRIGADE_TOKEN="$BRIGADE_API_TOKEN"
MAX_DURATION_MS=30000 # Alert if script takes more than 30 seconds
HEARTBEAT_URL="$VIGILMON_LATENCY_HEARTBEAT_URL"
# Get logs from the most recent SUCCEEDED worker
LATEST_SUCCEEDED=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
"$BRIGADE_API/v2/events?workerPhase=SUCCEEDED&limit=1" \
| jq -r '.items[0].id')
if [ -z "$LATEST_SUCCEEDED" ] || [ "$LATEST_SUCCEEDED" = "null" ]; then
echo "No recent succeeded workers found"
exit 1
fi
DURATION=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
"$BRIGADE_API/v2/events/$LATEST_SUCCEEDED/worker/logs?container=brigadier" \
| jq -r '.[] | select(.message | contains("script_duration_ms")) | .message' \
| grep -oP 'script_duration_ms=\K[0-9]+' | tail -1)
if [ -z "$DURATION" ]; then
echo "Could not parse execution duration from worker logs"
exit 1
fi
echo "Most recent script duration: ${DURATION}ms"
if [ "$DURATION" -gt "$MAX_DURATION_MS" ]; then
echo "LATENCY: ${DURATION}ms exceeds threshold ${MAX_DURATION_MS}ms"
exit 1
fi
curl -s "$HEARTBEAT_URL"
Step 5: Monitor substrate connectivity
Brigade's substrate is the Kubernetes cluster it launches worker pods in. If the substrate becomes temporarily inaccessible — due to API server issues, network partition, or expired credentials — Brigade can't launch any workers.
Monitor substrate connectivity with a Kubernetes API probe:
#!/usr/bin/env bash
# brigade-substrate-probe.sh — verify Brigade can reach Kubernetes substrate
BRIGADE_API="https://brigade.internal.example.com"
BRIGADE_TOKEN="$BRIGADE_API_TOKEN"
HEARTBEAT_URL="$VIGILMON_SUBSTRATE_HEARTBEAT_URL"
# Brigade exposes a substrate connectivity check via its API
SUBSTRATE_STATUS=$(curl -s \
-H "Authorization: Bearer $BRIGADE_TOKEN" \
"$BRIGADE_API/v2/substrate/nodes?limit=1" \
| jq -r '.items | length')
if [ -z "$SUBSTRATE_STATUS" ] || [ "$SUBSTRATE_STATUS" = "null" ]; then
echo "Brigade cannot reach substrate or returned unexpected response"
exit 1
fi
echo "Brigade substrate accessible: $SUBSTRATE_STATUS node(s) visible"
curl -s "$HEARTBEAT_URL"
Additionally, monitor the Kubernetes API server that Brigade targets:
- New monitor → HTTP / HTTPS
- URL:
https://k8s-cluster.example.com:6443/healthz - Interval: 1 minute
- Expected status:
200 - Name:
Brigade Substrate (K8s API)
Step 6: Monitor cron gateway schedules
If you use Brigade's cron gateway to trigger scheduled events, set up Vigilmon heartbeats to verify each scheduled job runs on time:
// brigade.js — cron job that reports to Vigilmon
const { events } = require("@brigadecore/brigadier");
events.on("brigade.sh/cron", "backup", async (event) => {
// ... perform backup work ...
await runBackup();
// Report to Vigilmon that this cron fired and succeeded
const heartbeatUrl = event.project.secrets.VIGILMON_BACKUP_HEARTBEAT_URL;
await fetch(heartbeatUrl);
});
events.process();
For a cron gateway event that fires daily at 03:00:
- In Vigilmon: Monitors → New Monitor → Heartbeat
- Name:
Brigade Daily Backup Job - Grace period: 25 hours
If the Brigade cron gateway stops firing — because the gateway crashed, the cron schedule was misconfigured, or Brigade can't reach the substrate — Vigilmon alerts you within 25 hours.
Step 7: Set up alerts
Configure Vigilmon alerts for all Brigade monitors:
| Monitor | Alert threshold | Notification | |---|---|---| | Brigade API Server Health | 2 minutes down | Slack #platform-alerts, PagerDuty | | GitHub Gateway Health | 2 minutes down | Slack #ci-health | | Generic Gateway Health | 2 minutes down | Slack #platform-alerts | | End-to-End Probe heartbeat | 20-minute silence | Slack #ci-health | | Queue Depth heartbeat | 10-minute silence | Slack #platform-alerts | | Pending Pods heartbeat | 10-minute silence | Slack #k8s-ops | | Script Latency heartbeat | 30-minute silence | Slack #ci-health | | Substrate heartbeat | 10-minute silence | PagerDuty | | Cron Job heartbeats | Per-schedule grace | Slack #platform-alerts |
For the Brigade API server and substrate monitors, escalate to PagerDuty immediately — without these, no Brigade workloads can run.
Step 8: Create a Brigade status page
Create a Vigilmon status page that summarizes Brigade health for your team:
- Go to Status Pages → New
- Name:
Brigade Platform Status - Add monitors:
- Brigade API Server
- GitHub Gateway
- Worker Queue Depth
- Substrate Connectivity
- Share the URL in your team's wiki or Slack channel description
This gives developers a quick reference when their Brigade-triggered CI jobs hang.
Interpreting Vigilmon alerts for Brigade
| Alert | Likely cause | First action |
|---|---|---|
| Brigade API Server down | Pod crashed, OOM, deployment rollout | kubectl get pods -n brigade; kubectl logs -n brigade -l app=brigade-apiserver |
| GitHub Gateway down | Gateway pod crash, ingress misconfiguration | Check gateway pod; test with a manual webhook delivery from GitHub |
| End-to-End Probe silent | Full pipeline failure, probe script error | Run brigade-gateway-probe.sh manually; check brig events list |
| Queue Depth silent | High event volume, scheduler issue, node pressure | brig events list --worker-phase PENDING; check node capacity |
| Pending Pods silent | Node resource pressure, PodDisruptionBudget, taint | kubectl describe pod -n brigade <pending-pod>; check node kubectl describe node |
| Script Latency silent | External dependency slow, probe log parse failure | Check last worker logs via brig job logs |
| Substrate silent | K8s API server issue, network partition, expired kubeconfig | kubectl cluster-info; check Brigade's kubeconfig secret |
| Cron Job silent | Cron gateway crash, Brigade API down, script failure | Check cron gateway pod; brig events list for missing cron events |
What's next
With Brigade monitoring in place:
- Worker log aggregation — ship Brigade worker pod logs to a centralized log platform and set up alerts on script
ERRORoutput, complementing Vigilmon's uptime perspective - Per-project probes — if you have multiple Brigade projects with different SLAs, create separate end-to-end probes and Vigilmon heartbeats per project
- Slack event notifications — use Vigilmon webhooks to post Brigade status changes directly to the Slack channel where your team triages CI issues
- Worker resource tuning — use pending pod data from Vigilmon alerts to right-size Brigade worker resource requests, reducing the frequency of pod scheduling failures
Brigade makes event-driven automation on Kubernetes elegant. Vigilmon makes the event path — from gateway ingestion through worker execution — observable, so you know immediately when events stop flowing or workers stop completing.