tutorial

Monitoring env0-Provisioned Cloud Environments with Vigilmon: External Uptime, API Health & Heartbeat Monitoring

Practical guide to complementing env0's self-service cloud environment management with Vigilmon's external uptime checks — health endpoints, synthetic HTTP monitors, and independent alerting for IaC-deployed services.

env0 is a self-service cloud environment management platform for infrastructure-as-code. It lets development teams spin up, manage, and tear down cloud environments on demand — backed by Terraform, OpenTofu, Pulumi, or CloudFormation — with governance controls, cost policies, and an approval workflow built in. env0 takes care of the provisioning lifecycle. What it doesn't provide is an independent external health check: a synthetic probe that measures whether the services running inside those environments 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 service in an env0-managed environment goes down.

This tutorial covers adding external uptime monitoring to services running inside environments provisioned by env0.

What You'll Build

  • A /health endpoint in a service running inside an env0-managed environment
  • A Vigilmon HTTP monitor with response-body assertions
  • A heartbeat monitor for env0 scheduled deployment and teardown jobs
  • An alert routing strategy that keeps env0 provisioning alerts and Vigilmon uptime alerts separate but correlated

Prerequisites

  • An env0 account with at least one active cloud environment
  • At least one service endpoint reachable via a public HTTPS URL inside that environment
  • A free account at vigilmon.online

Step 1: Add a Health Endpoint to Your Service

env0 manages the provisioning lifecycle of your cloud environments. Vigilmon needs an HTTP endpoint it can reach from the outside once the environment is deployed. The health check should verify real application dependencies — database connectivity, object storage access, and any critical upstream APIs — not just a static 200 that would pass even when the service is degraded.

Python (FastAPI)

# app/health.py
import os
import boto3
import asyncpg
from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/health")
async def health():
    checks = {}
    ok = True

    # Database connectivity check
    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

    # Object storage check (S3-compatible)
    try:
        s3 = boto3.client(
            "s3",
            region_name=os.environ.get("AWS_REGION", "us-east-1"),
        )
        s3.head_bucket(Bucket=os.environ["S3_BUCKET"])
        checks["object_storage"] = "ok"
    except Exception as exc:
        checks["object_storage"] = 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},
    )

Node.js (Express)

// src/health.js
const express = require('express');
const router = express.Router();

router.get('/health', async (req, res) => {
  const checks = {};
  let ok = true;

  // Database connectivity check
  try {
    await db.query('SELECT 1');
    checks.database = 'ok';
  } catch (err) {
    checks.database = `error: ${err.message}`;
    ok = false;
  }

  // Cache connectivity check
  try {
    await redisClient.ping();
    checks.cache = 'ok';
  } catch (err) {
    checks.cache = `error: ${err.message}`;
    ok = false;
  }

  // Downstream service check
  try {
    const response = await fetch(process.env.DOWNSTREAM_SERVICE_URL + '/ping', {
      signal: AbortSignal.timeout(3000),
    });
    checks.downstream_service = response.ok ? 'ok' : `http_${response.status}`;
    if (!response.ok) ok = false;
  } catch (err) {
    checks.downstream_service = `error: ${err.message}`;
    ok = false;
  }

  res.status(ok ? 200 : 503).json({
    status: ok ? 'ok' : 'degraded',
    checks,
  });
});

module.exports = router;

Verify the endpoint before configuring Vigilmon:

curl -s https://your-service.example.com/health | jq .
# {"status":"ok","checks":{"database":"ok","object_storage":"ok"}}

Step 2: Configure Vigilmon HTTP Monitor

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: https://your-service.example.com/health
  3. Check interval: 60 seconds — appropriate for production services.
  4. Response timeout: 10 seconds.
  5. Expected status code: 200.
  6. JSON body assertion:
    • Path: status
    • Expected value: ok
  7. Click Save.

Vigilmon immediately begins probing from multiple external locations. A non-200 response or a failed JSON assertion triggers an alert, independently of env0's environment status dashboard.

Monitoring multiple env0 environments

env0 is often used to manage several parallel environments — production, staging, ephemeral preview environments per pull request. Create a Vigilmon monitor for each long-lived environment:

| Environment | Service URL | Interval | |---|---|---| | Production | https://api.example.com/health | 60 s | | Staging | https://api-staging.example.com/health | 60 s | | QA environment | https://api-qa.example.com/health | 120 s | | Preview (per PR) | https://pr-123.preview.example.com/health | 120 s |

Group production monitors under a Monitor group and keep ephemeral preview monitors in a separate group so they don't create noise on the main dashboard.


Step 3: Heartbeat Monitor for Scheduled env0 Deployments

env0 supports scheduled environment deployments — automatic provisioning at the start of the business day, teardown at night, or nightly re-deploy pipelines that refresh environment state from the latest IaC templates. Use Vigilmon heartbeats to confirm that these scheduled deployments complete successfully and the environment reaches a healthy state.

#!/bin/bash
# scripts/post-deploy-healthcheck.sh
# Called from env0's post-deploy hook after a successful apply

set -euo pipefail

SERVICE_URL="$1"
MAX_ATTEMPTS=12
SLEEP_SECONDS=10

echo "Waiting for $SERVICE_URL to become healthy..."
for i in $(seq 1 $MAX_ATTEMPTS); do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$SERVICE_URL/health" --max-time 5)
  if [ "$STATUS" -eq 200 ]; then
    echo "Service is healthy (attempt $i)"
    curl -s -X POST "$VIGILMON_HEARTBEAT_URL" --max-time 5
    echo "Heartbeat sent"
    exit 0
  fi
  echo "Attempt $i: status $STATUS — waiting ${SLEEP_SECONDS}s"
  sleep $SLEEP_SECONDS
