Parseable is a cloud-native log analytics platform built around S3 (or S3-compatible storage) as its durable backend. Instead of running a complex Elasticsearch cluster, you ship logs to Parseable's lightweight ingestor, query them with SQL via its REST API, and let your object store handle the heavy lifting. The result is a dramatically simpler operations story — but that simplicity can mask failures that are hard to detect without external monitoring.
In this tutorial you'll set up end-to-end monitoring for your Parseable deployment using Vigilmon, covering ingestor health, query engine response time, storage backend connectivity, log stream health, and retention policy execution. Free tier, no credit card required.
Why Parseable needs external monitoring
Parseable's architecture — ingestor → object store → query engine — means failures can appear at any layer while the other layers continue to report healthy:
- Ingestor accepting connections but not writing to S3 — logs appear to land (HTTP 200 from the ingestor) but are silently dropped; your object store metrics stay flat while ingest appears healthy
- Query engine returning stale data — a compaction or staging-flush failure means recent logs don't appear in queries; users think logs are missing when they're actually stuck in the staging buffer
- S3 connectivity intermittent — transient S3 errors cause ingest batches to fail and retry; no alert fires until the retry queue fills and drops begin
- Retention policy not executing — your storage costs grow unboundedly because the cleanup job silently fails after a permissions change
- Log stream schema drift — an upstream service changes its log format; Parseable rejects new events while the stream shows as "active"
External monitoring with Vigilmon catches the things your internal metrics miss: whether the ingestor is reachable from outside your network, whether queries return data in acceptable time, and whether the whole pipeline is healthy end-to-end.
What you'll need
- A running Parseable server (Docker, Kubernetes, or binary)
- An S3 bucket (AWS S3, MinIO, Cloudflare R2, etc.)
- A free Vigilmon account
curlandjqfor the optional manual checks below
Step 1: Confirm the Parseable health endpoint
Parseable exposes a liveness endpoint at /api/v1/liveness. Check it:
curl -s http://localhost:8000/api/v1/liveness
A healthy response is a plain 200 OK with no body. If you've enabled authentication (recommended for production), you don't need auth headers for the liveness endpoint — it bypasses auth by design.
Also check the readiness endpoint which includes storage backend connectivity:
curl -s -o /dev/null -w "%{http_code}" \
http://localhost:8000/api/v1/readiness
A 200 means the ingestor can reach its S3 backend. A 503 means storage connectivity is broken. This is the more important check.
Step 2: Add the readiness check to Vigilmon
- Log in to Vigilmon and click Add Monitor.
- Choose HTTP(S) as the monitor type.
- Enter your Parseable readiness URL:
https://parseable.your-domain.com/api/v1/readiness - Set the check interval to 60 seconds.
- Under Expected Status Code, enter
200. - Click Save.
This single check covers both ingestor health and S3 connectivity in one probe — if either breaks, the readiness endpoint returns 503 and Vigilmon fires an alert.
Step 3: Monitor query engine response time
A healthy ingestor doesn't guarantee healthy queries. Add a query latency check by probing Parseable's query API with a known-fast SQL query against a lightweight log stream:
#!/bin/bash
# query-latency-check.sh
PARSEABLE_URL="${PARSEABLE_URL:-http://localhost:8000}"
STREAM="${STREAM:-application}" # replace with your stream name
USERNAME="${PARSEABLE_USERNAME:-admin}"
PASSWORD="${PARSEABLE_PASSWORD:-password}"
START=$(date +%s%3N)
HTTP_CODE=$(curl -s -o /tmp/parseable_resp.json -w "%{http_code}" \
-u "${USERNAME}:${PASSWORD}" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"SELECT count(*) as total FROM \\\"${STREAM}\\\" LIMIT 1\",
\"startTime\": \"$(date -u -d '5 minutes ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-5M +%Y-%m-%dT%H:%M:%SZ)\",
\"endTime\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
}" \
"${PARSEABLE_URL}/api/v1/query")
END=$(date +%s%3N)
LATENCY=$((END - START))
if [ "$HTTP_CODE" != "200" ]; then
echo "FAIL: query returned HTTP $HTTP_CODE"
cat /tmp/parseable_resp.json
exit 1
fi
if [ "$LATENCY" -gt 3000 ]; then
echo "SLOW: query took ${LATENCY}ms (threshold 3000ms)"
exit 1
fi
echo "OK: ${LATENCY}ms"
exit 0
Wrap this script in a small HTTP service (see Step 6 for a complete example) and add a Vigilmon monitor for the query latency endpoint. A non-200 response or a body that doesn't start with OK: will trigger an alert.
Step 4: Monitor log stream health
Parseable exposes stream metadata via /api/v1/logstream/{stream}. You can check whether your critical streams are active and receiving data:
// stream-health-check.js — Express.js health aggregator
import express from "express";
import fetch from "node-fetch";
const app = express();
const PARSEABLE_URL = process.env.PARSEABLE_URL || "http://localhost:8000";
const AUTH = Buffer.from(
`${process.env.PARSEABLE_USERNAME || "admin"}:${process.env.PARSEABLE_PASSWORD || "password"}`
).toString("base64");
const CRITICAL_STREAMS = (process.env.CRITICAL_STREAMS || "application,nginx").split(",");
app.get("/stream-health", async (req, res) => {
const results = await Promise.allSettled(
CRITICAL_STREAMS.map(async (stream) => {
const r = await fetch(
`${PARSEABLE_URL}/api/v1/logstream/${stream}/info`,
{ headers: { Authorization: `Basic ${AUTH}` } }
);
if (!r.ok) throw new Error(`stream ${stream} returned ${r.status}`);
const info = await r.json();
return { stream, events: info.ingestion?.count ?? 0, ok: true };
})
);
const failed = results
.filter((r) => r.status === "rejected")
.map((r) => r.reason.message);
if (failed.length > 0) {
return res.status(503).json({ status: "degraded", failed });
}
res.json({
status: "ok",
streams: results.map((r) => r.value),
});
});
app.listen(9091, () => console.log("Stream health check listening on :9091"));
Add a Vigilmon HTTP monitor for https://parseable.your-domain.com/stream-health (or wherever you deploy the sidecar), checking for a 200 status and "status":"ok" in the response body.
Step 5: Monitor retention policy execution
Parseable enforces retention via scheduled compaction jobs. If the retention policy silently fails, your S3 costs grow indefinitely. The cleanest way to monitor this is to check the storage statistics endpoint and alert when per-stream storage grows beyond an expected threshold:
#!/bin/bash
# retention-check.sh — alert when stream storage exceeds threshold
PARSEABLE_URL="${PARSEABLE_URL:-http://localhost:8000}"
STREAM="${STREAM:-application}"
MAX_STORAGE_MB="${MAX_STORAGE_MB:-10240}" # 10 GB default threshold
USERNAME="${PARSEABLE_USERNAME:-admin}"
PASSWORD="${PARSEABLE_PASSWORD:-password}"
RESP=$(curl -sf -u "${USERNAME}:${PASSWORD}" \
"${PARSEABLE_URL}/api/v1/logstream/${STREAM}/info")
if [ $? -ne 0 ]; then
echo "FAIL: could not reach Parseable API"
exit 1
fi
STORAGE_MB=$(echo "$RESP" | jq -r '.storage.storageSize // 0' | awk '{print int($1/1048576)}')
if [ "$STORAGE_MB" -gt "$MAX_STORAGE_MB" ]; then
echo "WARN: stream ${STREAM} is using ${STORAGE_MB}MB (threshold ${MAX_STORAGE_MB}MB) — retention may not be running"
exit 1
fi
echo "OK: stream ${STREAM} using ${STORAGE_MB}MB"
exit 0
Deploy this as a sidecar and expose it on an HTTP endpoint. Add a Vigilmon monitor for it — if storage grows beyond the threshold (indicating retention isn't cleaning up), the monitor fires an alert before your bill does.
Step 6: Full sidecar for all Parseable health checks
Here's a single Express.js service that exposes all three custom health endpoints in one deployment:
// parseable-health-sidecar.js
import express from "express";
import fetch from "node-fetch";
const app = express();
const PARSEABLE_URL = process.env.PARSEABLE_URL || "http://localhost:8000";
const AUTH = Buffer.from(
`${process.env.PARSEABLE_USERNAME}:${process.env.PARSEABLE_PASSWORD}`
).toString("base64");
const HEADERS = { Authorization: `Basic ${AUTH}`, "Content-Type": "application/json" };
const CRITICAL_STREAMS = (process.env.CRITICAL_STREAMS || "application").split(",");
// Query latency check
app.get("/query-health", async (req, res) => {
const stream = CRITICAL_STREAMS[0];
const now = new Date();
const fiveMinAgo = new Date(now - 5 * 60 * 1000);
const start = Date.now();
const r = await fetch(`${PARSEABLE_URL}/api/v1/query`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
query: `SELECT count(*) FROM "${stream}" LIMIT 1`,
startTime: fiveMinAgo.toISOString(),
endTime: now.toISOString(),
}),
});
const latency = Date.now() - start;
if (!r.ok || latency > 3000) {
return res.status(503).json({ status: "degraded", latencyMs: latency, httpStatus: r.status });
}
res.json({ status: "ok", latencyMs: latency });
});
// Stream health check
app.get("/stream-health", async (req, res) => {
const results = await Promise.allSettled(
CRITICAL_STREAMS.map(async (stream) => {
const r = await fetch(`${PARSEABLE_URL}/api/v1/logstream/${stream}/info`, { headers: HEADERS });
if (!r.ok) throw new Error(`${stream}: HTTP ${r.status}`);
return stream;
})
);
const failed = results.filter((r) => r.status === "rejected").map((r) => r.reason.message);
if (failed.length) return res.status(503).json({ status: "degraded", failed });
res.json({ status: "ok", streams: CRITICAL_STREAMS });
});
app.listen(9091, () => console.log("Parseable health sidecar on :9091"));
Step 7: Configure Vigilmon alert channels
With all monitors in place, route alerts where your team responds:
- In the Vigilmon dashboard, click Alert Channels.
- Add a Slack webhook for your
#platform-logschannel. - Add an Email channel for your on-call list.
- For each Parseable monitor, set Alert after N failures to
2to filter transient blips.
What you're monitoring now
| Monitor | What it catches |
|---|---|
| /api/v1/readiness | S3 connectivity broken, ingestor failed to start |
| Query latency endpoint | Slow compaction, staging buffer overflow |
| Stream health endpoint | Schema drift, ingestion rejection, missing stream |
| Retention check endpoint | Compaction job failure, S3 permission change |
Operational tips for Parseable
- Monitor your S3 bucket separately — Vigilmon can't directly check S3, but you can expose a small S3 connectivity probe as another endpoint. Use the AWS SDK to do a
HeadBucketcall and expose the result. - Set ingestion rate alerts — if events-per-minute for a critical stream drops to zero during business hours, that usually means an upstream producer broke before Parseable did.
- Test retention before you need it — run a retention dry-run against a non-critical stream and confirm the storage stats change. A silent failure here is expensive to discover at month-end billing.
External monitoring with Vigilmon gives you the outside-in view of Parseable that your internal metrics can't provide. When Parseable is down, your internal metrics pipeline is also down — which means Vigilmon is often the first place you'll know about an outage.