systemd-tmpfiles Cleanup: Why an Old Spool File Can Survive Its Age Limit
A spool file whose modification time is several days old can legitimately survive a systemd-tmpfiles age cleanup. In systemd v257, the selected timestamp set can include more than modification time, and other conditions can exclude an entry from the aging pass.
Start by identifying the applicable rule and the cleanup invocation. This article uses the tagged v257 tmpfiles.d manual and disposable, hypothetical spool metadata. No cleanup command was executed and no file was deleted.
Read the age policy, not just the oldest timestamp
An age field defines eligibility for age-based cleanup when the relevant cleanup operation runs. It is not a promise that a file disappears at an exact wall-clock deadline, and it is not a storage quota. An absent age or - disables age-based cleanup for that entry.
The v257 manual’s explicit age-by definition defaults to abcmABM. Lowercase letters apply to files: access, birth, status-change and modification times. The corresponding uppercase letters apply to directories, with directory status-change time omitted from the default. Availability of a particular timestamp depends on the filesystem and interface; do not invent a birth time if none is reported.
The manual also retains general prose describing three timestamps. For this version-specific review, use the explicit age-by definition when identifying the default selected set. Record the deployed systemd version before transferring the conclusion to another image.
Work a metadata counterexample
Consider an age of one day and a cleanup observation at Friday noon. A synthetic regular file has these timestamps:
birth time: Monday 09:00
modification time: Monday 10:00
status-change time: Friday 11:00
access time: Monday 10:00
Its modification time looks old, but the selected status-change time is only one hour old. Under the stated default timestamp policy, that recent value prevents age cleanup. This does not require new payload data to have been written at Friday 11:00; status changes and content modification are different metadata events.
Now create a second conceptual case in which every available selected timestamp is older than the threshold. That removes the timestamp objection, but it still does not prove deletion occurred. The rule must match, a cleanup pass must run, and exclusions must be considered.

Treat age-by changes as retention-policy changes
An explicit prefix such as mM:1d selects modification time for files and directories instead of using the default set. That changes which activity can preserve an entry. It is a policy example, not a recommendation to apply it to an active acquisition spool.
Suppose support reads a completed diagnostic file while an application still expects it to remain available. A policy using only modification time will not treat a recent access timestamp as protective. Conversely, whether a read actually updates access time depends on filesystem and mount behavior; do not assume every inspection resets the age.
Choose timestamps from the intended lifetime contract. “Old content,” “unused recently,” and “safe to discard” are different properties. None of those metadata interpretations alone proves that a downstream consumer has acknowledged a record.
Distinguish a BSD lock from an open descriptor
The documented aging algorithm attempts an exclusive BSD file lock on an entry it is considering removing. A conflicting shared or exclusive BSD lock causes that entry and its subtree to be skipped. An application can deliberately coordinate with that mechanism.
Merely holding an ordinary file descriptor open is not the same as holding the documented lock. A diagnosis that says “it survived because the logger had it open” needs evidence of the actual locking or another exclusion, rather than a process listing alone.
Likewise, a lock is a temporary coordination mechanism, not permanent retention. Review acquisition order and the application’s release path before depending on it. The synthetic example does not establish that an existing logger uses BSD locks or that another locking API has equivalent cleanup behavior.
Collect a read-only explanation first
For one unexpected survivor, preserve the matched rule, age field, explicit or default age-by, relevant timestamp values, filesystem timestamp availability and evidence that the expected cleanup pass ran. Then investigate exclusions and the application’s locking contract.
Keep this investigation separate from journald rotation or application-owned acknowledgement retention. A tmpfiles rule should not be used to bypass the logger’s own definition of a completed record.
A later disposable test can vary one condition at a time: recent modification, recent status change, recent access when supported, and a conflicting BSD lock. It should observe both survivors and eligible entries without touching customer data. The result to seek is an explainable eligibility decision, not a hard deadline inferred from modification time alone.