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
}
| Directive | Meaning |
|---|---|
daily | Daily rotation |
weekly | Weekly rotation |
monthly | Monthly rotation |
yearly | Yearly 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).
| Method | Advantage | Disadvantage | Recommendation |
|---|---|---|---|
create | No data loss | Application must reload | Default for most services |
copytruncate | No reload needed | Potential data loss | For 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
| Aspect | Systemd Journal | Logrotate |
|---|---|---|
| Data format | Binary (structured) | Text files |
| Access | journalctl with filters | cat, grep, less |
| Rotation | Automatic (built-in) | Externally configured |
| Metadata | Extensive (PID, unit, priority) | Text only |
| Applications | Systemd units | Any 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:
More articles
Backup Strategy for SMBs: Proxmox PBS + TrueNAS as a Reliable Backup Solution
Backup strategy for SMBs with Proxmox PBS and TrueNAS: implement the 3-2-1 rule, PBS as primary backup target, TrueNAS replication as offsite copy, retention policies, and automated restore tests.
OPNsense Suricata Custom Rules: Write and Optimize Your Own IDS/IPS Signatures
Suricata custom rules on OPNsense: rule syntax, custom signatures for internal services, performance tuning, suppress lists, and EVE JSON logging.
Systemd Security: Hardening and Securing Linux Services
Systemd security hardening: unit hardening with ProtectSystem, PrivateTmp, NoNewPrivileges, CapabilityBoundingSet, systemd-analyze security, sandboxing, resource limits, and creating custom timers.