How to Configure logrotate on Linux (Daily & Size-Based, With Examples)

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 with daily, 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 use create 0644 root root.

  • If your service runs as a non-root user (e.g., myapp:myapp), set create and optionally su accordingly so the app can keep writing after rotation.


Minimal, safe logrotate config (generic example)

Create /etc/logrotate.d/myapp:

/var/log/myapp/app.log {
      daily # rotate once per day
      size 200K                                                             # or when file exceeds ~200 KB
     rotate 7                                                              # keep 7 archives
     compress                                                              # gzip older logs
      delaycompress                                                         # compress starting from 2nd rotation
     missingok                                                             # OK if the file does not exist
     notifempty                                                            # skip rotation if file is empty
# Use exactly one of the following blocks:
# Option A) App can reopen logs (best practice):
create 0644 myapp myapp                                                     # create a fresh file with correct perms
# Option B) App cannot reopen, keep same inode:
# copytruncate                                                              # copy to archive, then truncate the original
# create 0644 myapp myapp                                                  # still ensure correct owner/perms after truncate
}

Which option should you choose?

  • Option A (create) is best if your app reopens logs (e.g., on SIGHUP 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:

/tmp/yourapp.log {
su myapp myapp # tell logrotate which user/group to use
daily
size 200K
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate # often needed for apps that won’t reopen logs
create 0644 myapp myapp
}

Best practice: avoid /tmp for persistent logs. Prefer /var/log/yourapp/yourapp.log.


Testing your configuration (safe & fast)

  1. Dry-run (no changes):

    sudo logrotate -d /etc/logrotate.d/myapp
  2. Force a rotation now (for verification):

    sudo logrotate -f /etc/logrotate.d/myapp
  3. 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:

sudo systemctl reload myapp.service
# or, if it uses SIGHUP:
sudo kill -HUP $(pidof myapp)

Common directives you’ll actually use

  • daily|weekly|monthly — cadence for scheduled rotation.

  • size 200K — rotate when file exceeds this size (combine with daily 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 and dateformat — use dates in archive names:

    dateext
    dateformat -%Y%m%d-%s

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:

sudo sed -i 's/\r$//' /etc/logrotate.d/myapp
# Also remove inline comments from directive lines and use "200K" (uppercase K).

“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:

/var/log/myapp/app.log {
size 5M
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 myapp myapp
# copytruncate # uncomment if your app can’t reopen logs
}

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:

/var/log/myapp/app.log {
daily
rotate 7
compress
missingok
notifempty
create 0644 myapp myapp

     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) or copytruncate (fallback).

  • Set sane retention: rotate 7 + compress.

  • Add size and/or daily depending on growth profile.

  • If under world-writable dirs (e.g., /tmp), add su 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.

Leave A Comment

All fields marked with an asterisk (*) are required

plugins premium WordPress