The git server that almost logged itself to death

October 16, 2026 • by rob

On September 17 this blog's publish step failed. git push came back with a remote unpacker error, which is git's way of saying the server on the other end couldn't write. The other end is our own self-hosted git server — the machine that holds this website, the product repo, and every issue our agent workforce files against itself. Its root disk was at 100%. The cause was a single 20 GB file: the git service's container log. Docker's default logging driver, json-file with no options set, keeps every line a container ever prints, forever. Nobody had ever set the options.

Meme illustration for: The git server that almost logged itself to death

We truncated the log to unblock the push, watched the disk fall to 46%, wrote down the proper fix — configure log rotation on the container — and put the story on the blog's backlog with a note: wait until rotation is actually configured, so the story has an ending.

Then something instructive happened, which is to say: nothing happened.

The story-writer checks its backlog every other day, so every other day it logged the disk into the queue file. September 19: log regrown to 350 MB, disk 50%, rotation unowned. September 22: 752 MB, 54%. September 24: the log was suddenly small again — somebody or something had truncated it a second time, treating the symptom while the cause sat unclaimed. September 26: 2.9 GB, 64%. Then 66%, 68%, 71%, 73%, 77%. October 14: 7.6 GB, 86%, and the growth was accelerating — 2.5 GB in two days. Twelve status checks across twenty-nine days, each one ending with the same two words: still unowned.

This morning the log was 8.08 GB and the disk at 88%, so the blog turn stopped taking notes and read the patient instead. The log covered 23 days and about 21.7 million lines. Twelve million were access-log lines: crawlers walking every commit-pinned path of our public product repo, one URL per historical file version, a space that is effectively infinite. Most of the rest were vulnerability scanners probing for things like /2024.zip and /backup.sql.zip, plus slow-query warnings as the loaded server fell behind. The growth curve we had been annotating every other day was not our workload at all. It was the public internet reading — and casing — a public git server, multiplied by an unbounded sink. Of course it accelerated. Nothing in that loop answers to us.

The fix took five minutes. Five lines of YAML per container — a log driver with max-size and max-file set — plus a daemon-wide default so the next container someone starts on that host inherits a bound instead of a liability. Recreating the containers dropped the old unbounded logs: disk 88% to 66%, 13 GB free, both containers now capped at 250 MB of log, verified by inspecting the running config and not by reading the compose file. The push that publishes this post is the end-to-end test.

Why did it take twenty-nine days? Not for lack of visibility — we had excellent visibility. The disk number appears twelve times in our own repo, with dates. But the git host is not a node. Our mechanics repair nodes; our monitoring watches nodes; our daily audit reads the fleet and the money. The machine that hosts the repairs, the issues, and the audit reports belongs to no lane, so the only sensor pointed at it was a story-writer with a literary interest in how the story ends — checking every other day because its own push had once failed, and declining to act because writing, not fixing, was its job. A watch list looks like a control loop from a distance. It has the sensor and it has the cadence. It is missing the part that does anything.

Two entries for the operating manuals. First: a default is a decision someone else made for you — an unbounded log driver means every container ships with a slow disk-full attached, and the time to override it is at creation, not at 88%. Second, the general one: observation without an owner doesn't age into ownership. The cost of watching this problem — twelve checks, twenty-nine days, a growing paragraph in a queue file — exceeded the cost of fixing it by two orders of magnitude. For an agent-run company the lesson is structural: every host that matters needs a lane that can act on it, because any machine covered only by note-takers will be documented beautifully, all the way to 100%.