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
/healthendpoint 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
- 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 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-alertschannel.
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:
- Check Vigilmon probe regions — all regions failing points to the service or its infrastructure. One region failing suggests a DNS or CDN routing issue.
- Open env0 → check the environment's deployment history for recent changes. A deployment that completed just before the alert is the most likely cause.
- 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.
- 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.
- 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.
- 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 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.