feat(monitoring): probe the Umami analytics collector every 15 minutes (#5565)#5567
Merged
Conversation
#5565) The collector's Node process OOM-died on 2026-07-20 and Railway neither restarted it nor flipped the deployment off SUCCESS, so nothing alerted. ~1.1M events were dropped over 4 days and the gap was only found by hand while asking an unrelated question about the #5534 activation funnel. Probes the collector directly rather than trusting Railway's deployment status, which stayed green throughout: - heartbeat /api/heartbeat -> 200 (app process alive) - tracker /script.js -> 200 + must contain /api/send (browsers can load the real tracker, not an error page answering 200) - ingest GET /api/send -> 405/400 (write path mounted; GET is the wrong verb on purpose so probing records no synthetic events) Three attempts per probe before alerting so a deploy restart cannot page anyone. A named User-Agent is mandatory, not cosmetic: Cloudflare's WAF 403s a bare curl agent on this host, which would alert on a healthy collector — that case is classified separately so it reads as a monitor bug. Unlike seed-freshness-monitor.yml this is NOT gated on a green main: the collector's health is independent of repo state, and a gate is one more way for the monitor to go quiet. Verified against production: green on the live collector, red (exit 1) against a dead origin and against an origin that 200s but is not the collector. Claude-Session: https://claude.ai/code/session_017LEK5hk1YCDNTwrvJkYV2k
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a 15-minute scheduled probe of the self-hosted Umami analytics collector, closing items 3–4 of #5565.
On 2026-07-20 17:13 UTC the collector's Node process OOM-died. Railway neither restarted it nor flipped the deployment off
SUCCESS, so nothing alerted for 4 days — ~1.1M events dropped across every tracked funnel (gate-hit, the #4931 checkout funnel,sign-up, and #5534's brand-new activation events, which shipped into a dead collector). The gap was found by hand, while asking an unrelated question about whether anyone had used the #5534 onboarding.The service side is already mitigated (
NODE_OPTIONS=--max-old-space-size=8192,restartPolicyType: ALWAYS). This PR is the part that makes the next failure visible in ≤15 minutes instead of ≤4 days.Design decisions
/api/heartbeat/script.js/api/sendGET /api/sendGETon the ingest route is deliberate. A mounted route rejects the wrong verb without recording anything, while a dead origin 5xxs. NeverPOST— that would pollute production analytics with synthetic monitor traffic. A test asserts the source contains noPOST.curl/*agent on this host (verified:curldefault → 403, named agent → 200). A probe without one alerts on a healthy collector — so a 403 is classified with its own message pointing at the probe rather than the collector.seed-freshness-monitor.yml. The collector's health is independent of repo state, and a gate is one more way for the monitor to go quiet — the precise failure mode being fixed.Validation
node --test), including the outage's exact signature (502 on all three paths), the 200-but-not-the-tracker case, the WAF-403 misclassification guard, and a workflow-wiring assertion.3 probes OK.127.0.0.1:9) → exit 1, all three probesrequest failedexample.com) → exit 1, all threeHTTP 404docs:check(80 doc claims match code),docs:statsregenerated and committed (workflowCount22→23), biome, markdownlint all clean.Note:
schedule/workflow_dispatchonly fire from the default branch, so the workflow's first real run is post-merge. The script itself is verified end-to-end above.Follow-up (not in this PR)
The memory bump is a runway extension, not a fix. The upstream
sessionData.updateMany()P2002 flood (umami-software/umami#4183) resumed at full rate on restart — ~12 errors in 51s. At the observed retention rate 8 GB buys roughly 8–10 months. Upgrading the image is not an escape hatch: #4183 is still open and reported crash-looping on v3.2.0, soautoUpdates: {type: "patch"}should stay. This monitor is what makes the recurrence a 15-minute incident instead of a silent one.Refs #5565
https://claude.ai/code/session_017LEK5hk1YCDNTwrvJkYV2k