Your nightly build pipeline stopped running two weeks ago. Nobody noticed until a deployment to a client environment failed because the artifact registry hadn't been updated. Azure Pipelines had no errors to report — the scheduled pipeline simply wasn't being triggered anymore. Build notifications only fire on failures, and you can't fail if you never run.
This is the silent CI/CD failure problem. HTTP monitors can't catch it because there's nothing to ping. The fix is heartbeat monitoring: your pipeline pings a unique URL at the end of every successful run, and if Vigilmon doesn't receive that ping within the expected window, you get alerted.
This tutorial shows you how to instrument Azure Pipelines with Vigilmon heartbeat monitors to catch failures before your team does.
What You'll Cover
- Heartbeat monitoring for scheduled Azure Pipelines runs
- Catching pipelines that stop triggering entirely
- Alerting on long-running or stuck agents
- Multi-environment monitoring (staging and production)
- Detecting scheduled pipeline drift in Azure DevOps
Prerequisites
- An Azure DevOps project with at least one pipeline
- A free account at vigilmon.online
The Problem: What Standard Azure Pipelines Monitoring Misses
Azure Pipelines has built-in email notifications — but they only fire when a pipeline fails. They won't alert you when:
- A scheduled trigger is disabled by a pipeline YAML change or UI toggle
- A pipeline is paused in the Azure DevOps UI and nobody re-enables it
- A stage is skipped because an approval gate is left pending indefinitely
- A self-hosted agent goes offline and builds queue forever without running
Vigilmon's heartbeat pattern closes all of these gaps.
Step 1: Create a Heartbeat Monitor in Vigilmon
A heartbeat monitor expects a ping on a regular interval. No ping → alert.
- Log in to Vigilmon and click New Monitor → Heartbeat.
- Give it a name like
Nightly Build — main. - Set the Expected interval to match your pipeline schedule. For a nightly cron, use 24 hours. For hourly CI, use 90 minutes (adds a 50% buffer).
- Set the Grace period to 30 minutes for short intervals, or 1 hour for daily jobs. This prevents false alerts from slight schedule drift.
- Save and copy the Ping URL — it looks like
https://vigilmon.online/api/heartbeat/<unique-id>.
Step 2: Add the Heartbeat Ping to a Scheduled Pipeline
Store the heartbeat URL as a Pipeline secret variable. In Azure DevOps, go to your pipeline → Edit → Variables → New variable:
- Name:
VIGILMON_NIGHTLY_HEARTBEAT_URL - Value: the ping URL from Step 1
- Check Keep this value secret
For variables shared across multiple pipelines, use a Variable group (Library → Variable groups → Add) and link it to the pipeline.
Here's a complete azure-pipelines.yml example with Vigilmon instrumentation:
# azure-pipelines.yml
trigger:
- none
schedules:
- cron: "0 2 * * *" # 02:00 UTC every night
displayName: Nightly build
branches:
include:
- main
always: true # run even when there are no code changes
pool:
vmImage: ubuntu-latest
variables:
- group: vigilmon-secrets # variable group containing VIGILMON_NIGHTLY_HEARTBEAT_URL
steps:
- task: NodeTool@0
inputs:
versionSpec: "20.x"
displayName: Set up Node.js
- script: npm ci
displayName: Install dependencies
- script: npm test
displayName: Run tests
- bash: |
curl -fsS -X POST "$(VIGILMON_NIGHTLY_HEARTBEAT_URL)" \
--max-time 10 \
--retry 3 \
--retry-delay 2
displayName: Ping Vigilmon heartbeat
condition: succeeded()
Key points about this setup:
condition: succeeded()— the heartbeat ping is only sent when all previous steps pass. If tests fail, this step is skipped and no ping is sent.--retry 3— transient network issues won't cause a false "missed heartbeat" alert.--max-time 10— the ping step won't hang and block the agent.always: trueon the schedule — ensures the pipeline runs on schedule even with no code changes, which is the common case for monitoring-style runs.
Step 3: Monitor Your Deployment Pipeline
Heartbeats are even more valuable for deployment pipelines — a silently stalled deploy leaves production or a client environment running stale code.
# azure-pipelines.yml (multi-stage with deployment)
trigger:
branches:
include:
- main
pool:
vmImage: ubuntu-latest
variables:
- group: vigilmon-secrets
stages:
- stage: Build
jobs:
- job: BuildAndTest
steps:
- script: npm ci
displayName: Install
- script: npm run build
displayName: Build
- script: npm test
displayName: Test
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: dist
artifactName: app
- stage: Deploy
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployProduction
environment: production
strategy:
runOnce:
deploy:
steps:
- script: ./scripts/deploy.sh production
displayName: Deploy
- script: npm run test:smoke
displayName: Smoke tests
- bash: |
curl -fsS -X POST "$(VIGILMON_DEPLOY_PROD_HEARTBEAT_URL)" \
--max-time 10 \
--retry 3
displayName: Ping Vigilmon deployment heartbeat
condition: succeeded()
Create a separate heartbeat monitor for this deployment stage with an interval of 48 hours (or however often you expect to deploy). If no successful deployment reaches production within that window, Vigilmon pages you.
Step 4: Detect Scheduled Pipeline Drift
Azure Pipelines scheduled triggers have a known limitation: always: false (the default) causes pipelines to skip runs when no code has changed. A monitoring pipeline that should run nightly may silently stop if the repo goes quiet.
Setting always: true in the schedule block fixes this for most cases. But Vigilmon's heartbeat monitor provides a second layer — if the pipeline misses a run for any reason (agent outage, schedule misconfiguration, pipeline paused), no ping arrives, and you get alerted.
To make the signal even more reliable, add metadata to the heartbeat payload:
- bash: |
curl -fsS -X POST "$(VIGILMON_NIGHTLY_HEARTBEAT_URL)" \
-H "Content-Type: application/json" \
-d "{\"pipeline\": \"$(Build.DefinitionName)\", \"build_id\": \"$(Build.BuildId)\", \"branch\": \"$(Build.SourceBranch)\"}" \
--max-time 10 \
--retry 3
displayName: Ping Vigilmon heartbeat with metadata
condition: succeeded()
Vigilmon accepts the body but doesn't require it — it only cares whether the ping arrived within the grace period.
Step 5: Multi-Environment Monitoring
For teams with staging and production pipelines, create one heartbeat monitor per environment per stage:
| Stage | Environment | Monitor name | Interval |
|---|---|---|---|
| Deploy stage | Production | Deploy → Production | 48 h |
| Deploy stage | Staging | Deploy → Staging | 24 h |
| Nightly build | main | Nightly Build — main | 25 h |
| Weekly security scan | — | Weekly Security Scan | 8 days |
Use pipeline variables or conditions to select the correct heartbeat URL per environment:
- bash: |
if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
HEARTBEAT_URL="$(VIGILMON_DEPLOY_PROD_HEARTBEAT_URL)"
else
HEARTBEAT_URL="$(VIGILMON_DEPLOY_STAGING_HEARTBEAT_URL)"
fi
curl -fsS -X POST "$HEARTBEAT_URL" --max-time 10
displayName: Ping environment heartbeat
condition: succeeded()
Step 6: Alert Channels
In Vigilmon, go to Notifications → New Channel and configure:
- Email — immediate alert when a heartbeat is missed
- Slack webhook — ping your
#alertsor#ci-cdchannel
When a pipeline stops pinging:
🔴 MISSED HEARTBEAT: Nightly Build — main
Last ping: 26 hours ago
Expected interval: 24 hours
When it resumes:
✅ HEARTBEAT RECOVERED: Nightly Build — main
Gap: 26 hours
Step 7: Protect Against Pending Approval Gates
One failure mode unique to Azure Pipelines: deployment environments with approval gates can stall a pipeline indefinitely when an approver is on leave or the approval notification is missed. The pipeline shows as "waiting" in the UI, which can look like in-progress work rather than a stuck state.
Heartbeat monitoring covers this case automatically: if the deployment stage never completes (because it's waiting forever for approval), no ping arrives, and Vigilmon alerts you after the grace period. This surfaces the stalled approval as an incident rather than letting it silently block deployments.
Complete Step Template
Here's a reusable step you can copy into any Azure Pipelines stage:
# Add this step at the end of any job or deployment you want heartbeat monitoring on
- bash: |
curl -fsS -X POST "$(VIGILMON_HEARTBEAT_URL)" \
--max-time 10 \
--retry 3 \
--retry-delay 2
displayName: Ping Vigilmon heartbeat
condition: succeeded()
Replace VIGILMON_HEARTBEAT_URL with the specific variable name for that pipeline's monitor (use one heartbeat monitor per stage or deployment environment).
What You're Now Catching
| Silent failure mode | How Vigilmon detects it | |---|---| | Scheduled trigger skips run (no code changes) | No ping arrives → alert after grace period | | Pipeline paused or disabled in Azure DevOps UI | No ping arrives → alert after grace period | | Self-hosted agent offline, builds queued forever | No ping arrives → alert after grace period | | Approval gate pending indefinitely | No deployment ping → alert | | Deploy stage skipped due to branch condition | No ping on deploy stage → alert | | Deploy pipeline stopped shipping to production | Deploy heartbeat missed → alert |
Azure Pipelines is reliable — until it quietly stops working. Vigilmon's heartbeat monitors give you the external signal that Azure DevOps itself can't provide: a definitive alert when your CI/CD pipeline hasn't run in longer than expected.
Add heartbeat monitoring to your Azure Pipelines today — register free at vigilmon.online.