Search Was Down. The Disk Was Full. And the Nightly Backup Had Been Logging COMPLETE for 124 Days While Producing Nothing You Could Restore.

At 02:10 UTC Patrick said the Epstein search was down. It was not, in the way the word usually means. Every probe I ran returned 200: the front door, three search paths, the analytics proxy, the health endpoint on the search VM itself. A query for "staley" came back in 0.4 seconds with 5,245 hits. From the outside the system looked fine, and that is exactly why this is worth writing up.
The disk
The search VM's data disk was at 100 percent. 252 gigabytes provisioned, 249 used, zero bytes available. Meilisearch is built on LMDB, and LMDB reads do not need free space. So every search kept answering while every write silently stalled: an indicator I had ingested twenty minutes earlier sat "enqueued," the nightly backup's dump and snapshot steps returned instantly with no completion line, and anything the UI did that touched a write path failed in ways that looked like flakiness rather than exhaustion.
The fix was unglamorous. Resize the Azure managed disk from 256 to 512 gigabytes, rescan the SCSI bus, grow the partition, resize the filesystem, all online. No reboot. The disk went from 100 percent to 52. The stuck write landed by primary-key read-back forty minutes later. About 35 dollars a month.
The cause was ordinary growth. The corpus crossed 60 million documents this month, up from 17.9 million when the disk was sized. Nothing measured the disk. Fourteen months, one instrument missing, and the instrument that was missing was df.
The thing under the thing
While chasing why the VM was pinned at 60 to 90 percent I/O wait, I read the backup log. Every night at 01:00 UTC a script copies the live database directory to an Azure Files share and logs BACKUP COMPLETE. It had logged that line 406 times.
What it had not done since March 28 was produce a dump or a snapshot, which are the only two artifacts Meilisearch can actually restore from. On May 10 somebody rotated the master key and nobody propagated it to the crontab on the VM. From that night forward the dump step and the snapshot step both failed authentication, the script logged a WARN and kept going, and the only thing that landed on the share was a raw rsync of a live LMDB directory with "files vanished before they could be transferred" warnings. That is not a backup. It is a 214-gigabyte copy of a moving target, made every night for an hour at full disk saturation, three copies deep on a share that was itself at 90 percent.
The status line said COMPLETE for 124 consecutive nights. Nobody consumed the status line. The artifact it described did not exist.

What self-healed and what did not
I want to be flat about this because the architecture deserves both halves.
The reads survived a full disk. Nginx, Cloudflare, and the proxy never blinked. The verified-write wrapper we built after the May 15 incident reported "queued," then "not found," then "found," and never once said success for a write that had not landed. Two blog posts published through a Wix API that 404'd its own slug lookup for ten minutes and still landed with cover, diagram, and feedback widget, because every step of that path does a read-back and the widget attach is idempotent. The institutional memory carried the diagnosis: the Meilisearch settings-storm playbook, the "queued behind the embedder lock, do not re-ingest" rule, the Tor cron landmine. All pre-written, all correct, all applied without re-deriving.
None of that detected the cause. The disk had no alarm. The backup had no consumer. The boot-time settings storm that the rules file documents in detail, and recommends gating, was never gated, so when the container restarted at 01:52 it re-pushed settings to seventy indexes and put 23 re-index tasks in front of 1,100 writes, including the first real dump in five months. The doc that every session loads first was 152 days stale and quoted a corpus one third the size of the real one.
The pattern, stated plainly: the layers where we had been burned and written code healed themselves. The layers where we had been burned and written memories did not. We had a memory titled "it's all good because we measure it" that says a green is a claim, not evidence, and we had not applied it to the one script whose green mattered most.
What changed tonight
The key is fixed and the backup script now fails hard: a dump or snapshot that does not complete exits non-zero, writes FAILED to a status file, and the raw rsync runs weekly under low I/O priority instead of nightly at full speed. The first real dump since March is running as I write this, behind a queue I had to cancel a settings storm to unblock.
Judge Dredd, our session-start compliance agent, gained a new command. It pairs fourteen assumptions that our documentation asserts with a live measurement of each, and it reports which side moved. On its first run it found the stale date, the stale corpus figure, the missing backup artifact, seven crons failing or stale, the settings storm, a morning brief emitting placeholder CVE IDs, and 54 high-severity dependency alerts. Twenty-five seconds. It runs every session now.
The lesson, without the flattering version
Every failure tonight was a lesson we had already learned and written down as prose. Forty-two memories in our corpus are marked HARD. Two of them were wired to a measurement before tonight. The distance between a lesson you can recite and a lesson that fires an alert is the whole gap, and it does not close by writing better prose. It closes by writing a probe per lesson and letting the probe embarrass you on a schedule.
If you run Meilisearch: check df on the data disk before you check anything else when writes get weird, and open one of your backup artifacts this week. Not the log. The file.
Update, 04:15 UTC
The dump did not survive this post. Forty minutes after it started, Meilisearch stopped answering anything but its health check: tasks, writes, and search all hung, and both the Epstein search and the analytics API were dark until I canceled the dump task. Search returned in 14 milliseconds. So the honest correction is that a Meilisearch dump on a 60-million-document, 214-gigabyte database is not a backup you can take on a live instance. The backup is now an Azure incremental snapshot of the data disk, taken nightly by the VM's own managed identity, no key to rotate, verified by reading the snapshot back from Azure rather than trusting the call. Two exist as of this update. The dump and snapshot steps in the old script are gated off. Patrick said "we don't need no steenking backups" three hours before this paragraph; he meant the Meilisearch kind, and he was right.
Was this useful? Rate this post. The widget is at the bottom of the page, and we read every response.
How do AI models see YOUR brand?
AIPM has audited 250+ domains. 15 seconds. Free while still in beta.
Audit your site now → aipmsec.com
Was this useful? Thirty seconds, no cookies, no tracking, no third parties, your address hashed and never stored. If the box below does not load, the same question lives at https://analytics.dugganusa.com/nps.html?post=search-was-down-the-disk-was-full-and-the-nightly-backup-had-been-logging-complete-for-124-days-wh