Why logrotate matters
Uncontrolled logs grow fast, eat disk, and break apps. logrotate keeps logs tidy by rotating, compressing, and pruning them on a schedule (daily/weekly) or when they reach a size threshold.
Prerequisites
-
Linux server with
logrotate
installed (most distros include it). -
Root or sudo privileges to place configs in
/etc/logrotate.d/
. -
The path to your app’s log file (we’ll use
/var/log/myapp/app.log
as a generic example).
Where logrotate runs from (and when it rotates)
On most Linux distributions, logrotate is executed by cron or systemd timers:
-
Cron:
/etc/cron.daily/logrotate
(usually runs once per day during early morning). -
systemd timer:
logrotate.timer
(similar cadence).
When should you expect a rotation?
-
If you use
daily
, rotation happens the next time the daily logrotate job runs (typically once every 24h). -
If you use
size 200K
together withdaily
, rotation will trigger when either condition is met (size exceeded or the next daily run—whichever comes first). -
You can always force a rotation manually for testing (details below).
Tip: Rotation happens when logrotate runs, not “immediately” as the file crosses the size threshold.
Understand who writes the log (this is critical)
-
If your service runs as
root
and writes/var/log/myapp/app.log
, your logrotate stanza can usecreate 0644 root root
. -
If your service runs as a non-root user (e.g.,
myapp:myapp
), setcreate
and optionallysu
accordingly so the app can keep writing after rotation.
Minimal, safe logrotate config (generic example)
Create /etc/logrotate.d/myapp
:
Which option should you choose?
-
Option A (
create
) is best if your app reopens logs (e.g., onSIGHUP
or after rotation hooks). This avoids losing log messages during the tiny window when files are moved/created. -
Option B (
copytruncate
) is a pragmatic fallback when the app won’t reopen logs. It preserves the same inode so the app continues writing without noticing a rename. Trade-off: a very small chance of missing a few lines during copy/truncate.
Special case: rotating logs under world-writable directories (e.g., /tmp
)
If your app logs to a path like /tmp/yourapp.log
, logrotate will complain about insecure parent permissions. Add the su
directive:
Best practice: avoid
/tmp
for persistent logs. Prefer/var/log/yourapp/yourapp.log
.
Testing your configuration (safe & fast)
-
Dry-run (no changes):
-
Force a rotation now (for verification):
-
Check results:
-
Active file:
/var/log/myapp/app.log
-
Archives:
/var/log/myapp/app.log-20250919.gz
(date may vary) or numbered files like.1
,.2.gz
, depending on your settings.
-
If your app supports it, send a reload after rotation so it reopens the file:
Common directives you’ll actually use
-
daily|weekly|monthly
— cadence for scheduled rotation. -
size 200K
— rotate when file exceeds this size (combine withdaily
if you want whichever comes first). -
rotate 7
— keep N archives; older are removed. -
compress
/delaycompress
— compress older archives; delay avoids compressing the newest rotated file (helpful for tooling). -
missingok
— don’t error if the file is absent. -
notifempty
— skip empty files. -
create 0644 USER GROUP
— create a new log after rotation with these permissions/ownership. -
copytruncate
— copy current log to archive and truncate the original (keeps inode). -
dateext
anddateformat
— use dates in archive names:
Troubleshooting (fix these fast)
“unknown unit ‘t’” near size 200k
Cause: extra characters on the line (often Windows CRLF or comment glued to the number).
Fix:
“skipping … parent directory has insecure permissions”
Cause: the log’s parent directory is world-writable (e.g., /tmp
).
Fix: add su USER GROUP
inside the block, or move logs to /var/log/...
.
App stops logging after rotation
Cause: the app holds the old file handle and does not reopen logs.
Fix: use copytruncate
, or send SIGHUP
, or configure the app to reopen logs after rotation.
Permissions wrong after rotation
Cause: mismatched create
settings.
Fix: ensure create MODE USER GROUP
matches the user/group that writes the log.
Example: size-only rotation (no daily)
Rotate purely by size and keep 14 days of archives:
Expectation: The next time the system’s logrotate job runs, if the file is ≥5 MB, it rotates; otherwise it doesn’t. It’s evaluated on each run.
Example: add pre/post hooks
You can run commands before or after rotation:
prerotate
/usr/bin/logger “About to rotate myapp logs”
endscript
postrotate
/bin/systemctl reload myapp.service >/dev/null 2>&1 || true
endscript
}
Quick checklist
-
Confirm who writes the log (user/group).
-
Choose between
create
(preferred) orcopytruncate
(fallback). -
Set sane retention:
rotate 7
+compress
. -
Add
size
and/ordaily
depending on growth profile. -
If under world-writable dirs (e.g.,
/tmp
), addsu USER GROUP
. -
Test with
-d
(dry-run) and then-f
(force). -
If supported, reload your service after rotation.
Frequently asked questions
Q: Will logrotate cut my log exactly at 200 KB instantly?
A: No. logrotate acts when it runs (daily/systemd schedule). It checks conditions (e.g., size). If met, it rotates then.
Q: Do I need copytruncate
?
A: Only if your app can’t reopen logs after rotation. Prefer create
+ service reload for cleaner rotation.
Q: Can I keep only the last N lines in the active file?
A: That’s not logrotate’s model. You can approximate via a cron job using tail -n 100 file > tmp && mv
or integrate a prerotate
script.
Q: My logs live in /tmp
. Is that OK?
A: It works with su
, but /tmp
is often cleaned on reboot. Prefer /var/log/...
for persistence and hygiene.
Final thoughts
With a small, correct stanza in /etc/logrotate.d/
, you’ll prevent runaway log growth and keep your system stable. Start simple: daily + size + rotate + compress
and the right permissions for your app’s user. Then refine with hooks and naming options as your needs evolve.