The disk has plenty of space and the writes still fail
Can I prove which resource actually ran out?
The ticket
CUSTOMER TICKET: nightly export failing, but you say the disk is fine
Account: Ardent Logistics (enterprise) Impact: no exports delivered for three nights Started: since the tuning work last week
Our export has failed three nights running. The message in your job output says there is no space left, so we asked our own team to check and they say the volume is almost empty, under one percent used. We have looked twice.
Someone suggested we buy more storage, which we are happy to do, but we would rather not spend the money on something that is apparently already empty. Please tell us what is actually full.
Your job
- Prove which resource ran out. The error names the failure, not the cause.
- Do not resize anything until you can say what would fill up again.
- Restore the export with the smallest correct change, then confirm it can run again tomorrow rather than only once.
Working notes
The job runs in the worker service. You can run it on demand:
docker compose -f labs/linux/_stack/compose.yaml \
-f labs/linux/_stack/compose.override.yaml \
exec worker python3 /app/exporter.py
Its configuration is in labs/linux/_stack/compose.override.yaml. After
editing, run tse apply, then tse check.
- Track
- Linux and CLI
- Time
- about 30 minutes
- Difficulty
- Straightforward
- Tier
- Core
Start it
In a Codespace or a local clone:
tse start linux/01-disk-has-space-and-writes-still-failThat provisions the broken system and prints the ticket above. Investigate with ordinary tools, then run tse check.
Look at the evidence
Real output, captured by running these commands against the broken system and checked against it on every build. It shows you what the evidence looks like. It cannot fix anything, and it will not tell you what is wrong.
Type a command you would reach for, or help.
Enter runs it. Shift and Enter start a new line. The up and down arrows walk back through what you have typed.
Investigation scratchpad
Saved in this browser as you type. Nothing is uploaded. 0 of 7 filled in.
In their words, not yours. Include scope and urgency.
Before running anything: target layer, expected output, two likely causes.
The command or query, and why it is safe to run here.
Three separate lists. This is the step people skip.
One proof sentence, one safe next step, one alternate hypothesis.
Plain language. Impact first. No blame, no speculation.
One gap, one command to repeat tomorrow, one confidence score.
Hints
Each hint gives away a little more. Try to spend a few minutes on your own evidence first, because the recall is what makes it stick.
Hint 1 of 3
Take the customer at their word, because they are right. The volume really is almost empty, and the write really did fail saying there was no room.
People usually resolve that by deciding somebody misread something. Nobody misread anything. Both readings are correct at the same moment, and the reason is that they are about different things.
The question worth asking is not "who is wrong". It is:
Which resource ran out, and does the error actually say?
Read the failure again and notice how little it commits to. It tells you the write failed and it tells you the kernel's reason code. It does not tell you what the device ran out of, and that omission is the whole exercise.
Before you look at any setting, run the job yourself and get the exact error rather than the customer's summary of it. Then find out how many things a filesystem can run out of. It is more than one, and only one of them is the number everybody checks.
Hint 2 of 3
A filesystem allocates two things separately, and running out of either one produces the same error:
| Resource | What it holds | What reports it |
|---|---|---|
| Blocks | the contents of files | df -h |
| Inodes | the files themselves, one each | df -i |
The customer checked the first. Nobody has checked the second.
That is why the error is so unhelpful: the kernel returns ENOSPC for both,
and "No space left on device" is its wording for either. The message names the
failure, not the cause.
Run both against the spool directory and compare:
docker compose -f labs/linux/_stack/compose.yaml \
-f labs/linux/_stack/compose.override.yaml \
exec worker sh -c 'df -h /var/spool/exports; df -i /var/spool/exports'
One of those is going to say almost nothing is used. The other is going to say it is completely full.
Then ask the question that follows: what is creating that many files. Count them, and compare the count against what the job was asked to produce. The error message itself carries both numbers if you read past the first line.
Hint 3 of 3
Space is at one percent. Inodes are at one hundred:
Filesystem Size Used Avail Use% Mounted on
tmpfs 64.0M 0 64.0M 0% /var/spool/exports
Filesystem Inodes Used Avail Use% Mounted on
tmpfs 512 512 0 100% /var/spool/exports
Every file consumes one inode no matter how small it is, and these are tiny. The job ran out of files it was allowed to create long before it came close to running out of room to put them in.
The failure itself tells you the scale:
{"files_written": 511, "files_expected": 5000}
Five thousand files for five thousand rows. Compare that against the setting
in labs/linux/_stack/compose.override.yaml:
EXPORT_ROWS_PER_FILE: "1"
The tuning work last week set the number of rows in each file to one. The export is not too big. It is split into five thousand pieces.
Put it back to a sane chunk size and the same 5000 rows land in a handful of
files. Then tse apply and tse check.
Resist resizing anything. More inodes would let tonight's run finish and would leave the real defect in place, which is a job that creates one file per row forever.
Solution
Write your customer update before you read this. Comparing your wording against the model answer is worth more than reading it cold.
Reveal the solution
Solution: the disk has space and the writes still fail
What the evidence proved
| Command | What it proved | What it did not prove |
|---|---|---|
| Running the export | It fails with [Errno 28] No space left on device |
Nothing about which resource ran out |
df -h /var/spool/exports |
Blocks are at 0%, the customer was right | Nothing about files, only about their contents |
df -i /var/spool/exports |
Inodes are at 100%, 512 of 512 used | |
| The failure payload | files_written: 511, files_expected: 5000 |
|
EXPORT_ROWS_PER_FILE |
The job was told to put one row in each file |
The two df invocations are the whole diagnosis, and neither means anything
without the other. Space is fine. Files are exhausted. Both are true, and only
the second matches the symptom.
Worth stating plainly, because it is what makes this expensive in practice: the
error message is not being unhelpful by accident. The kernel returns ENOSPC
when a filesystem cannot allocate a block or an inode, so "No space left on
device" is the correct wording for two entirely different problems. The message
names the failure. It never names the cause.
Root cause
Last week's tuning work set EXPORT_ROWS_PER_FILE to 1.
The export writes one file per chunk, so five thousand rows became five thousand files. The spool directory has 512 inodes. The job got 511 files in and then could not create another one, with the volume still empty because each file holds twenty bytes.
The export is not too large. It is split into five thousand pieces.
Scoped fix
In labs/linux/_stack/compose.override.yaml:
services:
worker:
environment:
EXPORT_ROWS_PER_FILE: "1000"
Then:
tse apply
tse check
Five files, six inodes used out of 512, and room to run again tomorrow.
Not the fix: raising the inode budget, or buying the storage the customer offered to buy. Either would let tonight's run finish and leave a job that creates one file per row in place, so the next larger export fails the same way. The check asserts the directory is not left full for exactly this reason.
Customer update
Your export was failing because it ran out of file slots rather than out of space, which is why your team was right that the volume was nearly empty. A filesystem limits how many files it can hold separately from how much data it can hold, and the error message our job reported covers both cases without distinguishing them.
The cause was a setting changed during last week's tuning work, which told the export to write one file per row. Your five thousand rows became five thousand files, and the directory allows five hundred and twelve. We have put the chunk size back, so the same data now writes as five files. Tonight's export will run normally.
Please do not buy the additional storage. It would not have helped, and the setting was the whole problem. If you would like, I can send you the two commands that tell these apart, so your team can check both next time.
Engineering escalation, if you needed one
Impact: three consecutive nightly exports failed for Ardent Logistics, no data delivered, customer was about to purchase storage that would not have helped. Evidence:
[Errno 28] No space left on deviceafter 511 of 5000 files;df -hat 0% anddf -iat 100% on the same directory. Confirmed: the volume has space, the job runs, the failure is reproducible. Ruled out: disk capacity, permissions, a data volume change. Suspected cause:EXPORT_ROWS_PER_FILEwas set to 1 during tuning, making the job write one file per row. Request: the exporter reports ENOSPC without saying which resource was exhausted, which cost three nights and nearly cost the customer a storage purchase. Can it check free inodes before a run and say so in the failure.
That request is the durable part. The setting is fixed for this customer. The next customer to hit this gets the same unhelpful message unless the job learns to say which of the two things it ran out of.
Check your understanding
Three questions on what the evidence here proved, and what it pointedly did not. Wrong answers explain themselves, and so do right ones.
tse quiz
Check your understanding
Three questions on what the evidence proved and what it did not. Every answer explains itself, including the right one.
3 questions, none answered yet.
Why this one exists
A filesystem runs out of two things independently: space for content and inodes for the files themselves. The kernel reports both as ENOSPC, so the error tells you what failed and not what ran out. `df -h` and `df -i` answer different questions and only one of them matches this symptom.
In an interview
"No space left on device" with an empty disk is a classic, and the reason it is asked is that it splits candidates cleanly. Reaching for `df -h`, seeing space, and concluding the error is wrong is the common answer. Knowing that a filesystem can exhaust two separate resources and that the message names neither is the one that gets a follow-up question.
Commands introduced
df -hdf -ils | wc -ldocker compose exec
Evidence layers
- the error the kernel returned
- free space
- free inodes
- what the job was configured to write