LOGROTATE — LOG LIFECYCLE MANAGEMENT Growing Log access.log 2.4 GB Disk: 95% logrotate Rotate Compress Purge old Signal app Rotated Files access.log (new) access.log.1.gz 120K access.log.2.gz 115K access.log.3.gz 118K Disk: 12% Timer systemd logrotate.timer Daily trigger Config /etc/logrotate.conf /etc/logrotate.d/ Per-app rules Automated log lifecycle: rotate, compress, purge, repeat

Log files grow silently in the background of every Linux server. Web servers, application frameworks, databases, and system daemons all write to log files continuously. Left unchecked, these files will consume all available disk space, eventually causing service outages, failed writes, and degraded performance. Logrotate is the standard utility on Linux that solves this problem by automatically rotating, compressing, and removing old log files on a schedule you define.

This guide covers everything you need to configure logrotate effectively: the global configuration, per-application configs, Nginx-specific setups, postrotation scripts, and how to troubleshoot when things go wrong.

Prerequisites

Before you begin, make sure you have:

  • A Linux server running Ubuntu 20.04, 22.04, or 24.04 (instructions also apply to Debian and most RHEL-based distributions)
  • Terminal access with sudo privileges
  • Basic familiarity with the command line and text editing (nano or vim)
  • Log files that need management (web server, application, or system logs)

What Is Logrotate?

Logrotate is a system utility designed to simplify the administration of log files. It automates the rotation, compression, and deletion of log files so they do not fill up your filesystem. Logrotate is pre-installed on virtually every Linux distribution and has been the standard log management tool for decades.

What logrotate does:

  • Rotates log files by renaming them with a numeric suffix or date stamp
  • Compresses old log files using gzip (or other algorithms) to save disk space
  • Removes log files older than a specified number of rotations
  • Signals applications to reopen their log files after rotation
  • Executes custom scripts before or after rotation
# Verify logrotate is installed
logrotate --version

# Expected output:
# logrotate 3.21.0

Why Log Rotation Matters

Without log rotation, a busy Nginx server generating 50,000 requests per day can produce access logs that grow by hundreds of megabytes daily. Over weeks or months, these files balloon to gigabytes. The consequences are predictable:

  • Disk full — services crash when the filesystem hits 100% capacity
  • Slow log analysis — tools like grep and awk become impractical on multi-gigabyte files
  • Backup bloat — uncompressed logs inflate backup sizes and transfer times
  • Compliance risk — some regulations require log retention policies with defined purge schedules

A simple logrotate configuration prevents all of these problems with zero ongoing effort.

How Logrotate Works

Logrotate follows a straightforward execution flow:

  1. A systemd timer triggers logrotate daily — On modern Ubuntu systems, the logrotate.timer unit runs logrotate.service once per day. On older systems, a cron job at /etc/cron.daily/logrotate handles this.

  2. Logrotate reads its configuration — It loads /etc/logrotate.conf (the global config), which includes all files under /etc/logrotate.d/.

  3. It checks each log file against its rules — Based on the directives (daily, size, etc.), logrotate determines whether each log file is due for rotation.

  4. Rotation happens — The current log file is renamed (e.g., access.log becomes access.log.1), and a new empty file is created. Previous rotated files are renumbered (access.log.1 becomes access.log.2, etc.).

  5. Compression runs — Rotated files are compressed (typically with gzip), with delaycompress waiting one cycle before compressing.

  6. Post-rotation scripts execute — Commands inside postrotate/endscript blocks run, typically sending a signal to the application to reopen its log file.

Verify the timer is active:

# Check the systemd timer
systemctl status logrotate.timer

# See when it last ran and when it will run next
systemctl list-timers logrotate

Global Configuration (/etc/logrotate.conf)

The global configuration file sets default directives that apply to all log files unless overridden by application-specific configs.

# View the global configuration
cat /etc/logrotate.conf

A typical default configuration looks like this:

# /etc/logrotate.conf

# Rotate log files weekly
weekly

# Keep 4 weeks of rotated logs
rotate 4

# Create new (empty) log files after rotating old ones
create

