Reported symptoms
At 02:40 an application team reports that their service will not restart.
The error is No space left on device. Nobody has deployed anything.
The monitoring dashboard is green, because the last successful check
landed before the filesystem filled and nothing has scraped since.
You open journalctl to find out what started this and the newest entry
is from 21:15 the previous evening.
Evidence provided
$ df -h /var; journalctl --disk-usage; journalctl -n 1 --no-pagerFilesystem Size Used Avail Use% Mounted on
/dev/mapper/vg0-var 40G 40G 0 100% /var
Archived and active journals take up 3.9G in the file system.
Aug 09 21:15:03 web01 kernel: EXT4-fs (dm-2): error count since last fsck: 0$ du -xh --max-depth=1 /var 2>/dev/null | sort -h | tail -51.2G /var/lib
3.9G /var/log/journal
6.1G /var/cache
28G /var/lib/containers
40G /var$ systemctl status systemd-journald --no-pager | tail -4; grep -rns 'SystemMaxUse\|SystemKeepFree' /etc/systemd/journald.conf /etc/systemd/journald.conf.d/ 2>/dev/nullAug 09 21:15:04 web01 systemd-journald[412]: Failed to write entry (23 items, 712 bytes), ignoring: No space left on device
Aug 09 21:15:04 web01 systemd-journald[412]: Failed to write entry (19 items, 604 bytes), ignoring: No space left on deviceRoot cause
Two facts combine.
First, the journal was made persistent - /var/log/journal exists - and
no size cap was ever configured. journald’s built-in default is
SystemMaxUse=10% of the filesystem, capped at 4 GiB, with
SystemKeepFree=15% reserved. On this 40 GiB /var that resolves to
about 3.9 GiB, which matches journalctl --disk-usage exactly. journald
is behaving correctly and is inside its budget.
Second, /var/lib/containers grew to 28 GiB. The journal did not fill
the disk. It is a bystander - but it is the bystander that stops your
ability to investigate, because journald cannot write to a full
filesystem and drops entries instead.
That distinction drives the fix. Reclaiming journal space buys you room
to work. It does not address why /var filled, and if you stop there
the host fills again.
Resolution
- Buy working room first, non-destructively.
sudo journalctl --vacuum-size=500Masks journald to drop archived journal files until the store is under 500 MiB. It never touches the active file, so no in-flight log line is lost. Confirm withdf -h /var. - Verify what survived.
journalctl --verifyreports PASS or FAIL per file. A FAIL here usually means someone already usedrm; note it, because those entries are gone and cannot be part of your timeline. - Find the real consumer.
du -xh --max-depth=1 /var | sort -h | tailthen descend into the largest entry. In this scenario that is/var/lib/containers. Reclaim it with the owning tool -podman system prune,docker system prune,dnf clean all,apt-get clean- never with a blindrm -rfon a path you have not attributed. - Check for deleted-but-open files.
sudo lsof +L1 /var | headlists files with a link count of 0 that a process still holds. Restart the owning service to release them; for journald that issudo systemctl restart systemd-journald. - Cap the journal explicitly. Create
/etc/systemd/journald.conf.d/10-size.confwith a fixed size rather than a percentage, so the cap does not silently change when the filesystem is resized: - ``
ini [Journal] SystemMaxUse=2G SystemKeepFree=4G SystemMaxFileSize=256M MaxRetentionSec=30day`` - Apply and confirm.
sudo systemctl restart systemd-journaldthenjournalctl --disk-usageshould report a figure at or below 2 GiB.systemd-analyze cat-config systemd/journald.confshows the merged effective configuration including your drop-in. - Prove ingestion has resumed.
logger -t breakfix "journal recovery test"thenjournalctl -t breakfix -n 1. If the line is not there within a second, journald is still not writing and the incident is not over.
Verification
- Free space is restored and stable.
df -h /varshows headroom, and it is still there thirty minutes later - a filesystem that refills immediately means you reclaimed the bystander and not the cause. - The journal is inside its cap.
journalctl --disk-usagereports below the configuredSystemMaxUse. - The journal is intact.
journalctl --verifyreturns PASS for every file. - New entries are arriving.
journalctl -fshows live traffic, and the test message written withloggeris queryable. - The gap is documented. Record the window between the last pre-incident entry and the first post-recovery entry. Anyone reading this timeline later needs to know those logs do not exist rather than concluding nothing happened.
Prevention
- Set
SystemMaxUseandSystemKeepFreeexplicitly on every host. The percentage defaults are computed from the filesystem, so the same config yields a different cap on every machine, and changes silently when a volume is grown. - Give
/var/logits own filesystem or LVM volume. Then a log flood fills a volume that only holds logs, and/var/libstays writable. - Alert on
/varand/var/logabove 80%, before the failure. - Alert separately on the journal having stopped ingesting. Disk-usage alerting catches the cause; ingestion alerting catches every other reason your evidence stops arriving.
- Forward logs off the host. Central logging is what makes this incident recoverable rather than merely survivable - the entries you cannot read locally are already at the collector.