Infrastructure-as-code has become the default for modern engineering teams. Servers, DNS records, databases, load balancers, and Kubernetes clusters are all declared in version-controlled configuration files — reviewed, tested, and deployed through CI/CD pipelines. But monitoring is often the last piece to join this workflow. Engineers still log into a dashboard, click through a wizard, and add monitors by hand. Those monitors aren't version-controlled, aren't reviewed, and aren't tied to the services they cover.
Monitoring as code closes this gap. It means your uptime monitors, heartbeat checks, and alert configurations are defined in code, committed to your repository, deployed automatically, and managed with the same discipline as the rest of your infrastructure.
This guide covers the why, the how, and practical patterns for implementing monitoring as code with the Vigilmon REST API — including CI/CD integration, Terraform patterns, and GitOps workflows for uptime monitoring.
Why Infrastructure-as-Code Teams Need Monitoring as Code
If you already manage infrastructure declaratively, the case for monitoring as code follows directly from the same principles.
The Drift Problem
When monitors are created manually, they drift from reality. A service gets renamed — the old monitor still checks the old URL. A background job gets removed — its heartbeat monitor lingers, generating false alerts. A new microservice launches — nobody remembers to add a monitor. The monitoring configuration becomes a stale artifact that reflects how the infrastructure was, not how it is.
Defining monitors in code, colocated with the services they cover, eliminates drift structurally. When a service is removed, the monitor definition is removed in the same commit. When a service is renamed, the URL in the monitor definition changes in the same PR.
Visibility and Review
When monitors live only in a dashboard, they're invisible to code review. A monitoring configuration change — adding an alert, changing a check interval, removing a monitor for a service that was supposed to stay up — doesn't generate a PR for teammates to review. Mistakes slip through because there's no review step.
Monitoring as code puts monitoring changes in PRs. A reviewer sees "this commit removes the heartbeat monitor for the order-processing worker" and can ask the right question before the change lands.
Reproducibility
In a monitoring-as-code setup, spinning up a new environment — staging, preview, QA — automatically creates the corresponding monitors. The monitors for production and staging are derived from the same configuration, parameterized by environment. No manual steps, no forgotten monitors, no environments flying blind.
Disaster Recovery
If you lose access to your monitoring dashboard — account issue, provider outage, team member departure — you can recreate your entire monitoring setup from the code in your repository. All monitor configurations, alert targets, and check settings are in version control.
The Vigilmon REST API
Vigilmon exposes a REST API for full programmatic control of your monitoring configuration. Every operation available in the dashboard is available via API:
- Create, update, and delete HTTP monitors
- Create, update, and delete TCP port monitors
- Create, update, and delete heartbeat (cron) monitors
- Retrieve monitor status and response time history
- Manage webhook notification endpoints
Authentication
All API requests use Bearer token authentication. Generate an API token from your Vigilmon account settings. Include it in every request:
Authorization: Bearer <your-api-token>
Tokens are scoped to your account and carry full read/write access to your monitors. Treat API tokens as secrets — store them in your CI/CD secret store (GitHub Actions secrets, GitLab CI variables, HashiCorp Vault), never in source code.
Base URL
https://vigilmon.online/api/v1
Creating and Managing Monitors via API
Creating an HTTP Monitor
curl -X POST https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer $VIGILMON_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production API",
"type": "http",
"url": "https://api.yourapp.com/health",
"interval": 60,
"alertThreshold": 2,
"notifications": {
"email": true,
"webhooks": ["https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"]
}
}'
Response:
{
"id": "mon_abc123",
"name": "Production API",
"type": "http",
"url": "https://api.yourapp.com/health",
"interval": 60,
"status": "active",
"createdAt": "2026-03-15T10:00:00Z"
}
Creating a Heartbeat Monitor
curl -X POST https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer $VIGILMON_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Processing Worker",
"type": "heartbeat",
"expectedInterval": 3600,
"gracePeriod": 300,
"notifications": {
"email": true,
"webhooks": ["https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"]
}
}'
The response includes a heartbeatUrl — the URL your cron job pings on each successful run:
{
"id": "mon_xyz789",
"name": "Order Processing Worker",
"type": "heartbeat",
"heartbeatUrl": "https://vigilmon.online/ping/mon_xyz789",
"expectedInterval": 3600,
"gracePeriod": 300,
"status": "active"
}
Creating a TCP Monitor
curl -X POST https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer $VIGILMON_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Database",
"type": "tcp",
"host": "db.yourapp.com",
"port": 5432,
"interval": 60
}'
Updating a Monitor
curl -X PATCH https://vigilmon.online/api/v1/monitors/mon_abc123 \
-H "Authorization: Bearer $VIGILMON_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"interval": 30
}'
Deleting a Monitor
curl -X DELETE https://vigilmon.online/api/v1/monitors/mon_abc123 \
-H "Authorization: Bearer $VIGILMON_API_TOKEN"
Listing All Monitors
curl https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer $VIGILMON_API_TOKEN"
CI/CD Integration Patterns
Pattern 1: Deploy-Time Monitor Sync
The simplest CI/CD integration: after each deployment, run a script that syncs your monitor configuration from a declarative file to Vigilmon.
monitors.json (committed to your repository):
{
"monitors": [
{
"name": "Production API - Health",
"type": "http",
"url": "https://api.yourapp.com/health",
"interval": 60
},
{
"name": "Production Frontend",
"type": "http",
"url": "https://yourapp.com",
"interval": 60
},
{
"name": "Production Database",
"type": "tcp",
"host": "db.yourapp.com",
"port": 5432,
"interval": 60
},
{
"name": "Nightly Backup Job",
"type": "heartbeat",
"expectedInterval": 86400,
"gracePeriod": 3600
}
]
}
sync-monitors.sh (runs in CI/CD pipeline after deploy):
#!/bin/bash
set -e
VIGILMON_TOKEN="${VIGILMON_API_TOKEN}"
BASE_URL="https://vigilmon.online/api/v1"
# Fetch current monitors
existing=$(curl -s -H "Authorization: Bearer $VIGILMON_TOKEN" "$BASE_URL/monitors")
# Sync each monitor from local config
jq -c '.monitors[]' monitors.json | while read -r monitor; do
name=$(echo "$monitor" | jq -r '.name')
# Check if monitor exists
existing_id=$(echo "$existing" | jq -r --arg name "$name" '.[] | select(.name == $name) | .id')
if [ -n "$existing_id" ]; then
# Update existing monitor
curl -s -X PATCH "$BASE_URL/monitors/$existing_id" \
-H "Authorization: Bearer $VIGILMON_TOKEN" \
-H "Content-Type: application/json" \
-d "$monitor" > /dev/null
echo "Updated monitor: $name"
else
# Create new monitor
curl -s -X POST "$BASE_URL/monitors" \
-H "Authorization: Bearer $VIGILMON_TOKEN" \
-H "Content-Type: application/json" \
-d "$monitor" > /dev/null
echo "Created monitor: $name"
fi
done
GitHub Actions integration:
name: Deploy and Sync Monitors
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy application
run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
- name: Sync Vigilmon monitors
run: bash sync-monitors.sh
env:
VIGILMON_API_TOKEN: ${{ secrets.VIGILMON_API_TOKEN }}
Pattern 2: Preview Environment Monitors
For teams that deploy preview environments for each pull request, create matching monitors automatically so preview environments are monitored with the same coverage as production.
name: PR Preview Deploy
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy preview environment
id: deploy
run: |
URL=$(./deploy-preview.sh ${{ github.event.pull_request.number }})
echo "url=$URL" >> $GITHUB_OUTPUT
- name: Create preview monitor
run: |
curl -X POST https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer ${{ secrets.VIGILMON_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"PR #${{ github.event.pull_request.number }} Preview\",
\"type\": \"http\",
\"url\": \"${{ steps.deploy.outputs.url }}/health\",
\"interval\": 300,
\"tags\": [\"preview\", \"pr-${{ github.event.pull_request.number }}\"]
}"
And on PR close:
- name: Delete preview monitor
if: github.event.action == 'closed'
run: |
# Find and delete the monitor for this PR
monitor_id=$(curl -s -H "Authorization: Bearer ${{ secrets.VIGILMON_API_TOKEN }}" \
"https://vigilmon.online/api/v1/monitors?tag=pr-${{ github.event.pull_request.number }}" | \
jq -r '.[0].id')
curl -X DELETE "https://vigilmon.online/api/v1/monitors/$monitor_id" \
-H "Authorization: Bearer ${{ secrets.VIGILMON_API_TOKEN }}"
Terraform Integration
For teams using Terraform for infrastructure management, defining Vigilmon monitors as Terraform resources provides full declarative lifecycle management — create, update, and delete monitors with terraform apply.
Using the Vigilmon Terraform Provider
If a Vigilmon Terraform provider is available in your setup, define monitors as resources:
terraform {
required_providers {
vigilmon = {
source = "vigilmon/vigilmon"
version = "~> 1.0"
}
}
}
provider "vigilmon" {
api_token = var.vigilmon_api_token
}
variable "vigilmon_api_token" {
description = "Vigilmon API token"
sensitive = true
}
resource "vigilmon_http_monitor" "api_health" {
name = "Production API - Health"
url = "https://api.${var.domain}/health"
interval = 60
notifications {
email = true
webhooks = [var.slack_webhook_url]
}
}
resource "vigilmon_http_monitor" "frontend" {
name = "Production Frontend"
url = "https://${var.domain}"
interval = 60
}
resource "vigilmon_tcp_monitor" "database" {
name = "Production Database"
host = var.db_host
port = 5432
interval = 60
}
resource "vigilmon_heartbeat_monitor" "backup_job" {
name = "Nightly Backup Job"
expected_interval = 86400
grace_period = 3600
}
output "backup_heartbeat_url" {
value = vigilmon_heartbeat_monitor.backup_job.heartbeat_url
description = "URL to ping after each successful backup run"
}
Using Terraform's HTTP Provider (API-Level)
For teams that want Terraform integration before a native provider is available, the generic http provider can call the Vigilmon API:
resource "null_resource" "vigilmon_api_monitor" {
triggers = {
url = "https://api.${var.domain}/health"
name = "Production API - Health"
interval = "60"
}
provisioner "local-exec" {
command = <<-EOT
curl -X POST https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer ${var.vigilmon_api_token}" \
-H "Content-Type: application/json" \
-d '{"name":"${self.triggers.name}","type":"http","url":"${self.triggers.url}","interval":${self.triggers.interval}}'
EOT
}
provisioner "local-exec" {
when = destroy
command = <<-EOT
MONITOR_ID=$(curl -s https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer ${var.vigilmon_api_token}" | \
jq -r '.[] | select(.name == "${self.triggers.name}") | .id')
curl -X DELETE "https://vigilmon.online/api/v1/monitors/$MONITOR_ID" \
-H "Authorization: Bearer ${var.vigilmon_api_token}"
EOT
}
}
Pulumi Integration
For teams using Pulumi, the same monitoring-as-code principle applies with Pulumi's resource model. Using Pulumi's Command resource to call the Vigilmon API:
import * as pulumi from "@pulumi/pulumi";
import * as command from "@pulumi/command";
const config = new pulumi.Config();
const vigilmonToken = config.requireSecret("vigilmonApiToken");
const domain = config.require("domain");
const apiMonitor = new command.local.Command("api-monitor", {
create: pulumi.interpolate`curl -s -X POST https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer ${vigilmonToken}" \
-H "Content-Type: application/json" \
-d '{"name":"Production API","type":"http","url":"https://api.${domain}/health","interval":60}' \
| jq -r '.id'`,
delete: pulumi.interpolate`MONITOR_ID=$(curl -s https://vigilmon.online/api/v1/monitors \
-H "Authorization: Bearer ${vigilmonToken}" | \
jq -r '.[] | select(.name == "Production API") | .id') && \
curl -X DELETE "https://vigilmon.online/api/v1/monitors/$MONITOR_ID" \
-H "Authorization: Bearer ${vigilmonToken}"`,
});
export const apiMonitorId = apiMonitor.stdout;
Version-Controlled Monitor Configurations
The key to GitOps for monitoring is a canonical, version-controlled monitor configuration that is the source of truth. Here's a recommended structure:
infra/
monitoring/
monitors.yaml # Declarative monitor definitions
environments/
production.yaml # Production-specific overrides
staging.yaml # Staging-specific settings
scripts/
sync.sh # Sync script called by CI/CD
validate.sh # Validate monitors.yaml before apply
monitors.yaml (base configuration):
monitors:
- name: "{{ env }}-api-health"
type: http
url: "https://api.{{ domain }}/health"
interval: "{{ check_interval }}"
notifications:
email: true
webhooks:
- "{{ slack_webhook }}"
- name: "{{ env }}-frontend"
type: http
url: "https://{{ domain }}"
interval: "{{ check_interval }}"
- name: "{{ env }}-database"
type: tcp
host: "{{ db_host }}"
port: 5432
interval: 60
- name: "{{ env }}-nightly-backup"
type: heartbeat
expectedInterval: 86400
gracePeriod: 3600
environments/production.yaml:
env: production
domain: yourapp.com
db_host: db.yourapp.com
check_interval: 60
slack_webhook: "https://hooks.slack.com/services/PROD/SLACK/WEBHOOK"
environments/staging.yaml:
env: staging
domain: staging.yourapp.com
db_host: db-staging.yourapp.com
check_interval: 300
slack_webhook: "https://hooks.slack.com/services/STAGING/SLACK/WEBHOOK"
The sync script reads the base configuration, applies environment overrides, and calls the Vigilmon API to reconcile the actual monitor state with the declared configuration.
GitOps Workflow for Uptime Monitoring
A full GitOps workflow for monitoring treats monitors as declarative resources with the same lifecycle as infrastructure:
1. Define in code: Engineers define monitors in YAML/JSON files colocated with services or in a dedicated monitoring directory.
2. Review in PRs: Changes to monitor configurations go through PR review. Adding, removing, or modifying monitors is visible to the team before it happens.
3. Validate before apply: CI runs a validation step that checks the configuration for common errors — invalid URLs, duplicate monitor names, missing required fields.
4. Apply on merge: When a PR merges to main, the sync script runs and applies the changes to Vigilmon via API.
5. Drift detection: A scheduled CI job (e.g., nightly) runs the sync script in check mode, alerting if Vigilmon's actual state has drifted from the declared configuration.
GitHub Actions full GitOps example:
name: Monitoring as Code
on:
push:
branches: [main]
paths:
- 'infra/monitoring/**'
schedule:
- cron: '0 6 * * *' # Daily drift detection
jobs:
sync-monitors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate monitor config
run: bash infra/monitoring/scripts/validate.sh
- name: Sync monitors to Vigilmon
run: bash infra/monitoring/scripts/sync.sh
env:
VIGILMON_API_TOKEN: ${{ secrets.VIGILMON_API_TOKEN }}
ENVIRONMENT: production
- name: Post sync summary
if: always()
uses: actions/github-script@v7
with:
script: |
// Post sync results as a comment on any related PRs
Heartbeat Monitoring in CI/CD Pipelines
CI/CD pipelines themselves benefit from heartbeat monitoring. If your deployment pipeline stops running — a broken scheduled workflow, a runner quota issue, a misconfigured cron in Kubernetes — you want to know before 48 hours of missed deployments pile up.
Add a heartbeat ping to the end of your CI/CD pipeline:
# GitHub Actions — ping Vigilmon at the end of each successful deploy pipeline
- name: Notify Vigilmon - deploy pipeline ran
if: success()
run: |
curl -s https://vigilmon.online/ping/${{ secrets.DEPLOY_PIPELINE_HEARTBEAT_ID }}
Configure the heartbeat monitor with an expectedInterval matching your deploy cadence. If you deploy at least once a day, set expectedInterval: 86400. If deployment pipelines should run at least weekly, set expectedInterval: 604800.
Best Practices
Colocate monitor definitions with services: A service's monitor configuration lives in the same repository as the service code. When the service is deleted, the monitor definition is deleted in the same commit.
Use environment variables for secrets: Never commit API tokens or webhook URLs. Use your CI/CD secret store and reference secrets as environment variables in your sync scripts.
Name monitors consistently: Use a naming convention that includes environment and service name: production-api-health, staging-payments-tcp. Consistent names make the sync script's reconciliation logic reliable.
Version control the heartbeat URL outputs: After creating a heartbeat monitor via API, store the resulting heartbeatUrl in your environment's secret store or configuration, not in source code. The URL is a secret (anyone with it can fake heartbeats).
Validate before apply: Add a validation step in CI that catches configuration errors before they reach the Vigilmon API. Fail the pipeline on invalid monitor configurations rather than silently skipping bad entries.
Test your sync script on staging first: Run the sync script against a staging Vigilmon account (or with dry-run mode) before deploying changes to production monitor configurations.
Conclusion
Monitoring as code brings the same discipline to uptime monitoring that infrastructure-as-code brought to provisioning: version control, peer review, reproducibility, and automatic drift correction. For teams already working with Terraform, Pulumi, or GitOps workflows, adding Vigilmon monitors to the declarative configuration layer is a natural extension.
The Vigilmon REST API makes monitoring as code practical today. Define your monitors in YAML or JSON, sync them via CI/CD, and treat uptime monitoring as a first-class artifact of your infrastructure — reviewed, versioned, and deployed alongside the services it covers.
Start building monitoring as code with Vigilmon's free tier at vigilmon.online — REST API access included, no credit card required, multi-region consensus monitoring from the first monitor.
Tags: #monitoring #monitoringascode #devops #sre #terraform #pulumi #gitops #cicd #vigilmon #infrastructure #2026