# Use date as a suffix of the rotated file
dateext

# Compress rotated log files
compress

# Packages drop their config snippets into this directory
include /etc/logrotate.d

Key points about the global config:

  • weekly — Rotate logs once per week. This is the default schedule.
  • rotate 4 — Keep 4 rotated files before deleting the oldest. With weekly rotation, this keeps one month of logs.
  • create — After rotating, create a new empty log file with the same permissions as the original.
  • dateext — Append the date (e.g., -20260114) instead of a numeric suffix to rotated files.
  • compress — Compress rotated files with gzip.
  • include /etc/logrotate.d — Load all application-specific configuration files from this directory.

Application-Specific Configs (/etc/logrotate.d/)

Each application can have its own logrotate configuration file in /etc/logrotate.d/. These files override the global defaults for specific log files.

# List all application-specific configs
ls -la /etc/logrotate.d/

# Common files you might see:
# apt        dpkg       nginx      rsyslog
# alternatives  cups    samba      ufw

Each file follows the same format: a log file path (or glob pattern), followed by directives enclosed in curly braces.

# Example: /etc/logrotate.d/rsyslog
/var/log/syslog
/var/log/mail.info
/var/log/mail.warn
/var/log/mail.err
/var/log/mail.log
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/cron.log
{
    rotate 4
    weekly
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        /usr/lib/rsyslog/rsyslog-rotate
    endscript
}

Configuration Directives Explained

Logrotate offers dozens of directives to control exactly how rotation behaves. Here are the most important ones:

Rotation Frequency

daily       # Rotate every day
weekly      # Rotate once per week (default)
monthly     # Rotate once per month
yearly      # Rotate once per year

Retention

rotate 7    # Keep 7 rotated files before deleting the oldest
maxage 30   # Remove rotated logs older than 30 days

Compression

compress         # Compress rotated files (gzip by default)
delaycompress    # Wait one rotation cycle before compressing
compresscmd /usr/bin/xz    # Use xz instead of gzip
compressoptions "-9"       # Pass options to the compression command
nocompress       # Do not compress rotated files

File Handling

missingok     # Do not error if the log file is missing
notifempty    # Do not rotate the log if it is empty
create 0640 www-data adm    # Create new file with specific mode, owner, group
copytruncate  # Copy the log and truncate the original (for apps that hold the file open)

Naming

dateext             # Use date-based suffixes (-20260114)
dateformat -%Y%m%d  # Customize the date format
extension .log      # Preserve the .log extension after the date suffix

Custom Configuration for Nginx

Nginx is one of the most common services that requires a logrotate configuration. When Nginx is installed from the official packages, it usually includes a config file, but you may need to customize it.

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    create 0640 www-data adm
    sharedscripts
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 $(cat /var/run/nginx.pid)
        fi
    endscript
}

