Skip to main content

Logrotate: automatic log rotation on Linux

VDS / VPS · 15.02.2026 · 6 min read
Illustration for “Logrotate: automatic log rotation on Linux”

Logrotate is the standard Linux tool for managing log files. It automatically archives, compresses, and deletes old logs on a schedule.

How logrotate works

  • Runs via cron (/etc/cron.daily/logrotate)
  • Reads configs from /etc/logrotate.conf and /etc/logrotate.d/
  • Applies rules to each log: rotate, compress, delete

Config for Nginx

sudo nano /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        nginx -s reopen
    endscript
}

Config for your application

sudo nano /etc/logrotate.d/myapp
/var/www/myapp/logs/*.log {
    weekly
    rotate 8
    compress
    delaycompress
    missingok
    notifempty
    create 0644 www-data www-data
}

Testing and running

# Test without actually running it
sudo logrotate -d /etc/logrotate.d/nginx

# Force run
sudo logrotate -f /etc/logrotate.conf

# Check status
sudo cat /var/lib/logrotate/status
DirectiveValue
daily/weekly/monthlyRotation frequency
rotate NKeep N archives
compressCompress with gzip
missingokDon't error if the file is missing
✅ Check free space with df -h. If /var is full — run logrotate -f /etc/logrotate.conf to force it.

Diagnosing common errors

The error error: skipping "/var/log/nginx/access.log" because parent directory has insecure permissions appears when the log directory is writable by group or by everyone — logrotate refuses to work with such paths for security reasons, so nobody can swap the log for a symlink to a system file.

error: skipping "/var/log/nginx/access.log" because parent directory has insecure permissions
(It's world writable or writable by group which is not "root")

The fix is to set the directory permissions with chmod 755 /var/log/nginx, not to widen the list of trusted groups in /etc/logrotate.conf — that would be a security hole, not a workaround. The su directive in a rule (su www-data www-data) is sometimes used instead of fixing permissions, but it only changes which user logrotate rotates the file as, and does not bypass the directory security check itself.

After configuring the rule the log still grows forever even though the rule looks correct. A common cause is that the process keeps the old file descriptor open even after rotation: without a postrotate step that signals the file to reopen (nginx -s reopen, systemctl reload myapp), the application keeps writing to the already-renamed and abandoned access.log.1 file, while the new access.log stays empty.

directory vs default: where to look for the problem

SymptomLikely causeCheck
The rule never firesA syntax error in the configlogrotate -d shows the parse error
File rotates but is not compressedThe compress directive is missing or commented outCheck the resulting config line by line
New file empty, old one keeps growingThe application did not reopen its descriptorCheck whether postrotate exists and what it does
"insecure permissions"Log directory permissions are too openls -ld /var/log/directory-name

When logrotate does not fit

  • The application already logs through journald — rotation and retention are handled by journalctl --vacuum-size/--vacuum-time, not logrotate; running both mechanisms is redundant.
  • You need centralized log aggregation from multiple servers with full-text search — logrotate only manages files locally, so aggregation needs a stack like Grafana Loki.
  • Logs must rotate by size in real time rather than once a day by cron — logrotate runs on the cron schedule instead of watching the file continuously; instant size-based rotation needs a tool like logrotate run more often, or an application with its own rotation.
  • A containerized application logs to stdout rather than a file — rotation of stdout logs is handled by the container engine itself through its logging driver (json-file with a size cap), not by logrotate inside the container.

Verifying the result

  1. Check the syntax without actually rotating: logrotate -d /etc/logrotate.d/nginx — the output must not contain any error lines.
  2. Force a rotation: logrotate -f /etc/logrotate.d/nginx and confirm a file like access.log.1.gz appears.
  3. Confirm the application keeps writing to the new file rather than the old one: tail -f /var/log/nginx/access.log must show new requests right after rotation.
  4. Check the status of the last run: cat /var/lib/logrotate/status | grep nginx must show today's date.

Freeing disk space from already-accumulated old logs is covered separately in the article on logrotate on a VPS. For systemd service logs with no files on disk, see the article on journalctl and rsyslog.

Check Existing Rules

# View all logrotate configs
ls /etc/logrotate.d/

# Test without an actual rotation
sudo logrotate --debug /etc/logrotate.conf

Create a Rule for Your Application

# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    daily           # Rotate every day
    missingok       # Don't error if the file is missing
    rotate 14       # Keep 14 archives
    compress        # Compress with gzip
    delaycompress   # Don't compress the latest archive
    notifempty      # Don't rotate empty files
    create 0640 www-data www-data  # Permissions for the new file
    postrotate
        systemctl reload nginx > /dev/null 2>&1 || true
    endscript
}

Logrotate Parameters

ParameterDescription
daily/weekly/monthlyRotation period
rotate NNumber of archives
size 100MRotate by size
compressCompress with gzip
maxage 30Delete archives older than 30 days
sharedscriptspostrotate runs once

Forcing Rotation Right Now

sudo logrotate --force /etc/logrotate.d/myapp
✅ Logrotate runs daily via cron (/etc/cron.daily/logrotate). Usually nothing extra needs to be configured.

Freeing Up Space From Old Logs

# Find large log files
sudo find /var/log -name "*.log" -size +100M

# Clear a file without deleting it (the process keeps writing)
sudo truncate -s 0 /var/log/nginx/access.log

# Or via journald
sudo journalctl --vacuum-size=500M

du vs df: finding where the space went

SymptomDiagnostic commandWhat it shows
df sees less free space than dulsof +L1Deleted but still open files
Unclear which directory is heaviestdu -h --max-depth=1 /var/log | sort -rhThe size of each log subdirectory
Logs grow faster than expectedwatch -n 5 'du -sh /var/log/nginx'The growth rate in real time

When regular rotation is not enough

  • The disk is already full right now, not just at risk of filling up later — logrotate --force will not help instantly if open deleted files are the cause; the processes holding them must be restarted first.
  • Logs accumulate faster than the disk can hold even with compression — logs need to move to a separate volume, object storage, or a centralized system like Grafana Loki, rather than more aggressive rotation on the same disk.
  • You need to find the specific cause of abnormal log growth, not just free up space — that is a log-analysis task for errors and suspicious traffic, not a rotation task.
Was this article helpful?
← Back to Knowledge Base Ask Support