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
grepandawkbecome 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:
-
A systemd timer triggers logrotate daily — On modern Ubuntu systems, the
logrotate.timerunit runslogrotate.serviceonce per day. On older systems, a cron job at/etc/cron.daily/logrotatehandles this. -
Logrotate reads its configuration — It loads
/etc/logrotate.conf(the global config), which includes all files under/etc/logrotate.d/. -
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.
-
Rotation happens — The current log file is renamed (e.g.,
access.logbecomesaccess.log.1), and a new empty file is created. Previous rotated files are renumbered (access.log.1becomesaccess.log.2, etc.). -
Compression runs — Rotated files are compressed (typically with gzip), with
delaycompresswaiting one cycle before compressing. -
Post-rotation scripts execute — Commands inside
postrotate/endscriptblocks 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.logfiles in the Nginx log directorydaily— Rotate every day (overrides the globalweekly)rotate 14— Keep two weeks of rotated logsdelaycompress— Do not compress yesterday’s log immediately; this allows log analysis tools to process it firstcreate 0640 www-data adm— The new log file will be owned bywww-datawith groupadm, and permissions0640sharedscripts— Run the postrotate script only once, even if multiple log files match the glob patternpostrotate— Send theUSR1signal to Nginx, which tells it to reopen its log files without restarting
Important: The
USR1signal 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:
-
copytruncatevscreate— If your application cannot reopen its log files (many Node.js and Python apps hold the file descriptor open), usecopytruncate. 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 30withmaxage 90— Keep 30 rotated files, but also delete anything older than 90 days. Themaxagedirective provides a safety net. -
dateext— Use date-based names likeapp.log-20260114.gzinstead of numeric names likeapp.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
|| trueor; trueto prevent script failures from stopping logrotate - Use
sharedscriptswhen 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:
sizereplaces time-based rotation entirely — logrotate only checks file sizemaxsizeworks alongside time-based rotation — rotate on schedule OR when the size is exceeded, whichever comes firstminsizeprevents 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
| Directive | Description | Example |
|---|---|---|
daily | Rotate log files every day | daily |
weekly | Rotate log files once per week | weekly |
monthly | Rotate log files once per month | monthly |
rotate N | Keep N rotated files before deletion | rotate 7 |
compress | Compress rotated files with gzip | compress |
delaycompress | Delay compression by one rotation cycle | delaycompress |
missingok | Do not error if the log file is missing | missingok |
notifempty | Do not rotate if the log file is empty | notifempty |
create mode owner group | Create new log file with specified attributes | create 0640 www-data adm |
copytruncate | Copy the log then truncate the original | copytruncate |
size N | Rotate when file exceeds N bytes/KB/MB | size 100M |
maxsize N | Rotate on schedule or when size exceeds N | maxsize 500M |
minsize N | Only rotate if file is at least N in size | minsize 1M |
maxage N | Remove rotated files older than N days | maxage 90 |
dateext | Use date-based suffixes for rotated files | dateext |
sharedscripts | Run postrotate scripts only once for all matched files | sharedscripts |
postrotate/endscript | Shell commands to run after rotation | See examples above |
prerotate/endscript | Shell commands to run before rotation | See examples above |
olddir /path | Move rotated files to a different directory | olddir /var/log/archive |
su user group | Run rotation as a specific user and group | su 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.confsets defaults; per-application configs in/etc/logrotate.d/override them - Use
dailywithcompressandrotate 14as a sensible starting point for most services - Always use
postrotatescripts to signal applications to reopen their log files - Test every configuration with
logrotate -dbefore deploying it - Use
copytruncatefor applications that cannot reopen their log files - Combine
dailywithmaxsizefor 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.