What each directive does in this context:

  • /var/log/nginx/*.log — Apply these rules to all .log files in the Nginx log directory
  • daily — Rotate every day (overrides the global weekly)
  • rotate 14 — Keep two weeks of rotated logs
  • delaycompress — Do not compress yesterday’s log immediately; this allows log analysis tools to process it first
  • create 0640 www-data adm — The new log file will be owned by www-data with group adm, and permissions 0640
  • sharedscripts — Run the postrotate script only once, even if multiple log files match the glob pattern
  • postrotate — Send the USR1 signal to Nginx, which tells it to reopen its log files without restarting

Important: The USR1 signal is specific to Nginx. Other applications use different signals or require a full restart. Always check the documentation for your specific service.

Custom Configuration for Application Logs

For custom application logs (Rails, Node.js, Django, or any application that writes its own logs), you need to create a logrotate config from scratch.

# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    daily
    rotate 30
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    dateext
    dateformat -%Y%m%d
    maxage 90
}

Key decisions for application logs:

  • copytruncate vs create — If your application cannot reopen its log files (many Node.js and Python apps hold the file descriptor open), use copytruncate. This copies the content to a rotated file and then truncates the original. There is a tiny window where log lines can be lost between the copy and the truncate, but for most applications this is acceptable.

  • rotate 30 with maxage 90 — Keep 30 rotated files, but also delete anything older than 90 days. The maxage directive provides a safety net.

  • dateext — Use date-based names like app.log-20260114.gz instead of numeric names like app.log.1.gz. This makes it easy to find logs from a specific date.

If your application supports signal-based log reopening:

# /etc/logrotate.d/myapp-with-signal
/var/log/myapp/*.log {
    daily
    rotate 30
    missingok
    notifempty
    compress
    delaycompress
    create 0644 appuser appgroup
    sharedscripts
    postrotate
        systemctl kill --signal=HUP myapp.service
    endscript
}

Post-Rotation Scripts (postrotate/endscript)

Post-rotation scripts are blocks of shell commands that execute after log rotation completes. They are essential for signaling applications to reopen their log files.

postrotate
    # This block runs after rotation
    /usr/bin/systemctl reload nginx > /dev/null 2>&1 || true
endscript

There are also prerotate scripts that run before rotation:

prerotate
    # This block runs before rotation
    if [ -d /etc/logrotate.d/httpd-prerotate ]; then
        run-parts /etc/logrotate.d/httpd-prerotate
    fi
endscript

Important rules for rotation scripts:

  • Always end with || true or ; true to prevent script failures from stopping logrotate
  • Use sharedscripts when the log path contains a glob pattern — this ensures the postrotate block runs only once instead of once per matched file
  • Keep scripts minimal and fast — logrotate blocks while scripts execute
  • Test scripts manually before adding them to a logrotate configuration

Common signal patterns for popular services:

# Nginx -- reopen log files
kill -USR1 $(cat /var/run/nginx.pid)

# Apache -- graceful restart
/usr/sbin/apachectl graceful > /dev/null 2>&1

# rsyslog -- reopen log files
/usr/lib/rsyslog/rsyslog-rotate

# systemd service -- generic signal
systemctl kill --signal=HUP myservice.service

Testing and Debugging

Always test your logrotate configurations before waiting for the daily timer to trigger them.

Debug Mode (Dry Run)

Debug mode simulates rotation without making any changes. It shows exactly what logrotate would do:

# Test a specific config file
sudo logrotate -d /etc/logrotate.d/nginx

# Test the entire configuration
sudo logrotate -d /etc/logrotate.conf

Example debug output:

reading config file /etc/logrotate.d/nginx

Handling 1 logs

rotating pattern: /var/log/nginx/*.log  after 1 days (14 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/nginx/access.log
  Now: 2026-01-14 10:00
  Last rotated at 2026-01-13 06:00
  log needs rotating
considering log /var/log/nginx/error.log
  Now: 2026-01-14 10:00
  Last rotated at 2026-01-13 06:00
  log needs rotating

Force Rotation

Force an immediate rotation regardless of the schedule:

# Force rotation for a specific config
sudo logrotate -f /etc/logrotate.d/nginx

# Force rotation with verbose output
sudo logrotate -vf /etc/logrotate.d/nginx

Checking the State File

Logrotate tracks when each log file was last rotated in a state file:

# View the state file
cat /var/lib/logrotate/status

# You'll see entries like:
# "/var/log/nginx/access.log" 2026-1-14-6:0:0
# "/var/log/nginx/error.log" 2026-1-14-6:0:0
# "/var/log/syslog" 2026-1-13-6:0:0

If logrotate is not rotating a file that you expect it to rotate, check this state file. If the last rotation date is recent, logrotate will skip the file until the next rotation interval passes.

Size-Based vs Time-Based Rotation

Logrotate supports both time-based and size-based rotation, and you can combine them.

Time-Based Only

/var/log/myapp/*.log {
    daily        # Rotate every day regardless of file size
    rotate 7
    compress
}

Size-Based Only

/var/log/myapp/*.log {
    size 100M    # Rotate only when the file exceeds 100 MB
    rotate 5
    compress
}

Combined: Time-Based with Size Limits

/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    maxsize 200M    # Also rotate if file exceeds 200 MB before the daily schedule
    minsize 1M      # Do not rotate if the file is smaller than 1 MB, even on schedule
}

Key differences:

  • size replaces time-based rotation entirely — logrotate only checks file size
  • maxsize works alongside time-based rotation — rotate on schedule OR when the size is exceeded, whichever comes first
  • minsize prevents rotation of tiny log files — the log must be at least this size AND the time interval must have passed

For high-traffic servers, the combination of daily with maxsize provides the best balance between predictable schedules and disk space protection.

Logrotate Directives Reference Table

DirectiveDescriptionExample
dailyRotate log files every daydaily
weeklyRotate log files once per weekweekly
monthlyRotate log files once per monthmonthly
rotate NKeep N rotated files before deletionrotate 7
compressCompress rotated files with gzipcompress
delaycompressDelay compression by one rotation cycledelaycompress
missingokDo not error if the log file is missingmissingok
notifemptyDo not rotate if the log file is emptynotifempty
create mode owner groupCreate new log file with specified attributescreate 0640 www-data adm
copytruncateCopy the log then truncate the originalcopytruncate
size NRotate when file exceeds N bytes/KB/MBsize 100M
maxsize NRotate on schedule or when size exceeds Nmaxsize 500M
minsize NOnly rotate if file is at least N in sizeminsize 1M
maxage NRemove rotated files older than N daysmaxage 90
dateextUse date-based suffixes for rotated filesdateext
sharedscriptsRun postrotate scripts only once for all matched filessharedscripts
postrotate/endscriptShell commands to run after rotationSee examples above
prerotate/endscriptShell commands to run before rotationSee examples above
olddir /pathMove rotated files to a different directoryolddir /var/log/archive
su user groupRun rotation as a specific user and groupsu root adm

Troubleshooting

Logrotate Is Not Rotating a File

# 1. Check for syntax errors
sudo logrotate -d /etc/logrotate.d/yourconfig

# 2. Verify the log file path matches the glob pattern
ls -la /var/log/myapp/*.log

# 3. Check the state file for the last rotation date
grep "myapp" /var/lib/logrotate/status

# 4. Verify the timer is running
systemctl status logrotate.timer

Permission Denied Errors

# Check ownership and permissions of the log file
ls -la /var/log/myapp/app.log

# Check that the logrotate config uses the correct su directive
# Add this inside your config block:
su root adm

Disk Space Not Being Freed After Rotation

If disk space is not freed after rotation, an application may still hold the old (deleted) file open:

# Find processes holding deleted files open
sudo lsof +L1 | grep deleted

# Restart the offending service to release the file descriptor
sudo systemctl restart myapp

This is exactly the scenario where copytruncate helps — it truncates the file in place rather than deleting it, so the file descriptor remains valid.

State File Corruption

If logrotate behaves erratically, the state file may be corrupted:

# Back up the current state file
sudo cp /var/lib/logrotate/status /var/lib/logrotate/status.bak

# Remove the state file (logrotate will recreate it)
sudo rm /var/lib/logrotate/status

# Force a full rotation to rebuild state
sudo logrotate -f /etc/logrotate.conf

Logs Rotated but Not Compressed

If you see uncompressed .1 files alongside compressed .2.gz files, this is expected behavior when delaycompress is enabled. The most recently rotated file stays uncompressed for one cycle. If you need immediate compression, remove the delaycompress directive.

Summary

Logrotate is one of those utilities that works silently in the background until something goes wrong — and when it goes wrong, it usually means your disk is full. Taking 15 minutes to set up proper logrotate configurations for your applications saves hours of emergency disk cleanup later.

The key points to remember:

  • The global config at /etc/logrotate.conf sets defaults; per-application configs in /etc/logrotate.d/ override them
  • Use daily with compress and rotate 14 as a sensible starting point for most services
  • Always use postrotate scripts to signal applications to reopen their log files
  • Test every configuration with logrotate -d before deploying it
  • Use copytruncate for applications that cannot reopen their log files
  • Combine daily with maxsize for high-traffic servers

For securing the rest of your server, check out our Linux Server Security Checklist: 20 Essential Steps and the Nginx Reverse Proxy Complete Guide for setting up Nginx correctly from the start.