ArangoDB is a multi-model database that handles documents, graphs, and key-value data in a single engine, deployed as a single server or a distributed cluster with coordinators and DB-servers. Its flexibility is a production strength, but its distributed mode introduces multiple independent failure surfaces: a coordinator being removed from the agency pool, a DB-server falling out of sync with its leader shard, and collection-level replication factor violations can all cause partial write failures that internal health dashboards may not surface clearly. Vigilmon adds an independent external layer — HTTP health checks on your ArangoDB-connected services that verify real coordinator connectivity, surface shard availability issues, and alert on application-level failures before your users notice.
This tutorial shows you how to build meaningful monitoring for ArangoDB-backed applications using Vigilmon.
What You'll Build
- A health endpoint that probes real ArangoDB coordinator connectivity and database availability
- Shard health and replication factor detection
- A Vigilmon HTTP monitor with appropriate timeout and assertion settings
- A heartbeat monitor for ArangoDB-backed background workers
- A cluster-aware alerting strategy for coordinator and DB-server tiers
Prerequisites
- A running ArangoDB instance (single-server or cluster mode)
- An application connected to ArangoDB (any runtime)
- A free account at vigilmon.online
Step 1: Add a Health Endpoint to Your ArangoDB Service
Add a /health endpoint that performs a lightweight ArangoDB operation to verify real connectivity and checks cluster state via the /_api/cluster/health endpoint (cluster mode) or the built-in /_admin/server/availability endpoint (any mode).
Node.js (arangojs)
// routes/health.js
import { Database } from 'arangojs';
import fetch from 'node-fetch';
const db = new Database({
url: process.env.ARANGODB_URL || 'http://localhost:8529',
databaseName: process.env.ARANGODB_DATABASE || '_system',
auth: {
username: process.env.ARANGODB_USER || 'root',
password: process.env.ARANGODB_PASSWORD || '',
},
});
const ARANGO_URL = process.env.ARANGODB_URL || 'http://localhost:8529';
export async function healthHandler(req, res) {
const checks = {};
let ok = true;
const start = Date.now();
// Check 1: ArangoDB availability endpoint
try {
const availRes = await fetch(`${ARANGO_URL}/_admin/server/availability`, {
signal: AbortSignal.timeout(5000),
});
checks.serverAvailability = availRes.ok ? 'ok' : `http ${availRes.status}`;
checks.availabilityLatencyMs = Date.now() - start;
if (!availRes.ok) ok = false;
} catch (err) {
checks.serverAvailability = `error: ${err.message}`;
ok = false;
}
// Check 2: AQL probe query
try {
const queryStart = Date.now();
const cursor = await db.query('RETURN DATE_NOW()');
const result = await cursor.next();
checks.queryStatus = result ? 'ok' : 'no result';
checks.queryLatencyMs = Date.now() - queryStart;
if (!result) ok = false;
} catch (err) {
checks.queryStatus = `error: ${err.message}`;
ok = false;
}
// Check 3: Cluster health (cluster mode only)
if (process.env.ARANGODB_CLUSTER === 'true') {
try {
const clusterRes = await fetch(
`${ARANGO_URL}/_admin/cluster/health`,
{
headers: {
Authorization: `Basic ${Buffer.from(
`${process.env.ARANGODB_USER || 'root'}:${process.env.ARANGODB_PASSWORD || ''}`
).toString('base64')}`,
},
signal: AbortSignal.timeout(5000),
}
);
if (clusterRes.ok) {
const body = await clusterRes.json();
const health = body.Health || {};
const nodes = Object.values(health);
const unhealthy = nodes.filter(n => n.Status !== 'GOOD');
checks.clusterNodeCount = nodes.length;
checks.unhealthyNodes = unhealthy.length;
checks.clusterStatus = unhealthy.length > 0 ? `${unhealthy.length} nodes not GOOD` : 'ok';
if (unhealthy.length > 0) ok = false;
} else {
checks.clusterStatus = `http ${clusterRes.status}`;
ok = false;
}
} catch (err) {
checks.clusterStatus = `error: ${err.message}`;
ok = false;
}
}
res.status(ok ? 200 : 503).json({
status: ok ? 'ok' : 'degraded',
service: 'arangodb-service',
checks,
});
}
Python (python-arango)
# routers/health.py
import os, time, base64
import requests
from arango import ArangoClient
from fastapi import APIRouter
from fastapi.responses import JSONResponse
router = APIRouter()
ARANGO_URL = os.environ.get('ARANGODB_URL', 'http://localhost:8529')
ARANGO_USER = os.environ.get('ARANGODB_USER', 'root')
ARANGO_PASSWORD = os.environ.get('ARANGODB_PASSWORD', '')
ARANGO_DB = os.environ.get('ARANGODB_DATABASE', '_system')
client = ArangoClient(hosts=ARANGO_URL)
db = client.db(ARANGO_DB, username=ARANGO_USER, password=ARANGO_PASSWORD)
auth_header = {
'Authorization': 'Basic ' + base64.b64encode(
f'{ARANGO_USER}:{ARANGO_PASSWORD}'.encode()
).decode()
}
@router.get("/health")
async def health():
checks = {}
ok = True
start = time.monotonic()
# Check 1: Server availability
try:
r = requests.get(f'{ARANGO_URL}/_admin/server/availability', timeout=5)
checks['availabilityLatencyMs'] = int((time.monotonic() - start) * 1000)
checks['serverAvailability'] = 'ok' if r.ok else f'http {r.status_code}'
if not r.ok:
ok = False
except Exception as e:
checks['serverAvailability'] = f'error: {e}'
ok = False
# Check 2: AQL probe query
try:
q_start = time.monotonic()
cursor = db.aql.execute('RETURN DATE_NOW()')
result = list(cursor)
checks['queryStatus'] = 'ok' if result else 'no result'
checks['queryLatencyMs'] = int((time.monotonic() - q_start) * 1000)
if not result:
ok = False
except Exception as e:
checks['queryStatus'] = f'error: {e}'
ok = False
# Check 3: Cluster health
if os.environ.get('ARANGODB_CLUSTER') == 'true':
try:
r = requests.get(
f'{ARANGO_URL}/_admin/cluster/health',
headers=auth_header,
timeout=5,
)
if r.ok:
health_data = r.json().get('Health', {})
nodes = list(health_data.values())
unhealthy = [n for n in nodes if n.get('Status') != 'GOOD']
checks['clusterNodeCount'] = len(nodes)
checks['unhealthyNodes'] = len(unhealthy)
checks['clusterStatus'] = f'{len(unhealthy)} nodes not GOOD' if unhealthy else 'ok'
if unhealthy:
ok = False
else:
checks['clusterStatus'] = f'http {r.status_code}'
ok = False
except Exception as e:
checks['clusterStatus'] = f'error: {e}'
ok = False
return JSONResponse(
status_code=200 if ok else 503,
content={'status': 'ok' if ok else 'degraded', 'service': 'arangodb-service', 'checks': checks},
)
Go (arangodb-go-driver)
// internal/health/handler.go
package health
import (
"context"
"encoding/json"
"net/http"
"os"
"time"
driver "github.com/arangodb/go-driver"
"github.com/arangodb/go-driver/http"
)
func Handler(arangoClient driver.Client) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
checks := map[string]any{}
ok := true
// Check 1: Database version (lightweight connectivity probe)
start := time.Now()
info, err := arangoClient.Version(ctx)
if err != nil {
checks["serverAvailability"] = "error: " + err.Error()
ok = false
} else {
checks["serverAvailability"] = "ok"
checks["availabilityLatencyMs"] = time.Since(start).Milliseconds()
checks["arangoVersion"] = info.Version
}
// Check 2: AQL probe query
db, err := arangoClient.Database(ctx, os.Getenv("ARANGODB_DATABASE"))
if err == nil {
qStart := time.Now()
cursor, err := db.Query(ctx, "RETURN DATE_NOW()", nil)
if err != nil {
checks["queryStatus"] = "error: " + err.Error()
ok = false
} else {
defer cursor.Close()
checks["queryStatus"] = "ok"
checks["queryLatencyMs"] = time.Since(qStart).Milliseconds()
}
} else {
checks["queryStatus"] = "error: database open: " + err.Error()
ok = false
}
// Check 3: Cluster health (cluster mode only)
if os.Getenv("ARANGODB_CLUSTER") == "true" {
cluster, err := arangoClient.Cluster(ctx)
if err == nil {
health, err := cluster.Health(ctx)
if err == nil {
unhealthy := 0
for _, node := range health.Health {
if node.Status != driver.ServerStatusGood {
unhealthy++
}
}
checks["clusterNodeCount"] = len(health.Health)
checks["unhealthyNodes"] = unhealthy
checks["clusterStatus"] = map[bool]string{true: "ok", false: "degraded"}[unhealthy == 0]
if unhealthy > 0 {
ok = false
}
} else {
checks["clusterStatus"] = "error: " + err.Error()
ok = false
}
}
}
w.Header().Set("Content-Type", "application/json")
if !ok {
w.WriteHeader(http.StatusServiceUnavailable)
}
_ = json.NewEncoder(w).Encode(map[string]any{
"status": map[bool]string{true: "ok", false: "degraded"}[ok],
"service": "arangodb-service",
"checks": checks,
})
}
}
Deploy and verify manually before wiring it into Vigilmon:
curl -i https://your-app.example.com/health
# HTTP/1.1 200 OK
# {"status":"ok","service":"arangodb-service","checks":{"serverAvailability":"ok","queryStatus":"ok","queryLatencyMs":8,"clusterStatus":"ok"}}
Step 2: Configure the Vigilmon HTTP Monitor
- Log in to Vigilmon → Add Monitor → HTTP.
- URL: your service's health endpoint (e.g.
https://api.example.com/health). - Check interval: 60 seconds.
- Response timeout: 15 seconds.
- Expected status:
200. - JSON assertion: path
status, expected valueok. - Save the monitor.
Vigilmon probes from multiple geographic regions simultaneously. A single-region blip will not page you; multi-region consensus is required before an incident opens. This is valuable for ArangoDB cluster deployments where a coordinator failure may affect some client connections but not others depending on load balancer behavior.
Tip: Create a second monitor with a JSON assertion on
checks.unhealthyNodesequals0. This surfaces partial cluster degradation — a DB-server losing shard leadership — before write errors reach your application.
Step 3: Heartbeat Monitoring for ArangoDB-Backed Workers
ArangoDB is commonly used for graph analytics pipelines, recommendation engines backed by traversal queries, and document-processing workflows. Workers that depend on ArangoDB will stall silently when coordinator connectivity is lost or when shard rebalancing causes temporary query failures. Vigilmon's heartbeat monitors detect silent worker death: your worker pings Vigilmon after each successful processing cycle, and if pings stop arriving, Vigilmon fires an alert.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat.
- Set the name:
arangodb-worker. - Set the expected interval: 5 minutes (adjust to your pipeline frequency).
- Set the grace period: 10 minutes.
- Save — copy the unique heartbeat URL.
Wire It Into Your Worker
Node.js:
import { Database } from 'arangojs';
import fetch from 'node-fetch';
const db = new Database({
url: process.env.ARANGODB_URL,
auth: { username: process.env.ARANGODB_USER, password: process.env.ARANGODB_PASSWORD },
});
const collection = db.collection('jobs');
async function runWorkerCycle() {
const cursor = await db.query({
query: 'FOR job IN jobs FILTER job.status == "pending" LIMIT 50 RETURN job',
bindVars: {},
});
const jobs = await cursor.all();
for (const job of jobs) {
await collection.update(job._key, { status: 'processed', processedAt: Date.now() });
}
// Only ping Vigilmon after successful processing
await fetch(process.env.VIGILMON_HEARTBEAT_URL).catch(() => {});
}
setInterval(runWorkerCycle, 5 * 60 * 1000);
Python:
import os, time, requests
from arango import ArangoClient
client = ArangoClient(hosts=os.environ['ARANGODB_URL'])
db = client.db(
os.environ['ARANGODB_DATABASE'],
username=os.environ['ARANGODB_USER'],
password=os.environ['ARANGODB_PASSWORD'],
)
def run_worker_cycle():
cursor = db.aql.execute(
"FOR job IN jobs FILTER job.status == 'pending' LIMIT 50 RETURN job"
)
jobs = list(cursor)
jobs_col = db.collection('jobs')
for job in jobs:
jobs_col.update({'_key': job['_key'], 'status': 'processed'})
# Only ping Vigilmon after successful batch
requests.get(os.environ['VIGILMON_HEARTBEAT_URL'], timeout=5)
while True:
try:
run_worker_cycle()
except Exception as e:
print(f'Worker cycle failed — heartbeat NOT sent: {e}')
time.sleep(300)
Step 4: ArangoDB Built-In Health Endpoints
ArangoDB exposes several built-in administrative endpoints you can monitor directly with Vigilmon:
| Endpoint | Purpose | Expected response |
|---|---|---|
| /_admin/server/availability | Single-server readiness | HTTP 200 when ready |
| /_admin/cluster/health | Cluster node health | JSON Health map, all Status: GOOD |
| /_api/version | ArangoDB version (connectivity probe) | HTTP 200 with version JSON |
For direct monitoring of the ArangoDB coordinator without an application-level proxy, point a Vigilmon monitor at /_admin/server/availability:
URL: https://arangodb.internal:8529/_admin/server/availability
Auth: Basic (root / your-password)
Expected status: 200
Interval: 60s
This is the most direct signal that ArangoDB is serving requests. Use it alongside your application-level health endpoint for defense in depth.
Step 5: Alerting Strategy
| Alert channel | When to use | |---|---| | PagerDuty | Server availability check fails (coordinator unreachable) | | PagerDuty | AQL probe query fails (database not accepting queries) | | Slack webhook | Unhealthy cluster nodes > 0 (partial degradation) | | Slack + Email | Query latency > 1000ms sustained (cluster under load) | | PagerDuty | Heartbeat missed (worker pipeline stopped) | | Status page | Any user-facing service backed by ArangoDB |
Set response time thresholds as an early warning:
- Alert at
50msfor AQL probe query latency (simple queries should complete in < 20ms) - Alert at
1000msfor health endpoint response time
What Vigilmon Catches That Internal Monitoring Misses
| Scenario | ArangoDB internal metrics | Vigilmon | |---|---|---| | Coordinator reachable internally but not from app servers | No | HTTP probe from app's perspective | | DB-server shard leader change causes transient write failures | Cluster log only | AQL probe fails — 503 fires | | Agency quorum lost (cluster operations blocked) | Internal cluster event | Cluster health check returns degraded | | Worker pipeline silently stopped | Process appears running | Heartbeat grace period expires → alert | | AQL query timeout on overloaded cluster | Latency metric only | Query probe fails with timeout | | Monitoring pipeline (Prometheus/Grafana) fails | Alerts silently lost | Vigilmon is external — unaffected |
ArangoDB's multi-model flexibility makes it a powerful choice for complex data workloads — but distributed cluster failures require external monitoring that goes beyond process-level checks. External health checks with Vigilmon give you the independent signal you need — before query failures reach your users.
Start monitoring your ArangoDB applications in under 5 minutes — register free at vigilmon.online.