Skip to content

ops: install a disk alarm as part of box setup - #306

Merged
ralyodio merged 1 commit into
masterfrom
ops/disk-alarm
Sep 25, 2026
Merged

ralyodio merged 1 commit into
masterfrom
ops/disk-alarm

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

dev2 runs crawlproof and nichedb on one Postgres, so disk is a shared failure mode — a full filesystem stops writes for every database on it and takes both sites down together. Nothing was watching it.

Folded into system_setup rather than shipped as a separate script, since setup-supabase.sh is deployed as a single scp'd file and a second file is a step someone forgets.

Three checks

  • warn at 70%
  • critical at 85%
  • critical whenever the observed growth rate would fill the disk inside 48 hours

That third one is the one that earns its keep. A percentage threshold is useless against a burst: this box grew 149 → 247 GB in four hours (~22 GB/h) during nichedb's catalogue import, and "15% used" never looked alarming at any point.

Why cron and not a session watch

The first version of this lived inside an agent session's monitor. It died with the session, and ~100 GB of overnight growth went unnoticed. Monitoring that isn't on the box isn't monitoring.

The estimate that nearly misled us

The comments record it deliberately: nichedb's "6 GB/day" was measured on Railway with a throttled disk. The same importers ran roughly 40x faster on dev2's NVMe. Growth figures do not transfer between hosts — that mistake made days of headroom look like months.

Already installed and logging on dev2 (/var/log/dev2-disk.log doubles as the growth record, which is the right place to read a real steady-state rate off once nichedb's importers finish). The nichedb session has asked this session to own it.

dev2 runs crawlproof and nichedb on one Postgres, so disk is a SHARED failure
mode: a full filesystem stops writes for every database on it and takes both
sites down together. Nothing was watching it.

Folded into system_setup rather than shipped as a separate script, because
setup-supabase.sh is deployed as a single scp'd file and a second file is a
step someone forgets.

Three checks: warn at 70%, critical at 85%, and — the one that earns its keep —
critical whenever the observed growth rate would fill the disk inside 48 hours.
A percentage threshold is useless against a burst: this box grew 149 -> 247 GB
in four hours (~22 GB/h) during nichedb's catalogue import and 15% never
looked alarming.

It runs from cron on the box, not from an agent session. The first version of
this lived in a session monitor, died with that session, and ~100 GB of
overnight growth went unnoticed as a result. Monitoring that is not on the box
is not monitoring.

The comments also record the estimate that nearly misled us: nichedb's
"6 GB/day" was measured on Railway with a throttled disk, and the same
importers ran ~40x faster on NVMe. Growth figures do not transfer between
hosts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

48 finding(s)

HIGH/CRITICAL: 2 | MEDIUM: 31 | LOW: 15

Severity Rule Location
HIGH tls-verification-disabled lib/onion.ts:48
HIGH secret-generic-credential lib/sp/platforms/facebook.ts:32
MEDIUM js-unescaped-html-sink app/(app)/dashboard/admin/email-broadcast/EmailBroadcastForm.tsx:125
MEDIUM js-unescaped-html-sink app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/page.tsx:214
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:67
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:97
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:104
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:110
MEDIUM js-unescaped-html-sink app/(marketing)/recent/page.tsx:186
MEDIUM js-unescaped-html-sink app/(marketing)/recent/page.tsx:190
MEDIUM js-unescaped-html-sink app/c/[project]/[slug]/page.tsx:77
MEDIUM js-unescaped-html-sink app/c/[project]/page.tsx:57
MEDIUM js-unescaped-html-sink app/careers.js/route.ts:228
MEDIUM js-unescaped-html-sink app/careers.js/route.ts:285
MEDIUM js-unescaped-html-sink app/layout.tsx:129
MEDIUM js-open-redirect app/login/form.tsx:39
MEDIUM js-unescaped-html-sink app/r/[token]/page.tsx:176
MEDIUM js-open-redirect app/signup/form.tsx:43
MEDIUM js-open-redirect components/billing/buy-credits-modal.tsx:98
MEDIUM js-unescaped-html-sink components/json-ld.tsx:8
MEDIUM js-unescaped-html-sink components/report/markdown-view.tsx:15
MEDIUM js-unescaped-html-sink lib/careers/page-templates.ts:198
MEDIUM js-dynamic-code-execution lib/crawl-limits.ts:67
MEDIUM redos-nested-quantifier lib/emailMarkdown.ts:41
MEDIUM redos-nested-quantifier lib/emailMarkdown.ts:324
MEDIUM redos-nested-quantifier lib/lx/articleGen.ts:99
MEDIUM redos-nested-quantifier lib/tracker/agent-gate.ts:61
MEDIUM sh-remote-script-execution prober/deploy/provision.sh:30
MEDIUM sql-template-interpolation scripts/detect-slot-themes.ts:31
MEDIUM sql-template-interpolation scripts/purge-constructed-keywords.ts:163
MEDIUM sql-template-interpolation scripts/purge-offniche-keywords.ts:124
MEDIUM js-dynamic-code-execution scripts/test-crawl-limits.mjs:14
MEDIUM js-dynamic-code-execution scripts/test-crawl-limits.mjs:24
LOW secret-generic-credential app/(marketing)/docs/autoblog-webhook/page.tsx:145
LOW secret-generic-credential lib/sp/platforms/linkedin.ts:25
LOW js-dynamic-code-execution tests/careers-page-templates.test.ts:21
LOW js-dynamic-code-execution tests/careers-widget-script.test.ts:19
LOW js-dynamic-code-execution tests/careers-widget-script.test.ts:69
LOW js-dynamic-code-execution tests/contract/ad-visitor-id.test.ts:51
LOW js-dynamic-code-execution tests/contract/ad-visitor-id.test.ts:52
LOW js-dynamic-code-execution tests/contract/ads-click-cooldown-redis.test.ts:20
LOW js-dynamic-code-execution tests/contract/ads-click-cooldown-redis.test.ts:24
LOW js-dynamic-code-execution tests/contract/ads-click-cooldown-redis.test.ts:25
LOW js-dynamic-code-execution tests/contract/ads-click-cooldown-redis.test.ts:26
LOW js-dynamic-code-execution tests/contract/ads-click-cooldown-redis.test.ts:31
LOW js-dynamic-code-execution tests/contract/ads-click-cooldown-redis.test.ts:35
LOW secret-generic-credential tests/contract/posthog-integration.test.ts:13
LOW secret-generic-credential tests/lead-campaign.test.ts:16

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 8e9646c into master Sep 25, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant