Remote Support Start download

Linux Logrotate: Log Management and Rotation Strategies for Servers

LinuxServerSecurity
Linux Logrotate: Log Management and Rotation Strategies for Servers

Log files grow relentlessly. An uncontrolled /var/log can bring an entire Linux system to a halt when the root partition fills up. Logrotate is the standard tool on Linux for automatically rotating, compressing, and deleting log files. Together with the Systemd Journal, it forms the foundation for professional log management on every server.

How Logrotate Works

Logrotate is typically executed once daily as a cron job or Systemd timer. On each run, it checks its configuration and decides for each log file whether rotation is necessary.

The rotation process:

access.log        → access.log.1       (current file renamed)
access.log.1      → access.log.2       (previous rotation shifted)
access.log.2      → access.log.3       (and so on)
access.log.3      → access.log.3.gz    (compressed)
access.log.4.gz   → (deleted)          (oldest file removed)

Trigger Mechanism

# Check Systemd timer (modern distributions)
systemctl status logrotate.timer

# Or classic cron job
cat /etc/cron.daily/logrotate

Basic Configuration

Main Configuration: /etc/logrotate.conf

cat /etc/logrotate.conf
# Global default settings
weekly              # Weekly rotation
rotate 4            # Keep 4 versions
create              # Create new log file after rotation
dateext             # Date instead of number in filename
compress            # Compress rotated files (gzip)
delaycompress       # Compress on next rotation cycle

# Include application-specific configurations
include /etc/logrotate.d

Application-Specific Configuration

Each application places its logrotate configuration in /etc/logrotate.d/:

ls /etc/logrotate.d/
# apt  dpkg  nginx  rsyslog  samba  ufw  ...

Rotation Strategies

Time-Based Rotation

/var/log/nginx/access.log {
    daily               # Daily rotation
    rotate 30           # Keep 30 days
    missingok           # No error if file is missing
    notifempty          # Do not rotate if empty
    compress            # Compress with gzip
    delaycompress       # Delay compression by one rotation
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
    endscript
}
DirectiveMeaning
dailyDaily rotation
weeklyWeekly rotation
monthlyMonthly rotation
yearlyYearly rotation

Size-Based Rotation

/var/log/application/app.log {
    size 100M           # Rotate when > 100 MB
    rotate 10           # Keep 10 versions
    missingok
    compress
    copytruncate        # Copy and truncate instead of rename
    create 0644 appuser appgroup
}

Size-based rotation is ideal for applications with highly variable log volume. Instead of rotating daily, rotation occurs only when a defined threshold is exceeded.

Combined Strategy

/var/log/syslog {
    daily               # Check at least daily
    size 50M            # But also rotate at 50 MB
    rotate 14           # Keep 14 versions
    compress
    delaycompress
    missingok
    notifempty
    postrotate
        /usr/lib/rsyslog/rsyslog-rotate
    endscript
}

compress and delaycompress

compress

The compress directive compresses rotated log files with gzip (default). This saves significant disk space:

# Example: Nginx access log
ls -lh /var/log/nginx/
# access.log           12M   (current file)
# access.log.1         12M   (last rotation, uncompressed)
# access.log.2.gz      1.8M  (compressed, ~85% smaller)
# access.log.3.gz      1.6M

Alternative compression method:

compresscmd /usr/bin/zstd
compressoptions -T0 --rm
compressext .zst
uncompresscmd /usr/bin/unzstd

Zstd offers better compression ratios than gzip at higher speeds.

delaycompress

delaycompress delays compression by one rotation cycle. This is important because some applications still have the just-rotated file open:

Rotation 1: access.log → access.log.1           (uncompressed)
Rotation 2: access.log.1 → access.log.2.gz      (now compressed)

Without delaycompress, access.log.1 would be compressed immediately. Programs that still hold handles to the old file could produce errors.

copytruncate vs create

create (Default)

create 0640 www-data adm

Logrotate renames the log file and creates a new empty file. The application must then open the new file — this is usually triggered by a postrotate script that signals the application to reload.

copytruncate

copytruncate

Logrotate copies the log file and then truncates the original. The application notices nothing because the file handle remains valid.

Advantage: No application reload needed. Disadvantage: Log lines can be lost between copy and truncate (race condition).

MethodAdvantageDisadvantageRecommendation
createNo data lossApplication must reloadDefault for most services
copytruncateNo reload neededPotential data lossFor applications without reload mechanism

postrotate Scripts

postrotate scripts are executed after each rotation. They signal the application to start using the new log file.

Nginx

/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
    endscript
}

kill -USR1 instructs Nginx to reopen log files without interrupting the service.