done

echo "Service did not become healthy after $MAX_ATTEMPTS attempts"
exit 1

In Vigilmon, create a Heartbeat monitor with a grace period slightly above your deployment window. For a nightly scheduled redeploy that takes up to 15 minutes, use a 2-hour grace period. Store the heartbeat URL as a secret in env0 and inject it as VIGILMON_HEARTBEAT_URL.

env0 API triggered deploy with health confirmation

# scripts/trigger_env0_deploy.py
import os
import time
import requests

ENV0_API_TOKEN = os.environ["ENV0_API_TOKEN"]
ENVIRONMENT_ID = os.environ["ENV0_ENVIRONMENT_ID"]
SERVICE_HEALTH_URL = os.environ["SERVICE_HEALTH_URL"]
VIGILMON_HEARTBEAT_URL = os.environ["VIGILMON_HEARTBEAT_URL"]

def trigger_deploy():
    resp = requests.post(
        f"https://api.env0.com/environments/{ENVIRONMENT_ID}/deployments",
        headers={"Authorization": f"Bearer {ENV0_API_TOKEN}"},
        json={"deploymentType": "DEPLOY"},
    )
    resp.raise_for_status()
    return resp.json()["id"]

def wait_for_deploy(deployment_id, timeout=900):
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = requests.get(
            f"https://api.env0.com/environments/{ENVIRONMENT_ID}/deployments/{deployment_id}",
            headers={"Authorization": f"Bearer {ENV0_API_TOKEN}"},
        )
        resp.raise_for_status()
        status = resp.json()["status"]
        if status == "SUCCESS":
            return True
        if status in ("FAILED", "ABORTED", "CANCELLED"):
            raise RuntimeError(f"Deployment ended with status: {status}")
        time.sleep(30)
    raise TimeoutError("Deployment timed out")

if __name__ == "__main__":
    deployment_id = trigger_deploy()
    wait_for_deploy(deployment_id)

    # Confirm the service is healthy after deploy
    resp = requests.get(SERVICE_HEALTH_URL, timeout=10)
    resp.raise_for_status()
    assert resp.json()["status"] == "ok"

    # Send heartbeat — only fires on fully successful deploy + healthy service
    requests.post(VIGILMON_HEARTBEAT_URL, timeout=5)
    print("Deploy and health check complete — heartbeat sent")

Step 4: Alert Routing Strategy

env0 alerts on provisioning events: deployment failures, policy violations, environment cost overruns, TTL-based environment expirations. Vigilmon alerts on what's running inside those environments: an endpoint is unreachable, a health check failed, a scheduled deployment missed its window. These are distinct failure modes.

| Failure mode | Source | Route to | |---|---|---| | Deployment failure | env0 | Slack #infra-deployments + engineer | | Cost policy exceeded | env0 | FinOps team + manager | | Service endpoint down | Vigilmon | PagerDuty on-call + Slack #prod-alerts | | Scheduled deploy heartbeat 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-alerts channel.

Step 5: Correlating env0 Deployments and Vigilmon Alerts During Incidents

When Vigilmon fires a downtime alert for a service inside an env0 environment, use this checklist:

  1. Check Vigilmon probe regions — all regions failing points to the service or its infrastructure. One region failing suggests a DNS or CDN routing issue.
  2. Open env0 → check the environment's deployment history for recent changes. A deployment that completed just before the alert is the most likely cause.
  3. Review the latest deployment diff — what Terraform or OpenTofu resources changed? A misconfigured security group, updated load balancer listener, or changed DNS record can silently break reachability.
  4. Check environment variables — env0 manages environment-specific variable sets. If a variable was updated between the last healthy deployment and the failing one, that change may have propagated a bad configuration.
  5. Inspect the health check in detail — which dependency is failing? A database issue after a deploy often points to a Terraform RDS change that modified the connection endpoint or rotated credentials.

Step 6: Public Status Page

env0 environment dashboards are private provisioning views for your engineering and platform teams. Give customers and API consumers a public-facing status page using Vigilmon.

  1. In Vigilmon, go to Status Pages → Create.
  2. Add your production service monitors and any critical heartbeat monitors.
  3. Configure a custom domain (e.g., status.example.com) or use the Vigilmon subdomain.
  4. Embed the status badge in your developer documentation:
<img src="https://vigilmon.online/badge/<monitor-id>.svg" alt="Service Status" />

What Vigilmon Adds to an env0 Stack

| Capability | env0 | Vigilmon | |---|---|---| | Self-service environment provisioning | Yes | No | | IaC deployment orchestration | Yes | No | | Cost policies and governance controls | Yes | No | | Environment TTL and lifecycle management | Yes | No | | External synthetic HTTP probing | No | Yes | | DNS resolution failure detection | No | Yes | | TLS/SSL certificate expiry alerts | No | Yes | | Scheduled deployment heartbeat monitoring | No | Yes | | Public status page | No | Yes | | Independent of cloud provider health | No | Yes |


env0 gives you self-service control over the full lifecycle of your cloud environments — from provisioning to teardown, with governance and cost guardrails built in. Vigilmon gives you the external, synthetic view that tells you what your users actually experience while those environments are running. Together they cover every dimension of cloud environment reliability: from IaC-orchestrated provisioning to customer-facing uptime.

Add external uptime monitoring to your env0-managed environments today — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →