Logrotate is a fundamental tool on any Linux system that automates log file management. Without proper rotation, logs can fill up disk space, degrade performance, and make troubleshooting difficult.

In this post, I’ll explain how to configure and use logrotate to keep your logs under control, with practical examples for NGINX, Docker, custom applications, and more.

⚠️ DISCLAIMER: Please note that this article is a manual translation of the Spanish version. The screenshots may have been originally taken in Spanish, and some words/names may not match completely. If you find any errors, please report them to the author. ⚠️

Requirements

  • Linux system. Throughout this post I’ll use Debian, if you use another distribution, adapt the commands to your package manager.
  • Access as root or via sudo/doas. In my case, I’ll do it as root (using su).
  • (Optional) Services generating logs like NGINX, Docker, Apache, etc.

Things to keep in mind

  1. Logrotate normally runs once a day. On modern systems with systemd, this is managed via a timer (logrotate.timer), although if your system doesn’t have systemd, it may use cron and be managed in the /etc/cron.daily/logrotate file. In fact, if we check the cron script content, we’ll see that if it detects systemd is present, it does nothing (exit 0):

    cat /etc/cron.daily/logrotate
    

    Logrotate Cron Script - Cron script content

    On the other hand, the systemd timer is located at /usr/lib/systemd/system/logrotate.timer and we can see its configuration with:

    cat /usr/lib/systemd/system/logrotate.timer
    
    [Unit]
    Description=Daily rotation of log files
    Documentation=man:logrotate(8) man:logrotate.conf(5)
    
    [Timer]
    OnCalendar=daily
    RandomizedDelaySec=1h
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    
  2. It’s essential to test configurations before applying them using debug mode (-d).

  3. Be careful with permissions of rotated files.

What is logrotate?

Logrotate is a system utility designed to simplify log file administration. It allows automatic rotation, compression, removal, and mailing of logs, and as if that weren’t enough, we can also execute custom commands and scripts before and after rotation.

How does it work?

  1. Runs recurrently via systemd timers (logrotate.timer) or cron jobs (/etc/cron.daily/logrotate).
  2. Reads configurations from /etc/logrotate.conf and /etc/logrotate.d/.
  3. Applies the rules defined for each file or set of files.
  4. Executes commands and scripts pre and post-rotation if configured.
  5. Records state in /var/lib/logrotate/status.

Advantages:

  • Space savings: Compresses old logs and removes very old ones (as we configure it)
  • Prevention: Prevents the disk from filling up due to uncontrolled logs
  • Automation: Everything happens without manual intervention

Installation

Logrotate comes preinstalled on most current Linux distributions, but we can verify its existence and version with:

su -

# May fail if not run as root
logrotate --version

You can also verify its location:

which logrotate

Expected output:

Logrotate Version - Installed version

If for some reason you don’t have it installed:

apt update

apt install logrotate

Verify that the timer or cron is active:

# On systems with systemd
systemctl status logrotate.timer

# Or check the cron script
ls -l /etc/cron.daily/logrotate

Logrotate - Installation status

File structure

Logrotate uses a hierarchical configuration structure:

Main files:

  • /etc/logrotate.conf - System-wide global configuration

  • /etc/logrotate.d/ - Directory with service-specific configurations

  • /var/lib/logrotate/status - State file that records when each log was last rotated:

    Logrotate Status File - State file

Directories:

/etc/
├── logrotate.conf          # Global configuration
├── logrotate.d/            # Per-service configurations
│   ├── alternatives
│   ├── apt
│   ├── dpkg
│   ├── nginx               # NGINX configuration
│   ├── rsyslog
│   └── ...
└── cron.daily/
    └── logrotate           # Script that runs logrotate daily when using cron instead of systemd timers

/var/lib/logrotate/
└── status                  # Rotation state

Order of precedence: Service configurations in /etc/logrotate.d/ take priority over global directives in /etc/logrotate.conf.

Configuration directives

Logrotate has a large number of directives to customize its behavior that can be consulted in the manual (man logrotate). Here I list the most common ones, but don’t forget to check the manual to take full advantage of its potential:

Directive Description
daily, weekly, monthly, yearly Rotation frequency
rotate N Number of rotated files to keep
compress Compress old logs
delaycompress Don’t compress the first rotated log, useful for easy consultation
size N Rotate when file reaches size (e.g., size 100M)
maxsize N Rotate if size exceeded, regardless of time
missingok Don’t generate error if log doesn’t exist
notifempty Don’t rotate if file is empty
create mode owner group Create new log with specific permissions
copytruncate First makes a copy of the log with a new name, then empties the original to prevent the process writing to the log from losing reference to the original file
dateext Add date instead of number to rotated log name
dateformat Date format (default: -%Y%m%d)
maxage N Delete logs older than N days
sharedscripts Execute pre/postrotate commands and scripts once instead of once per file
prerotate ... endscript Script to execute before rotating logs
postrotate ... endscript Script to execute after rotating logs

The recommended way to configure logrotate is through files in /etc/logrotate.d/. This makes it easier to manage.

Configuration examples

Suppose you have an application that generates logs in /var/log/myapp/, and you want to rotate them daily, keeping 7 days of compressed logs but without compressing yesterday’s log so you can consult it quickly.

You would start by creating a configuration file for logrotate to manage those logs:

nano -cl /etc/logrotate.d/myapp

Content:

/var/log/myapp/*.log {
    daily
    rotate 7
    compress
    delaycompress
    notifempty
    missingok
    create 0640 juanje admins
    sharedscripts
    prerotate
        # Commands before rotation
    endscript
    postrotate
        # Commands after rotation
    endscript
}
  • /var/log/myapp/*.log: Applies to all .log files in /var/log/myapp/ directory
  • daily: Rotate every day
  • rotate 7: Keep 7 log rotations
  • compress: Compress old logs
  • delaycompress: Don’t compress yesterday’s log (useful if someone needs to consult it)
  • notifempty: Don’t rotate empty files
  • missingok: Don’t fail if file doesn’t exist
  • create 0640 juanje admins: Create new file with 0640 permissions, owner juanje and group admins
  • sharedscripts: Execute scripts once per rotation instead of once per file
  • prerotate and postrotate: Spaces for custom commands or scripts before and after rotation

The rotations would generate files like:

/var/log/myapp/
├── myapp.log       # Current log
├── myapp.log.1     # Yesterday's log (uncompressed)
├── myapp.log.2.gz  # Log from 2 days ago (compressed)
├── ...
└── myapp.log.7.gz  # Log from 7 days ago (compressed)

It’s also possible to specify different configurations for different log files within the same configuration file:

/var/log/myapp/access.log
/var/log/myapp/error.log
/var/log/myapp/debug.log
{
    daily
    rotate 14
    compress
    delaycompress
    notifempty
    missingok
    create 0640 juanje admins
}

/var/log/myapp/transactions.log
{
    weekly
    rotate 4
    compress
    delaycompress
    notifempty
    missingok
    create 0640 juanje admins
}

A real example is the default configuration that NGINX has for rotating its logs, which can be found in /etc/logrotate.d/nginx:

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    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
}

In this case, before rotating the logs, prerotate will invoke run-parts, which will execute any script found in the /etc/logrotate.d/httpd-prerotate directory.

After rotation, postrotate will reload NGINX so it starts writing to the new log files.

Testing the configuration

We should always test our configurations before applying them. To do this, we can run logrotate with the -d (debug) parameter which simulates the rotation without making real changes:

# Test global configuration
logrotate -d /etc/logrotate.conf

# Test specific configuration
logrotate -d /etc/logrotate.d/nginx

Output:

warning: logrotate in debug mode does nothing except printing debug messages!  Consider using verbose mode (-v) instead if this is not what you want.

reading config file /etc/logrotate.conf
including /etc/logrotate.d
reading config file alternatives
reading config file apt
...
reading config file rsyslog
Reading state from file: /var/lib/logrotate/status
Allocating hash table for state file, size 64 entries
Creating new state
...

Handling 12 logs

rotating pattern: /var/log/alternatives.log monthly empty log files are not rotated, (12 rotations), old logs are removed
considering log /var/log/alternatives.log
  Now: 2026-02-03 02:28
  Last rotated at 2026-02-02 01:52
  log does not need rotating (log has been rotated at 2026-02-02 01:52, which is less than a month ago)
...

Forcing rotation

On the other hand, to force immediate rotation, we’ll use the -f (force) parameter:

# Force all logs
logrotate -f /etc/logrotate.conf

# Force specific configuration
logrotate -f /etc/logrotate.d/nginx

Logrotate Force - Force rotation

Despite not generating output, we can see that its exit code is 0, indicating it executed successfully.

Conclusion

Logrotate is an essential tool for any Linux system administrator that will save you many headaches by automating the lifecycle of your log files.

And even though it’s configured by default in most distributions, it never hurts to learn how to modify and adapt its configuration files to the needs of our infrastructure.

References