Apache

/var/log/apache2/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root adm
    sharedscripts
    postrotate
        if invoke-rc.d apache2 status > /dev/null 2>&1; then
            invoke-rc.d apache2 reload > /dev/null
        fi
    endscript
}

PostgreSQL

/var/log/postgresql/postgresql-*.log {
    weekly
    rotate 10
    compress
    delaycompress
    missingok
    notifempty
    create 0640 postgres adm
    su postgres postgres
    postrotate
        /usr/lib/postgresql/16/bin/pg_ctl logrotate -D /var/lib/postgresql/16/main
    endscript
}

sharedscripts

The sharedscripts directive is important with wildcard patterns: without it, the postrotate script would be executed for each rotated file individually. With sharedscripts, it is called only once after all rotations complete.

Systemd Journal

In addition to logrotate, the Systemd Journal (journald) manages logs for all Systemd units. Both systems work in parallel and complement each other.

Journal Configuration

# /etc/systemd/journald.conf
[Journal]
Storage=persistent          # Store logs on disk (instead of RAM only)
SystemMaxUse=2G             # Maximum total size
SystemMaxFileSize=128M      # Maximum size per journal file
MaxRetentionSec=90day       # Maximum age
Compress=yes                # Enable compression
ForwardToSyslog=yes         # Also forward to rsyslog
# Apply configuration
systemctl restart systemd-journald

# Check journal size
journalctl --disk-usage

Manual Cleanup

# Limit journal to 1 GB
journalctl --vacuum-size=1G

# Delete entries older than 30 days
journalctl --vacuum-time=30d

# Limit to maximum 5 files
journalctl --vacuum-files=5

Journal vs Logrotate

AspectSystemd JournalLogrotate
Data formatBinary (structured)Text files
Accessjournalctl with filterscat, grep, less
RotationAutomatic (built-in)Externally configured
MetadataExtensive (PID, unit, priority)Text only
ApplicationsSystemd unitsAny log files

Troubleshooting

Dry Run (Debug Mode)

# Test logrotate in debug mode (no changes)
logrotate -d /etc/logrotate.conf

# Test a single configuration
logrotate -d /etc/logrotate.d/nginx

# Forced rotation (for testing)
logrotate -f /etc/logrotate.d/nginx

Common Errors

Error: “skipping because parent directory has insecure permissions”

# Check and fix directory permissions
chmod 755 /var/log/myapp
chown root:root /var/log/myapp

Error: “error: skipping because of taboo extension”

Logrotate ignores files with certain extensions (.rpmsave, .rpmorig, .dpkg-old). Custom configurations must be named without such extensions.

Error: Rotation does not occur

# Check the status file
cat /var/lib/logrotate/status

# Contains the timestamp of the last rotation per file:
# "/var/log/nginx/access.log" 2026-4-10-0:0:0

Best Practices

1. Monitor Log Directories

# Disk usage of key log directories
du -sh /var/log/*/ 2>/dev/null | sort -rh | head -10

# Warning when /var partition exceeds 80%
VAR_USAGE=$(df /var --output=pcent | tail -1 | tr -d ' %')
if [ "$VAR_USAGE" -gt 80 ]; then
  echo "WARNING: /var is ${VAR_USAGE}% full"
fi

2. Separate /var/log Partition

For production servers, a dedicated partition for /var/log is recommended. This way, a logging issue can never fill the root partition:

# Example: 20 GB /var/log partition
/dev/sda3  /var/log  ext4  defaults,noexec,nosuid  0 2

3. Central Log Aggregation

For environments with multiple servers, logs should be collected centrally:

# rsyslog: Forward logs to central server
# In /etc/rsyslog.d/50-remote.conf
*.* @logserver.example.com:514    # UDP
*.* @@logserver.example.com:514   # TCP

4. Protect Security-Relevant Logs

Auth logs and audit logs deserve special treatment:

/var/log/auth.log {
    daily
    rotate 90           # Retain for 90 days (compliance)
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root adm
    postrotate
        /usr/lib/rsyslog/rsyslog-rotate
    endscript
}

Retaining security-relevant logs for 90 days is a common standard in compliance frameworks such as ISO 27001 or SOC 2.

Conclusion

Logrotate is an unassuming but indispensable tool on every Linux server. With the right configuration — tailored to the log volume and requirements of each application — /var/log stays under control. Combined with the Systemd Journal and central log aggregation, you get a robust log management system that reliably covers both operational and compliance requirements.

More on these topics:

Need IT consulting?

Contact us for a no-obligation consultation on Proxmox, OPNsense, TrueNAS and more.

Get in touch