Terramate is a Terraform orchestration and code generation tool that brings order to large-scale infrastructure-as-code repositories. It manages stack dependencies, change detection, and execution ordering — letting teams run terraform apply only on the stacks affected by a given change, without accidentally touching unrelated infrastructure. What Terramate doesn't provide is an independent external health check: a synthetic probe that measures whether the services deployed through your Terramate stacks are reachable and responding correctly from the outside. Vigilmon fills that gap. It sits entirely outside your infrastructure and probes your services from multiple external locations, alerting you immediately when a deployed service goes down, regardless of what Terramate's run logs report.
This tutorial covers adding external uptime monitoring to services deployed and managed through Terramate stacks.
What You'll Build
- A
/healthendpoint in a service managed by a Terramate stack - A Vigilmon HTTP monitor with response-body assertions
- A heartbeat monitor for Terramate scheduled plan and apply jobs
- An alert routing strategy that keeps Terramate deployment alerts and Vigilmon uptime alerts separate but correlated
Prerequisites
- A Terramate project with at least one deployed service stack
- At least one service endpoint reachable via a public HTTPS URL
- A free account at vigilmon.online
Step 1: Add a Health Endpoint to Your Service
Terramate orchestrates your infrastructure deployments. Vigilmon needs an HTTP endpoint it can reach from the outside once the stack is deployed. The health check should verify the real service dependencies — database connectivity, upstream API reachability, and application initialization — not just a static 200 that passes even when the service is degraded.
Python (FastAPI)
# app/health.py
import os
import httpx
import asyncpg
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/health")
async def health():
checks = {}
ok = True
# Database connectivity
try:
conn = await asyncpg.connect(os.environ["DATABASE_URL"], timeout=3)
await conn.fetchval("SELECT 1")
await conn.close()
checks["database"] = "ok"
except Exception as exc:
checks["database"] = f"error: {exc}"
ok = False
# Upstream API dependency
try:
async with httpx.AsyncClient(timeout=5.0) as client:
resp = await client.get(os.environ["UPSTREAM_API_URL"] + "/ping")
checks["upstream_api"] = "ok" if resp.status_code == 200 else f"http_{resp.status_code}"
if resp.status_code != 200:
ok = False
except Exception as exc:
checks["upstream_api"] = f"error: {exc}"
ok = False
status_code = 200 if ok else 503
return JSONResponse(
status_code=status_code,
content={"status": "ok" if ok else "degraded", "checks": checks},
)
Go
// internal/health/handler.go
package health
import (
"database/sql"
"encoding/json"
"net/http"
"os"
"time"
)
type HealthResponse struct {
Status string `json:"status"`
Checks map[string]string `json:"checks"`
}
func Handler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
checks := make(map[string]string)
ok := true
// Database ping
if err := db.Ping(); err != nil {
checks["database"] = "error: " + err.Error()
ok = false
} else {
checks["database"] = "ok"
}
// Upstream service check
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(os.Getenv("UPSTREAM_API_URL") + "/ping")
if err != nil {
checks["upstream_api"] = "error: " + err.Error()
ok = false
} else {
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
checks["upstream_api"] = "ok"
} else {
checks["upstream_api"] = fmt.Sprintf("http_%d", resp.StatusCode)
ok = false
}
}
status := "ok"
statusCode := http.StatusOK
if !ok {
status = "degraded"
statusCode = http.StatusServiceUnavailable
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
json.NewEncoder(w).Encode(HealthResponse{Status: status, Checks: checks})
}
}
Verify the endpoint before configuring Vigilmon:
curl -s https://your-service.example.com/health | jq .
# {"status":"ok","checks":{"database":"ok","upstream_api":"ok"}}
Step 2: Configure Vigilmon HTTP Monitor
- Log in to Vigilmon → Add Monitor → HTTP.
- URL:
https://your-service.example.com/health - Check interval: 60 seconds — appropriate for production services.
- Response timeout: 10 seconds.
- Expected status code:
200. - JSON body assertion:
- Path:
status - Expected value:
ok
- Path:
- Click Save.
Vigilmon immediately begins probing from multiple external locations. A non-200 response or a failed JSON assertion triggers an alert, independently of whether Terramate's own deployment pipeline reports success.
Monitoring each stack's service
Terramate organizes infrastructure into stacks, and each stack typically corresponds to one or more deployed services. Create a Vigilmon monitor for each production service endpoint:
| Stack | Service URL | Interval |
|---|---|---|
| stacks/api | https://api.example.com/health | 60 s |
| stacks/worker | https://worker.example.com/health | 60 s |
| stacks/frontend | https://app.example.com/health | 60 s |
| stacks/api-staging | https://api-staging.example.com/health | 120 s |
Group all monitors under a Monitor group named after the service or environment.
Step 3: Heartbeat Monitor for Scheduled Terramate Jobs
Terramate deployments often run on a schedule — nightly drift detection runs, weekly full plan previews, or CI-gated apply pipelines. Use Vigilmon heartbeats to guarantee these scheduled orchestration jobs complete on time.
#!/bin/bash
# scripts/nightly-drift-check.sh
set -euo pipefail
echo "Running Terramate change detection..."
terramate run --changed -- terraform plan -detailed-exitcode
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] || [ $EXIT_CODE -eq 2 ]; then
# Exit code 0: no changes. Exit code 2: changes detected (not an error).
curl -s -X POST "$VIGILMON_HEARTBEAT_URL" --max-time 5
echo "Drift check complete — heartbeat sent"
else
echo "Drift check failed with exit code $EXIT_CODE"
exit $EXIT_CODE
fi
In Vigilmon, create a Heartbeat monitor with a grace period slightly above your cron interval. For a nightly job, use 25 hours. Store the heartbeat URL as a CI secret or environment variable.
GitHub Actions scheduled job
# .github/workflows/nightly-drift-check.yml
name: Nightly Terramate Drift Check
on:
schedule:
- cron: '0 3 * * *' # 3 AM UTC daily
jobs:
drift-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Terramate needs full history for change detection
- name: Setup Terramate
uses: terramate-io/terramate-action@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Run drift detection
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
terramate run --changed -- terraform plan -detailed-exitcode
- name: Send heartbeat on success
if: success()
run: curl -X POST "${{ secrets.VIGILMON_HEARTBEAT_URL }}"
Step 4: Alert Routing Strategy
Terramate alerts on infrastructure orchestration events: stack execution failures, dependency ordering errors, drift between desired and actual state. Vigilmon alerts on the services that Terramate deployed: an endpoint is down, a health check is failing, a drift-check job missed its window. These are distinct failure modes.
| Failure mode | Source | Route to |
|---|---|---|
| Stack apply failure | Terramate CI | Slack #infra-deployments + on-call |
| Drift detected in production stack | Terramate | Infrastructure team + ticket |
| Service endpoint down after deploy | Vigilmon | PagerDuty on-call + Slack #prod-alerts |
| Drift check job missed | Vigilmon | Slack #infra-ops-critical |
| TLS certificate expiring | Vigilmon | DevOps team |
Configure Vigilmon alert channels under Alerts → Add channel:
- Email: your on-call distribution list.
- PagerDuty webhook: wire to your primary on-call rotation for endpoint outages.
- Slack webhook: a dedicated
#vigilmon-alertschannel.
Step 5: Correlating Terramate Deployments and Vigilmon Alerts During Incidents
When Vigilmon fires a downtime alert for a service managed by Terramate, use this checklist:
- Check Vigilmon probe regions — all regions failing points to the service itself. One region failing suggests a DNS or routing issue.
- Check recent Terramate runs — did a stack apply complete just before the alert fired? A bad deployment is the most common cause of immediate post-deploy outages.
- Review the stack's Terraform state — compare the previous and current state to identify what changed. A misconfigured load balancer rule or security group can silently break reachability.
- Check dependent stacks — Terramate tracks inter-stack dependencies. If an upstream shared infrastructure stack (VPC, DNS, load balancer) was recently applied, it may have affected the failing service.
- Inspect the health check in detail — which dependency is failing? Database connectivity issues after a deploy often point to a migration failure or connection pool misconfiguration introduced in the same change.
Step 6: Public Status Page
Terramate run logs and drift reports are internal infrastructure telemetry. Give customers and API consumers a public-facing status page using Vigilmon.
- In Vigilmon, go to Status Pages → Create.
- Add your production service monitors and any critical heartbeat monitors.
- Configure a custom domain (e.g.,
status.example.com) or use the Vigilmon subdomain. - Embed the status badge in your developer documentation:
<img src="https://vigilmon.online/badge/<monitor-id>.svg" alt="Service Status" />
What Vigilmon Adds to a Terramate Stack
| Capability | Terramate | Vigilmon | |---|---|---| | Stack change detection | Yes | No | | Terraform execution ordering | Yes | No | | Drift detection between runs | Yes | No | | Infrastructure code generation | Yes | No | | External synthetic HTTP probing | No | Yes | | DNS resolution failure detection | No | Yes | | TLS/SSL certificate expiry alerts | No | Yes | | Scheduled job heartbeat monitoring | No | Yes | | Public status page | No | Yes | | Independent of CI/CD pipeline health | No | Yes |
Terramate gives you precise orchestration over which Terraform stacks run, in what order, and when. Vigilmon gives you the external, synthetic view that tells you what your users actually experience after those stacks are deployed. Together they cover every dimension of infrastructure reliability: from orchestrated provisioning to customer-facing uptime.
Add external uptime monitoring to your Terramate-managed services today — register free at vigilmon.online.