Logrotate: Managing Log Files Before They Fill Your Disk

Log files have a way of growing quietly until they fill up your disk at the worst possible moment. I’ve seen it happen more than once — a busy web server accumulating logs, nobody checking, and then one night the disk hits 100% and everything stops. Logrotate prevents this.

It’s already installed on most Linux systems and handles log rotation for many system services automatically. But understanding how it works lets you configure it for your own application logs.

How Logrotate Works

Logrotate runs periodically (usually daily via cron or systemd timer) and processes rotation jobs defined in config files. For each job it:

  1. Renames the current log file (adds a number or date suffix)
  2. Creates a new empty log file (optional)
  3. Signals the application to reopen its log file
  4. Compresses old logs (optional)
  5. Deletes logs older than the retention period

The “signal to reopen” step is critical. Most applications open a log file once and keep writing to the same file descriptor. If logrotate just renames the file, the app keeps writing to the renamed file. The app needs to be told to close its current file handle and open the new one.

Default Configuration

The main config is at /etc/logrotate.conf. Individual service configs live in /etc/logrotate.d/. Check what’s already configured:

ls /etc/logrotate.d/

You’ll see entries for nginx, apache2, rsyslog, and others. These are typically installed by the package manager.

Configuration Directives

A typical logrotate config:

/var/log/myapp/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0644 www-data www-data
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}

Let me explain each directive:

daily — rotate daily. Other options: weekly, monthly, size 100M (rotate when file exceeds size).

missingok — don’t error if the log file doesn’t exist.

rotate 14 — keep 14 old rotated log files. Combined with daily, that’s 14 days of history.

compress — compress rotated files with gzip.

delaycompress — don’t compress the most recently rotated file. Why? Some applications might still be writing to it. Delay compression by one rotation cycle.

notifempty — don’t rotate if the log file is empty.

create 0644 www-data www-data — create a new empty log file after rotation, with these permissions and owner.

postrotate / endscript — commands to run after rotation. This is where you tell the application to reopen its log files.

Nginx Log Rotation

A good Nginx rotation config:

/var/log/nginx/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
            run-parts /etc/logrotate.d/httpd-prerotate; \
        fi \
    endscript
    postrotate
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

sharedscripts — run the postrotate script once, even when rotating multiple files. Without this, postrotate runs for each matching file.

invoke-rc.d nginx rotate sends USR1 signal to Nginx, telling it to reopen log files.

Application Log Rotation

For a custom application:

/var/log/myapp/app.log {
    daily
    missingok
    rotate 7
    compress
    delaycompress
    notifempty
    create 0644 deploy deploy
    postrotate
        /usr/bin/kill -USR1 $(cat /var/run/myapp.pid) 2>/dev/null || true
    endscript
}

If your application doesn’t support USR1 for log reopening, you might need to restart it instead:

postrotate
    systemctl restart myapp
endscript

Restart is more disruptive but works when the app doesn’t implement signal-based log reopening. Some modern apps (especially those that log to stdout, captured by systemd) don’t need a postrotate script at all.

Size-Based Rotation

Rotate when a file gets too large, regardless of time:

/var/log/bigapp/*.log {
    size 500M
    missingok
    rotate 5
    compress
    notifempty
    postrotate
        systemctl reload bigapp
    endscript
}

size 500M — rotate when the file exceeds 500MB. Can use K, M, or G suffixes.

Combine size and time with maxsize:

daily
maxsize 100M

This rotates daily OR when the file exceeds 100MB, whichever comes first.

Date-Based Naming

By default, logrotate appends numbers: app.log.1, app.log.2.gz, etc. For date-based names:

/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    dateext
    dateformat -%Y-%m-%d
}

This creates files like app.log-2024-01-15.gz. Easier to find specific dates.

Testing Configuration

Test a config without actually rotating:

logrotate --debug /etc/logrotate.d/myapp

Debug mode shows what logrotate would do without making changes. Always test before deploying a new config.

Force rotation immediately (ignoring time/size conditions):

logrotate --force /etc/logrotate.d/myapp

Useful to test that the postrotate script works correctly.

The State File

Logrotate tracks when each log was last rotated in /var/lib/logrotate/status:

cat /var/lib/logrotate/status

Each line shows the log file path and when it was last rotated. If logrotate seems to be not rotating on schedule, check this file.

Common Issues

Logs not rotating — check the state file (maybe it was rotated recently), check that the log file matches the glob pattern in the config, verify the config syntax with logrotate --debug.

Application still writing to old file — the postrotate script might not be working. Check that it’s actually sending the right signal or reloading the service. Test manually.

Ownership issues after rotation — the create directive sets permissions for new files. If your application runs as a specific user, match it in create.

Compression not working — make sure gzip is installed. Check that delaycompress isn’t preventing compression when you expect it.

Verifying Disk Usage

After setting up rotation, check that it’s working over time:

du -sh /var/log/*
ls -lh /var/log/nginx/

Healthy log directories should stay roughly constant in size after rotation is established. If they’re still growing, either rotation isn’t running or the retention is too long.

Set up monitoring for disk usage — I use Netdata or just a simple cron-triggered email when a disk exceeds a threshold. Logrotate prevents the common case of gradual log accumulation, but it won’t save you from an application generating 10GB of logs per day.

Scroll to Top