Log Retention and Rotation: Complete Implementation Guide
Introduction
Log retention and rotation are critical components of system administration, security monitoring, compliance, and troubleshooting. Properly managed logs provide an essential audit trail for security investigations, help diagnose system issues, enable performance analysis, and demonstrate compliance with regulatory requirements. However, logs that are not properly rotated can consume massive amounts of disk space, while logs retained for too short a period may not be available when needed for forensic analysis or compliance audits.
This comprehensive guide provides Linux system administrators with practical knowledge for implementing effective log rotation policies, configuring retention periods that meet both operational and compliance requirements, and establishing automated procedures for log management. Whether you're managing a single server or a large-scale infrastructure, this guide covers the essential tools, configurations, and best practices for maintaining comprehensive, compliant, and manageable log systems.
Why Log Retention and Rotation Matter
Operational Benefits:
- Disk Space Management: Prevents log files from consuming all available storage
- Performance Optimization: Keeps log files at manageable sizes for faster searching and processing
- Troubleshooting: Maintains historical data for diagnosing recurring or intermittent issues
- Capacity Planning: Provides data for trend analysis and infrastructure planning
Security and Compliance Benefits:
- Security Incident Investigation: Provides forensic evidence for security breach analysis
- Compliance Requirements: Meets regulatory requirements for log retention (GDPR, PCI-DSS, HIPAA, SOX)
- Audit Trail: Demonstrates system activity and changes for auditors
- Intrusion Detection: Enables detection of attack patterns over time
Regulatory Requirements for Log Retention
Different regulations mandate specific log retention periods:
GDPR (General Data Protection Regulation):
- No specific retention period mandated
- Logs must be retained only as long as necessary for stated purpose
- Personal data in logs subject to data minimization principles
- Typically 6 months to 2 years for security logs
PCI-DSS (Payment Card Industry Data Security Standard):
- Minimum 3 months immediately available
- Minimum 1 year total retention
- System component logs must be retained and protected
HIPAA (Health Insurance Portability and Accountability Act):
- Minimum 6 years for audit logs
- Security incident logs retained for investigation period
SOX (Sarbanes-Oxley Act):
- Minimum 7 years for financial systems
- Access logs, change logs, and audit trails required
ISO 27001:
- Typically requires 12 months of log retention
- Security events retained for analysis and investigation
This guide helps you implement retention policies that meet these requirements while maintaining operational efficiency.
Understanding Log Rotation
What is Log Rotation?
Log rotation is the automated process of:
- Renaming current log files
- Creating new log files
- Compressing old log files
- Deleting very old log files based on retention policy
- Notifying services to write to new log files
Rotation Methods
Time-Based Rotation: Rotate logs based on time intervals (daily, weekly, monthly)
- Best for: Predictable log sizes, compliance requirements with time-based retention
- Example: Rotate daily, keep 30 days
Size-Based Rotation: Rotate logs when they reach a specific size
- Best for: High-volume logging, preventing individual files from becoming too large
- Example: Rotate at 100MB, keep 10 files
Combined Approach: Rotate based on both time and size
- Best for: Variable log volumes with size limits
- Example: Rotate daily or at 500MB, whichever comes first
Logrotate - The Standard Tool
Logrotate is the standard log rotation utility on Linux systems, providing flexible and powerful log management capabilities.
Installing Logrotate
# Install logrotate (usually pre-installed)
sudo apt-get update
sudo apt-get install -y logrotate # Debian/Ubuntu
sudo yum install -y logrotate # RHEL/CentOS
# Verify installation
logrotate --version
# Check if logrotate is scheduled
systemctl status logrotate.timer # systemd-based systems
ls -la /etc/cron.daily/logrotate # cron-based systems
Logrotate Configuration Structure
# Main configuration file
cat /etc/logrotate.conf
# Configuration directory for individual services
ls -la /etc/logrotate.d/
# Logrotate state file (tracks rotation status)
cat /var/lib/logrotate/status
Basic Logrotate Configuration
# Create custom logrotate configuration
sudo tee /etc/logrotate.d/custom-app << 'EOF'
# Custom Application Log Rotation
/var/log/custom-app/*.log {
# Rotate daily
daily
# Keep 30 days of logs
rotate 30
# Compress old logs
compress
# Delay compression by one rotation cycle
delaycompress
# Don't rotate if log is empty
notifempty
# Create new log file with specific permissions
create 0640 www-data adm
# Use date as extension instead of numbers
dateext
# Date format
dateformat -%Y%m%d
# Post-rotation script
postrotate
# Reload service to use new log file
systemctl reload custom-app > /dev/null 2>&1 || true
endscript
}
EOF
# Test configuration without actually rotating
sudo logrotate -d /etc/logrotate.d/custom-app
# Force rotation (for testing)
sudo logrotate -f /etc/logrotate.d/custom-app
# Check rotation status
sudo cat /var/lib/logrotate/status | grep custom-app
Logrotate Directives Explained
# Create comprehensive example with all common directives
sudo tee /etc/logrotate.d/comprehensive-example << 'EOF'
# Comprehensive Logrotate Example
/var/log/app/*.log {
# ROTATION FREQUENCY
daily # Rotate daily (also: hourly, weekly, monthly, yearly)
# RETENTION POLICY
rotate 90 # Keep 90 rotated logs
# SIZE CONSTRAINTS
size 100M # Rotate if log exceeds 100MB (also: k, M, G)
maxsize 500M # Force rotation at 500MB regardless of schedule
# COMPRESSION
compress # Compress rotated logs with gzip
delaycompress # Compress on next rotation (keeps previous log uncompressed)
compresscmd /usr/bin/gzip # Compression command
compressext .gz # Compression extension
compressoptions -9 # Maximum compression
# FILE HANDLING
create 0640 www-data adm # Create new file with mode, owner, group
# Or: nocreate # Don't create new file
# Or: copytruncate # Copy then truncate (for apps that can't reopen files)
# EMPTY FILES
notifempty # Don't rotate if log is empty
# Or: ifempty # Rotate even if empty
# MISSING FILES
missingok # Don't error if log file is missing
# Or: nomissingok # Error if log file is missing
# DATING
dateext # Use date in rotated filename
dateformat -%Y%m%d-%s # Date format
dateyesterday # Use yesterday's date
# OLD DIRECTORY
olddir /var/log/app/archive # Move rotated logs to different directory
# createolddir 0755 root root # Create olddir if missing
# MAIL
mail [email protected] # Email log before deletion
mailfirst # Mail first rotated log
# Or: maillast # Mail last rotated log before deletion
# SHARED SCRIPTS
sharedscripts # Run postrotate script once for all logs
# EXTENSION
extension .log # Force specific extension
# PRE/POST ROTATION SCRIPTS
prerotate
# Commands to run before rotation
/usr/local/bin/pre-rotation-backup.sh
endscript
postrotate
# Commands to run after rotation
systemctl reload app > /dev/null 2>&1 || true
endscript
# LAST ACTION
lastaction
# Commands to run after all logs rotated
/usr/local/bin/cleanup.sh
endscript
}
EOF
Apache/Nginx Log Rotation
# Apache log rotation configuration
sudo tee /etc/logrotate.d/apache2 << 'EOF'
/var/log/apache2/*.log {
daily
rotate 52 # Keep 52 weeks (1 year)
compress
delaycompress
notifempty
create 0640 root adm
dateext
sharedscripts
postrotate
# Reload Apache gracefully
if /etc/init.d/apache2 status > /dev/null ; then \
/etc/init.d/apache2 reload > /dev/null; \
fi;
endscript
}
# Separate configuration for access logs (higher retention for compliance)
/var/log/apache2/access*.log {
daily
rotate 365 # Keep 1 year for compliance
compress
delaycompress
notifempty
create 0640 root adm
dateext
sharedscripts
postrotate
if /etc/init.d/apache2 status > /dev/null ; then \
/etc/init.d/apache2 reload > /dev/null; \
fi;
endscript
}
EOF
# Nginx log rotation configuration
sudo tee /etc/logrotate.d/nginx << 'EOF'
/var/log/nginx/*.log {
daily
rotate 52
compress
delaycompress
notifempty
create 0640 www-data adm
dateext
sharedscripts
postrotate
# Send USR1 signal to reopen log files
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
EOF
MySQL/MariaDB Log Rotation
# MySQL log rotation configuration
sudo tee /etc/logrotate.d/mysql-server << 'EOF'
# MySQL Error Log
/var/log/mysql/error.log {
daily
rotate 365 # Keep 1 year
compress
delaycompress
notifempty
create 0640 mysql adm
dateext
sharedscripts
postrotate
# Flush MySQL logs
test -x /usr/bin/mysqladmin && \
/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs
endscript
}
# MySQL Slow Query Log
/var/log/mysql/slow-query.log {
daily
rotate 90 # Keep 90 days
compress
delaycompress
notifempty
create 0640 mysql adm
dateext
maxsize 1G
sharedscripts
postrotate
test -x /usr/bin/mysqladmin && \
/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs
endscript
}
# MySQL General Query Log
/var/log/mysql/query.log {
size 100M # Rotate at 100MB (can grow quickly)
rotate 10 # Keep only 10 rotations
compress
delaycompress
notifempty
create 0640 mysql adm
dateext
sharedscripts
postrotate
test -x /usr/bin/mysqladmin && \
/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs
endscript
}
EOF
PostgreSQL Log Rotation
# PostgreSQL log rotation configuration
sudo tee /etc/logrotate.d/postgresql << 'EOF'
/var/log/postgresql/postgresql-*.log {
daily
rotate 365
compress
delaycompress
notifempty
create 0640 postgres postgres
dateext
missingok
sharedscripts
postrotate
# Send SIGHUP to PostgreSQL to reopen log files
/usr/bin/pg_ctlcluster --skip-systemctl-redirect 13 main reload > /dev/null
endscript
}
EOF
Syslog Rotation
# Rsyslog rotation configuration
sudo tee /etc/logrotate.d/rsyslog << 'EOF'
/var/log/syslog
/var/log/mail.info
/var/log/mail.warn
/var/log/mail.err
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
{
daily
rotate 90 # Keep 90 days
compress
delaycompress
notifempty
create 0640 syslog adm
dateext
sharedscripts
postrotate
# Tell rsyslog to reopen log files
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
# Critical logs with longer retention
/var/log/auth.log
/var/log/secure
{
daily
rotate 365 # Keep 1 year for security audit
compress
delaycompress
notifempty
create 0600 root root
dateext
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
EOF
Compliance-Driven Retention Policies
PCI-DSS Compliant Log Rotation
# PCI-DSS requires 3 months immediately available, 1 year total
sudo tee /etc/logrotate.d/pci-compliance << 'EOF'
# PCI-DSS Audit Logs (Payment Processing Systems)
# Application logs
/var/log/payment-app/*.log {
daily
rotate 365 # 1 year total retention
compress
delaycompress
notifempty
create 0600 root root
dateext
dateformat -%Y%m%d
# First 90 days uncompressed (immediately available)
# This is handled by delaycompress and manual processes
sharedscripts
postrotate
systemctl reload payment-app > /dev/null 2>&1 || true
endscript
}
# Access logs (cardholder data environment)
/var/log/nginx/payment*.log {
daily
rotate 365
compress
delaycompress
notifempty
create 0640 www-data adm
dateext
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
# Database audit logs
/var/log/mysql/audit.log {
daily
rotate 365
compress
delaycompress
notifempty
create 0600 mysql mysql
dateext
maxsize 1G
sharedscripts
postrotate
mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs
endscript
}
# System audit logs (auditd)
/var/log/audit/audit.log {
daily
rotate 365
compress
delaycompress
notifempty
create 0600 root root
dateext
sharedscripts
postrotate
/sbin/service auditd rotate > /dev/null 2>&1 || true
endscript
}
EOF
GDPR Compliant Log Rotation
# GDPR-compliant log rotation with privacy considerations
sudo tee /etc/logrotate.d/gdpr-compliance << 'EOF'
# GDPR Compliant Log Rotation
# Web server logs (contain personal data - IP addresses)
/var/log/nginx/access.log
/var/log/apache2/access.log {
daily
rotate 180 # 6 months retention (adjust per your privacy policy)
compress
delaycompress
notifempty
create 0640 www-data adm
dateext
sharedscripts
# Anonymize IP addresses before compression
prerotate
# Anonymize last rotated log
if [ -f /var/log/nginx/access.log.1 ]; then
/usr/local/bin/anonymize-logs.sh /var/log/nginx/access.log.1
fi
endscript
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
/etc/init.d/apache2 reload > /dev/null 2>&1 || true
endscript
}
# Application logs (minimal personal data retention)
/var/log/app/user-activity.log {
daily
rotate 90 # 90 days only
compress
delaycompress
notifempty
create 0640 app-user adm
dateext
# Sanitize personal data before rotation
prerotate
/usr/local/bin/gdpr-sanitize.sh /var/log/app/user-activity.log.1
endscript
postrotate
systemctl reload app-service > /dev/null 2>&1 || true
endscript
}
# Security logs (legitimate interest, longer retention)
/var/log/auth.log
/var/log/security.log {
daily
rotate 730 # 2 years for security incident investigation
compress
delaycompress
notifempty
create 0600 root root
dateext
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
EOF
# Create log anonymization script
sudo tee /usr/local/bin/anonymize-logs.sh << 'EOF'
#!/bin/bash
# Anonymize IP addresses in log files for GDPR compliance
LOG_FILE="$1"
if [ ! -f "$LOG_FILE" ]; then
exit 0
fi
# Create temporary file
TEMP_FILE=$(mktemp)
# Anonymize IPv4 addresses (mask last octet)
# 192.168.1.100 becomes 192.168.1.0
sed -E 's/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)[0-9]{1,3}/\10/g' "$LOG_FILE" > "$TEMP_FILE"
# Anonymize IPv6 addresses (mask last 4 groups)
# 2001:db8::1 becomes 2001:db8::0
sed -i -E 's/([0-9a-f]{1,4}::[0-9a-f]{1,4})/\1:0/g' "$TEMP_FILE"
# Replace original file
mv "$TEMP_FILE" "$LOG_FILE"
chmod 640 "$LOG_FILE"
EOF
sudo chmod +x /usr/local/bin/anonymize-logs.sh
HIPAA Compliant Log Rotation
# HIPAA requires 6 years retention for audit logs
sudo tee /etc/logrotate.d/hipaa-compliance << 'EOF'
# HIPAA Compliant Log Rotation (Healthcare Systems)
# Application audit logs
/var/log/healthcare-app/audit.log {
daily
rotate 2190 # 6 years retention (365 * 6)
compress
delaycompress
notifempty
create 0600 root root
dateext
dateformat -%Y%m%d
# After 90 days, move to archive storage
lastaction
find /var/log/healthcare-app -name "audit.log-*.gz" -mtime +90 \
-exec mv {} /archive/logs/healthcare-app/ \;
endscript
postrotate
systemctl reload healthcare-app > /dev/null 2>&1 || true
endscript
}
# Database access logs
/var/log/mysql/access-audit.log {
daily
rotate 2190 # 6 years
compress
delaycompress
notifempty
create 0600 mysql mysql
dateext
maxsize 500M
postrotate
mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs
endscript
}
# System access logs
/var/log/auth.log
/var/log/secure {
daily
rotate 2190 # 6 years
compress
delaycompress
notifempty
create 0600 root root
dateext
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
EOF
Advanced Log Management
Size-Based Rotation with Limits
# High-volume application with size limits
sudo tee /etc/logrotate.d/high-volume-app << 'EOF'
/var/log/high-volume-app/*.log {
# Rotate when log reaches 500MB
size 500M
# Keep last 20 rotations
rotate 20
# But never keep more than 30 days
maxage 30
compress
delaycompress
notifempty
create 0640 app-user adm
dateext
# If log grows beyond 2GB, force immediate rotation
maxsize 2G
postrotate
systemctl reload high-volume-app > /dev/null 2>&1 || true
endscript
}
EOF
Hourly Log Rotation
# For extremely high-volume logs
sudo tee /etc/logrotate.d/hourly-rotation << 'EOF'
/var/log/streaming-app/*.log {
hourly # Rotate every hour
rotate 168 # Keep 1 week (24 * 7)
compress
delaycompress
notifempty
create 0640 stream-user adm
dateext
dateformat -%Y%m%d-%H
postrotate
systemctl reload streaming-app > /dev/null 2>&1 || true
endscript
}
EOF
# Create hourly cron job for logrotate
sudo tee /etc/cron.hourly/logrotate-hourly << 'EOF'
#!/bin/bash
/usr/sbin/logrotate /etc/logrotate.d/hourly-rotation
EOF
sudo chmod +x /etc/cron.hourly/logrotate-hourly
Copytruncate Method
# For applications that can't reopen log files
sudo tee /etc/logrotate.d/copytruncate-example << 'EOF'
/var/log/legacy-app/application.log {
daily
rotate 30
compress
# Copy and truncate instead of move and create
copytruncate
notifempty
dateext
}
EOF
Multi-Level Archiving
# Recent logs on fast storage, old logs on archive storage
sudo tee /etc/logrotate.d/multi-level-archive << 'EOF'
# Recent logs (last 30 days on SSD)
/var/log/application/*.log {
daily
rotate 30
compress
delaycompress
notifempty
create 0640 app-user adm
dateext
# Move logs older than 30 days to archive storage
lastaction
find /var/log/application -name "*.log-*.gz" -mtime +30 \
-exec mv {} /mnt/archive-storage/logs/application/ \;
endscript
postrotate
systemctl reload application > /dev/null 2>&1 || true
endscript
}
EOF
# Additional script to handle archive storage rotation
sudo tee /etc/logrotate.d/archive-storage << 'EOF'
# Archive storage (keep 1 year)
/mnt/archive-storage/logs/application/*.gz {
monthly
rotate 12
compress
notifempty
maxage 365
}
EOF
Centralized Logging with Rotation
Rsyslog Central Server
# Configure rsyslog to receive remote logs
sudo tee /etc/rsyslog.d/remote-logs.conf << 'EOF'
# Enable UDP syslog reception
module(load="imudp")
input(type="imudp" port="514")
# Enable TCP syslog reception
module(load="imtcp")
input(type="imtcp" port="514")
# Template for remote host logs
template(name="RemoteHost" type="string"
string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
# Log remote hosts to separate files
if $fromhost-ip != '127.0.0.1' then ?RemoteHost
& stop
EOF
sudo systemctl restart rsyslog
# Configure rotation for remote host logs
sudo tee /etc/logrotate.d/remote-logs << 'EOF'
/var/log/remote/*/*.log {
daily
rotate 90
compress
delaycompress
notifempty
create 0640 syslog adm
dateext
# Organize by hostname and date
olddir archive
createolddir 0755 syslog adm
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
EOF
Syslog Client Configuration
# Configure client to send logs to central server
sudo tee /etc/rsyslog.d/central-logging.conf << 'EOF'
# Send all logs to central server
*.* @@central-log-server:514 # TCP
*.* @central-log-server:514 # UDP (less reliable but faster)
# Also keep local copies
*.* /var/log/messages
EOF
sudo systemctl restart rsyslog
Monitoring and Alerting
Log Rotation Monitoring Script
# Create monitoring script for log rotation
sudo tee /root/scripts/monitor_log_rotation.sh << 'EOF'
#!/bin/bash
# Monitor Log Rotation Status
REPORT_FILE="/var/log/admin/log_rotation_status_$(date +%Y%m%d).log"
ALERT_EMAIL="[email protected]"
WARN_SIZE_MB=5000 # Warn if log directory exceeds 5GB
mkdir -p /var/log/admin
exec > >(tee -a "$REPORT_FILE")
echo "========================================"
echo "LOG ROTATION STATUS REPORT"
echo "Date: $(date)"
echo "========================================"
echo ""
# Check logrotate status file
echo "=== Last Rotation Times ==="
tail -20 /var/lib/logrotate/status
echo ""
# Check for large unrotated logs
echo "=== Large Log Files (>100MB) ==="
find /var/log -type f -size +100M -exec ls -lh {} \; | \
awk '{print $9, $5}' | sort -k2 -hr
echo ""
# Check disk usage by log directory
echo "=== Disk Usage by Log Directory ==="
du -sh /var/log/*/ 2>/dev/null | sort -hr | head -20
echo ""
# Check total log directory size
TOTAL_SIZE_KB=$(du -s /var/log | awk '{print $1}')
TOTAL_SIZE_MB=$((TOTAL_SIZE_KB / 1024))
echo "Total /var/log size: ${TOTAL_SIZE_MB}MB"
if [ "$TOTAL_SIZE_MB" -gt "$WARN_SIZE_MB" ]; then
echo "WARNING: Log directory size exceeds threshold!"
echo "Log directory size: ${TOTAL_SIZE_MB}MB (threshold: ${WARN_SIZE_MB}MB)" | \
mail -s "Log Size Alert" "$ALERT_EMAIL"
fi
echo ""
# Check for rotation failures
echo "=== Recent Logrotate Errors ==="
grep -i "error" /var/log/syslog | grep logrotate | tail -10
echo ""
# Check for logs that haven't been rotated recently
echo "=== Logs Not Rotated in 7+ Days ==="
find /var/log -name "*.log" -type f -mtime +7 -exec ls -lh {} \; | \
awk '{print $9, $5, $6, $7, $8}'
echo ""
# Verify logrotate is scheduled
echo "=== Logrotate Schedule Status ==="
if systemctl is-enabled logrotate.timer &>/dev/null; then
echo "Logrotate timer: ENABLED"
systemctl status logrotate.timer | grep -E "Active|Trigger"
elif [ -f /etc/cron.daily/logrotate ]; then
echo "Logrotate cron job: EXISTS"
ls -l /etc/cron.daily/logrotate
else
echo "WARNING: Logrotate scheduling not found!"
echo "Logrotate not scheduled!" | mail -s "Logrotate Alert" "$ALERT_EMAIL"
fi
echo ""
echo "========================================"
echo "REPORT COMPLETED: $(date)"
echo "========================================"
EOF
sudo chmod +x /root/scripts/monitor_log_rotation.sh
# Schedule weekly monitoring
echo "0 8 * * 1 /root/scripts/monitor_log_rotation.sh | mail -s 'Weekly Log Rotation Report' [email protected]" | sudo crontab -
Disk Space Monitoring
# Create disk space monitoring for logs
sudo tee /root/scripts/monitor_log_disk_usage.sh << 'EOF'
#!/bin/bash
# Monitor Log Disk Usage
ALERT_EMAIL="[email protected]"
WARN_PERCENT=80
CRIT_PERCENT=90
# Check log partition usage
LOG_USAGE=$(df -h /var/log | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$LOG_USAGE" -ge "$CRIT_PERCENT" ]; then
echo "CRITICAL: Log partition usage at ${LOG_USAGE}%" | \
mail -s "CRITICAL: Log Disk Usage Alert" "$ALERT_EMAIL"
elif [ "$LOG_USAGE" -ge "$WARN_PERCENT" ]; then
echo "WARNING: Log partition usage at ${LOG_USAGE}%" | \
mail -s "WARNING: Log Disk Usage Alert" "$ALERT_EMAIL"
fi
# Log largest files for investigation
if [ "$LOG_USAGE" -ge "$WARN_PERCENT" ]; then
echo "Top 20 largest log files:" > /tmp/large_logs.txt
find /var/log -type f -exec du -h {} \; | sort -hr | head -20 >> /tmp/large_logs.txt
mail -s "Large Log Files Report" "$ALERT_EMAIL" < /tmp/large_logs.txt
rm /tmp/large_logs.txt
fi
EOF
sudo chmod +x /root/scripts/monitor_log_disk_usage.sh
# Schedule hourly disk usage checks
echo "0 * * * * /root/scripts/monitor_log_disk_usage.sh" | sudo crontab -
Log Archiving and Compression
Automated Log Archiving
# Create log archiving script
sudo tee /root/scripts/archive_old_logs.sh << 'EOF'
#!/bin/bash
# Archive old logs to long-term storage
ARCHIVE_DIR="/mnt/archive-storage/logs"
SOURCE_DIR="/var/log"
DAYS_BEFORE_ARCHIVE=90
ARCHIVE_NAME="logs-archive-$(date +%Y%m%d).tar.gz"
mkdir -p "$ARCHIVE_DIR"
# Find logs older than threshold
find "$SOURCE_DIR" -name "*.log-*.gz" -mtime +$DAYS_BEFORE_ARCHIVE | \
tar -czf "$ARCHIVE_DIR/$ARCHIVE_NAME" -T -
# Verify archive created
if [ -f "$ARCHIVE_DIR/$ARCHIVE_NAME" ]; then
echo "Archive created: $ARCHIVE_DIR/$ARCHIVE_NAME"
# Calculate checksum
sha256sum "$ARCHIVE_DIR/$ARCHIVE_NAME" > "$ARCHIVE_DIR/$ARCHIVE_NAME.sha256"
# Remove archived files from source
find "$SOURCE_DIR" -name "*.log-*.gz" -mtime +$DAYS_BEFORE_ARCHIVE -delete
echo "Old logs archived and removed from $SOURCE_DIR"
else
echo "ERROR: Archive creation failed"
exit 1
fi
EOF
sudo chmod +x /root/scripts/archive_old_logs.sh
# Schedule monthly archiving
echo "0 2 1 * * /root/scripts/archive_old_logs.sh" | sudo crontab -
Compress Uncompressed Logs
# Compress old uncompressed logs to save space
sudo tee /root/scripts/compress_old_logs.sh << 'EOF'
#!/bin/bash
# Compress uncompressed log files older than 7 days
find /var/log -name "*.log-*" ! -name "*.gz" -type f -mtime +7 | while read logfile; do
gzip -9 "$logfile"
echo "Compressed: $logfile"
done
EOF
sudo chmod +x /root/scripts/compress_old_logs.sh
# Schedule daily compression
echo "0 3 * * * /root/scripts/compress_old_logs.sh" | sudo crontab -
Troubleshooting Log Rotation
Common Issues and Solutions
# Debug logrotate configuration
sudo logrotate -d /etc/logrotate.conf
# Force rotation (useful for testing)
sudo logrotate -f /etc/logrotate.conf
# Check logrotate status
sudo cat /var/lib/logrotate/status
# Manually test specific configuration
sudo logrotate -d /etc/logrotate.d/apache2
sudo logrotate -v -f /etc/logrotate.d/apache2
# Check for errors in syslog
sudo grep logrotate /var/log/syslog | tail -20
# Verify file permissions
ls -la /var/log/*.log
ls -la /var/lib/logrotate/status
# Test postrotate scripts manually
/etc/init.d/apache2 reload
systemctl reload nginx
Fix Common Permission Issues
# Fix logrotate state file permissions
sudo chown root:root /var/lib/logrotate/status
sudo chmod 644 /var/lib/logrotate/status
# Fix log directory permissions
sudo chown -R syslog:adm /var/log
sudo chmod 755 /var/log
# Fix individual log file permissions
sudo find /var/log -name "*.log" -exec chmod 640 {} \;
Best Practices Summary
Log Retention Best Practices
-
Define Clear Retention Policies:
- Security logs: 1-2 years minimum
- Compliance logs: Per regulatory requirements
- Application logs: 30-90 days
- Debug logs: 7-14 days
-
Implement Multi-Tier Storage:
- Recent logs (0-30 days): Fast SSD storage
- Archive logs (31-365 days): Slower HDD storage
- Long-term archive (1+ years): Tape or cold storage
-
Compress Rotated Logs:
- Use gzip or bzip2 compression
- Delay compression by one cycle for recent access
- Compress logs older than 7 days
-
Monitor Disk Usage:
- Set up alerts at 80% and 90% capacity
- Regularly review largest log files
- Adjust retention policies based on usage
-
Test Rotation Regularly:
- Verify rotation is working
- Test log restoration from archives
- Validate postrotate scripts
-
Document Retention Policies:
- Document why each retention period was chosen
- Include regulatory requirements
- Review and update annually
Configuration Checklist
# Create comprehensive configuration checklist
cat > /root/docs/log_rotation_checklist.md << 'EOF'
# Log Rotation Configuration Checklist
## Configuration Files
- [ ] /etc/logrotate.conf reviewed and configured
- [ ] Service-specific configs in /etc/logrotate.d/ created
- [ ] Retention periods match compliance requirements
- [ ] Compression enabled for rotated logs
- [ ] Postrotate scripts tested
## Monitoring
- [ ] Disk usage monitoring configured
- [ ] Rotation status monitoring scheduled
- [ ] Alerts configured for failures
- [ ] Log review processes established
## Testing
- [ ] Logrotate configuration tested with -d flag
- [ ] Forced rotation tested with -f flag
- [ ] Postrotate scripts execute successfully
- [ ] Log files created with correct permissions
- [ ] Services reload/restart correctly after rotation
## Documentation
- [ ] Retention policies documented
- [ ] Compliance requirements identified
- [ ] Rotation schedules documented
- [ ] Recovery procedures documented
## Security
- [ ] Log file permissions set correctly (640 or 600)
- [ ] Log directories protected (755 with appropriate ownership)
- [ ] Sensitive logs encrypted if required
- [ ] Access to logs restricted to authorized personnel
## Archiving
- [ ] Archive storage configured
- [ ] Archive verification process established
- [ ] Archive retention policy defined
- [ ] Archive restoration tested
EOF
Conclusion
Effective log retention and rotation are essential for maintaining system health, enabling security investigations, and meeting compliance requirements. This guide has provided comprehensive coverage of configuring logrotate for various services, implementing compliance-driven retention policies, and establishing monitoring and archiving procedures.
Key Takeaways
1. Balance Retention with Resources: Longer retention provides better forensic capability but consumes more storage. Find the right balance for your requirements.
2. Automate Everything: Manual log management doesn't scale. Automated rotation, compression, and archiving ensure consistency.
3. Compliance Drives Retention: Understand regulatory requirements (PCI-DSS, GDPR, HIPAA) and configure retention accordingly.
4. Monitor Proactively: Don't wait for disk space issues. Monitor rotation status and disk usage continuously.
5. Test Regularly: Verify that rotation works, archives are accessible, and restoration procedures function correctly.
By implementing the configurations and procedures outlined in this guide, you'll establish robust log management practices that support operational needs, security investigations, and compliance requirements while keeping storage usage under control.


