LinuxVIII · Logging and journaldJournal sizing
Persistent journals and disk pressure
What you'll learn
- Make the journal persistent so logs survive a reboot
- Tune journald SystemMaxUse, SystemKeepFree, and SystemMaxFileSize
- Identify the disk-pressure failure modes of the journal
- Reclaim journal disk space during an incident with journalctl --vacuum-*
- Forward the journal to a SIEM for long-term retention
- Investigate a "disk full" incident that the journal caused
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
The journal is bounded, but the size limit is configurable. A host with verbose services and a small journal limit rotates aggressively; a host with a large journal and no SIEM forwarding keeps everything until the disk fills. The discipline is to size the journal based on the workload and forward to long-term storage before the journal cycles.
First: is the journal persistent at all?
Sizing an ephemeral journal is wasted effort. journald decides
where to write from the Storage= directive in
journald.conf:
Storage= | Where the journal lives | Survives reboot |
|---|---|---|
volatile | /run/log/journal/ (tmpfs) | No |
auto (default on many distros) | /var/log/journal/ only if that directory already exists, otherwise /run | Only if the directory exists |
persistent | /var/log/journal/, created by journald if missing | Yes |
none | Nowhere; forwarding only | No |
The trap is auto. It does not create /var/log/journal/. On
a minimal image where nobody ever created it, journald quietly
runs in memory, and every log line is destroyed at reboot.
$ journalctl --header | grep -i storage; ls -d /var/log/journal 2>&1; journalctl --list-boots | tail -3Storage: persistent
/var/log/journal
-2 8f3c... Sat 2026-08-08 09:14:02 UTC—Sat 2026-08-08 21:02:55 UTC
-1 21ab... Sat 2026-08-08 21:03:31 UTC—Sun 2026-08-09 06:41:10 UTC
0 5d90... Sun 2026-08-09 06:41:44 UTC—Sun 2026-08-09 11:22:07 UTCIllustrative output
Make it persistent with a drop-in, not by editing the shipped
journald.conf (a package update can replace that file):
# /etc/systemd/journald.conf.d/10-persistent.conf
[Journal]
Storage=persistent
sudo mkdir -p /etc/systemd/journald.conf.d
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
journalctl --flush # move /run entries into /var
journalctl --header | grep -i storage # must report: persistent
journalctl --flush asks journald to migrate whatever it
already has in /run/log/journal/ into the new persistent
directory, so the boot in progress is not lost at the moment
you make the switch.
The journal limits
None of these defaults is a fixed number. They are computed from the size of the filesystem the journal lives on, then capped:
| Directive | Default | Meaning |
|---|---|---|
SystemMaxUse= | 10% of /var’s filesystem, capped at 4 G | Maximum disk space the journal uses |
SystemKeepFree= | 15% of /var’s filesystem, capped at 4 G | Free space journald leaves for everything else |
SystemMaxFileSize= | 1/8 of SystemMaxUse, capped at 128 M | Maximum size of one journal file |
SystemMaxFiles= | 100 | Maximum number of journal files |
RuntimeMaxUse= | 10% of /run, capped at 4 G | Same, for the volatile journal |
RuntimeKeepFree= | 15% of /run | Same, for the volatile journal |
man 5 journald.conf: the MaxUse pair “defaults to 10% and the
[KeepFree pair] to 15% of the size of the respective file system,
but each of the calculated default values is capped to 4G”, and
MaxFileSize “defaults to one eighth of the values configured with
SystemMaxUse= and RuntimeMaxUse= capped to 128M, so that usually
seven rotated journal files are kept as history”.
The percentage matters more than the cap on the hosts where this
bites. “4 GB” is only the default on a filesystem of 40 GB or
more; on a 10 GB /var the journal caps itself at 1 GB, and a
runbook that assumes 4 GB will size retention wrong by a factor
of four. Read the effective values rather than the table:
# What journald computed, in its own words. This is the authoritative line.
journalctl -u systemd-journald --no-pager | grep -i 'System Journal' | tail -1
# System Journal (/var/log/journal/<id>) is 215.3M, max 4G, 3.7G free.
# What the journal occupies right now
journalctl --disk-usage
# The merged configuration, across every drop-in
systemd-analyze cat-config systemd/journald.conf | grep -vE '^\s*(#|$)'
SystemMaxUse is the hard cap on what the journal occupies.
SystemKeepFree is the floor under free space on the filesystem.
Whichever binds first triggers rotation — which is why a journal
well under SystemMaxUse still rotates on a nearly full disk.
$ journalctl -u systemd-journald --no-pager | grep -i 'System Journal' | tail -1; journalctl --disk-usageAug 11 06:01:20 host systemd-journald[1670868]: System Journal (/var/log/journal/c850e0f0) is 1.2G, max 4G, 15.9G free.
Archived and active journals take up 1.2G in the file system.Illustrative output
Sizing the journal
For production, the right answer depends on the workload:
| Workload | Recommended SystemMaxUse |
|---|---|
| Minimal services, fast SIEM forwarding | 1-2 GB |
| Moderate services, daily SIEM batch | 2-4 GB |
| Verbose services, no SIEM forwarding | 8-16 GB or more |
| Hosts with no SIEM | larger still, OR archive to long-term storage |
The journal cycle rate (when entries are deleted) is roughly
SystemMaxUse divided by daily log volume. On a host that
produces 1 GB of logs per day with SystemMaxUse=4 GB, the
journal cycles every four days.
Tuning
Journal sizing is configured in journald.conf, so the
drop-in goes in /etc/systemd/journald.conf.d/:
# /etc/systemd/journald.conf.d/90-sizing.conf
[Journal]
SystemMaxUse=8G
SystemKeepFree=20%
SystemMaxFileSize=256M
$ sudo mkdir -p /etc/systemd/journald.conf.d && sudoedit /etc/systemd/journald.conf.d/90-sizing.conf && sudo systemctl restart systemd-journald && journalctl --header | headStorage: persistent
Current journal size: 7.9G (max 8.0G, leaving 20% free)
Maximum journal size: 8.0G
Journal files: 32
...Illustrative output
systemctl daemon-reload is not needed here — nothing about
the unit changed. systemctl restart systemd-journald is what
makes journald re-read its configuration.
Investigating journal disk pressure
$ df -h /var/log/journal/; du -sh /var/log/journal/; journalctl --header | grep -E 'Max|size' ; journalctl --verify 2>&1 | headFilesystem Size Used Avail Use% Mounted on
/dev/sdc1 20G 18G 2.0G 90%
12G /var/log/journal
Storage: persistent
Current journal size: 7.8G (max 8.0G, leaving 20% free)
...Illustrative output
Reclaiming the space now
Lowering SystemMaxUse= does not shrink a journal that is
already oversized. journald applies the new limit going
forward; it does not go back and delete what it has already
written. man 5 journald.conf is explicit about the analogous
case: once the filesystem fills, journald stops using more
space, “but it will not be removing existing files to reduce
the footprint again, either”.
The command that actually frees space is journalctl --vacuum-*:
# Look first
journalctl --disk-usage
# Archive the active journal file so it becomes eligible
sudo journalctl --rotate
# Then pick a bound. Any one of these, or several.
sudo journalctl --vacuum-size=2G # keep at most 2 GB
sudo journalctl --vacuum-time=7d # discard entries older than 7 days
sudo journalctl --vacuum-files=10 # keep at most 10 journal files
# Confirm
journalctl --disk-usage
df -h /var/log/journal/
Vacuuming buys time; it does not fix the cause. Follow it with
the real decision: cap the journal properly with a
journald.conf.d drop-in, quieten whatever unit is generating
the volume, or grow /var.
Long-term retention with SIEM forwarding
The journal is not a long-term store. For retention beyond the journal cycle, forward to a SIEM:
# /etc/rsyslog.d/40-journal-to-siem.conf
module(load="imjournal")
global(
DefaultNetstreamDriver="gtls"
DefaultNetstreamDriverCAFile="/etc/rsyslog.d/tls/ca.pem"
DefaultNetstreamDriverCertFile="/etc/rsyslog.d/tls/client-cert.pem"
DefaultNetstreamDriverKeyFile="/etc/rsyslog.d/tls/client-key.pem"
)
action(type="omfwd"
target="siem.example.com"
port="6514"
protocol="tcp"
StreamDriver="gtls"
StreamDriverMode="1"
StreamDriverAuthMode="x509/name"
StreamDriverPermittedPeers="siem.example.com"
queue.type="LinkedList"
queue.filename="siem_queue"
queue.maxdiskspace="10g"
action.resumeRetryCount="-1")
This forwards every journal entry to the SIEM. The journal keeps the recent days; the SIEM keeps everything.
Knowledge check
Knowledge check · 6 questions
Q1. What happens when the journal reaches SystemMaxUse?
Q2. With `SystemKeepFree=15%`, the journal can end up occupying far more than 15% of the filesystem.
Q3. Which of the following are correct journal-sizing practices? Select all that apply.
Q4. You put SystemMaxUse=8G into /etc/systemd/system/systemd-journald.service.d/override.conf under a [Journal] header, restart journald, and it starts cleanly. What is the effect on the journal size limit?
Q5. /var is at 100%, the journal is 12 GB, and journald has stopped writing. You lower SystemMaxUse to 2G in a journald.conf.d drop-in and restart journald. df still shows 100%. Why, and what frees the space?
Q6. On a host with Storage=auto and no /var/log/journal directory, `journalctl -b -1` will show the previous boot after a crash-reboot.
Passing score: 75%. Answers are checked in this browser.