journald disk caps: SystemMaxUse vs MaxRetentionSec vs SystemKeepFree — picking one and ignoring the others

journald has three different ways to cap disk usage. They overlap. They interact. The man page lists all of them with equal weight, which makes it tempting to set all three at once and assume the strictest one wins. That’s not exactly what happens — and the differences bite when a sudden burst of log volume fills your disk despite “having limits set.”

Here’s how the three actually behave, and which one I now set on every server (ignoring the other two).

The three caps

  • SystemMaxUse — absolute upper bound on the persistent journal size. Default is 10% of the filesystem holding /var/log/journal, capped at 4 GB. Once journald hits this, it deletes oldest entries to make room.
  • SystemKeepFree — minimum free space to maintain on the filesystem. Default is 15% of the filesystem. journald evicts oldest entries when free space drops below this, regardless of how big the journal currently is.
  • MaxRetentionSec — maximum age of journal entries. Default is 0 (no age limit). When set, journald drops entries older than the threshold during its periodic cleanup.

journald applies them simultaneously: any limit being violated triggers eviction. So you can have all three set, with the most-restrictive one winning at any given moment. That’s the theory.

The practical problem

The interaction surprises people in two ways:

  • Setting SystemMaxUse=2G alone doesn’t help if your disk is at 95% capacity from non-journal data — SystemKeepFree default kicks in and journald aggressively trims toward keeping 15% free, which can be more than 2 GB on a small disk.
  • Setting SystemKeepFree=1G alone is dangerous on small VPS instances. If your filesystem is 20 GB and 14 GB is non-journal data, journald will use up to 5 GB (filling to leave 1 GB free), which is far more than you wanted to allocate to logs.

The result: people set all three “to be safe,” then have to debug which one fired when journal entries disappear.

Pick one: SystemMaxUse

For 95% of servers, the right answer is just SystemMaxUse, set explicitly, with SystemKeepFree turned off. The reasoning:

  • You know how much disk you want to allow for logs (200 MB on a small VPS, 1 GB on a meaty server). That’s a flat number.
  • SystemKeepFree couples log retention to the rest of your disk usage. When something else fills the disk, journald responds by deleting logs — exactly when you most want logs to debug what’s happening.
  • MaxRetentionSec is fine in addition, but it’s a “no older than” cap, not a size cap; it doesn’t actually solve the disk-fill problem.

Drop into /etc/systemd/journald.conf.d/disk-cap.conf:

[Journal]
Storage=persistent
SystemMaxUse=500M
SystemKeepFree=0
RuntimeMaxUse=200M

Walking through:

  • Storage=persistent — keep journals on disk across reboots. Required for the limits to mean anything (with auto, journald uses RAM until you create /var/log/journal).
  • SystemMaxUse=500M — your hard cap on persistent journal size.
  • SystemKeepFree=0 — disable the disk-coupling behavior. The 0 means “always honor SystemMaxUse, ignore filesystem free space.”
  • RuntimeMaxUse=200M — hard cap for the in-memory tmpfs journal (/run/log/journal) used pre-disk-mount and during early boot. Default is 10% of RAM, which is usually overkill.

Apply: sudo systemctl restart systemd-journald. Verify what journald thinks the effective limits are:

journalctl --disk-usage  # Current usage
journalctl --vacuum-size=500M  # One-shot trim to 500M
sudo systemd-analyze cat-config systemd/journald.conf  # Effective config

When MaxRetentionSec actually helps

One scenario: compliance or operational policies that say “logs must be deleted after N days.” Then MaxRetentionSec=2592000 (30 days) ensures any entry older than that is dropped on the next vacuum, regardless of how much disk it occupies.

Pair it with SystemMaxUse: the size cap kicks in if logs grow too fast within the retention window; the time cap drops anything older. Both useful when paranoia is policy. For a small server with no compliance requirements, MaxRetentionSec adds nothing.

The “logs are gone” debug recipe

If you went looking for a log entry from yesterday and it’s missing, before assuming a bug:

# What's the oldest entry currently in the journal?
journalctl --since "1 year ago" -n 1 -o short-iso

# How much disk is journald using right now?
journalctl --disk-usage

# Has the journal been vacuumed?
journalctl --header | grep -E 'Sequential|Tail'

If your oldest entry is recent and disk usage is at the cap, journald evicted it as designed. Bump SystemMaxUse. If it’s much older than expected and disk usage is below the cap, something else is wrong (possibly journald being killed by an OOM and losing entries during recovery).

The two-line recommendation

For 90% of servers I touch:

# /etc/systemd/journald.conf.d/disk-cap.conf
[Journal]
SystemMaxUse=500M
SystemKeepFree=0

Two lines. Predictable disk usage. No coupling to other things on the disk. No surprise eviction. Done.

Cover photo: sejio402 on Pexels.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.