Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Configure logrotate to manage log rotation on Linux

By updated

Improve this page

logrotate automatically archives, compresses and deletes old log files (/var/log/*.log). While journald manages the binary journal of systemd, logrotate takes care of the text files written by services straight into /var/log/ (nginx, Apache, MySQL, business applications). Without rotation, these files grow until they fill the disk.

What you will learn

  • Understand the layout of logrotate (logrotate.conf + /etc/logrotate.d/)
  • Read and write a rotation policy for a service
  • Use the compression, retention and permissions directives
  • Run post-rotation scripts (reloading a service)
  • Test a configuration safely and force a manual rotation
  • Know the difference between logrotate and journald

In which context?

logrotate is essential as soon as a service writes text files, and the cases below are the ones that fill a disk in production:

  • nginx or Apache produce access.log and error.log files that can reach several GB a day on a busy site
  • A business application writes its logs in /var/log/myapp/ without any built-in rotation
  • The /var disk fills up and triggers alerts, old logs must be compressed and purged
  • Compliance requires keeping logs 90 days, but not forever

logrotate and journald, two complementary roles

The two tools do not compete: each one handles a different kind of log, and knowing which one owns a file tells you where to look for its retention settings.

ToolHandlesStorageConfiguration
journaldBinary systemd journal/var/log/journal/journald.conf
logrotateClassic text files/var/log/*.loglogrotate.conf + /etc/logrotate.d/

Both coexist on a modern system. journald captures the stdout/stderr streams of systemd services, syslog messages and kernel logs. logrotate manages the text files that services write directly to disk.

Installation

logrotate is preinstalled on most distributions. Otherwise:

Terminal window
sudo apt update && sudo apt install logrotate

Then check the installed version. It matters: directives such as maxsize or hourly do not exist in old versions. An unknown directive does not block the rest of the configuration, logrotate prints unknown option '...' -- ignoring line (prefixed with warning: in 3.22, error: in 3.21, measured) and goes on, which is worse than a plain error since the policy then applies without the option you thought you had set.

Terminal window
# Check the version
logrotate --version
logrotate 3.21.0

This is the version of Ubuntu 24.04; Debian 13 and AlmaLinux 10 ship 3.22.0 (measured on 2026-09-30).

Configuration layout

The configuration of logrotate rests on two levels, and a per-service file only has to state what differs from the global defaults:

FileRole
/etc/logrotate.confDefault global rules (frequency, retention)
/etc/logrotate.d/One file per service that overrides the global rules

Global configuration

This file describes no log in particular: it sets the default values that every file of /etc/logrotate.d/ inherits. Look at the commented lines first, they tell you what is not active. On Ubuntu 24.04, dateext and compress ship disabled: believing otherwise makes you search for a long time why the archives come out as an uncompressed .1. The file below is the one of Ubuntu 24.04; the two other reference distributions differ from it, see right after the output.

Terminal window
cat /etc/logrotate.conf
# see "man logrotate" for details
# global options do not affect preceding include directives
# rotate log files weekly
weekly
# use the adm group by default, since this is the owning group
# of /var/log/.
su root adm
# keep 4 weeks worth of backlogs
rotate 4
# create new (empty) log files after rotating old ones
create
# use date as a suffix of the rotated file
#dateext
# uncomment this if you want your log files compressed
#compress
# packages drop log rotation information into this directory
include /etc/logrotate.d
DirectiveEffect
weeklyWeekly rotation by default
rotate 4Keep 4 archived files
createCreate a new file after rotation
dateextSuffix the archives with the date (-20260628)
compressCompress the archives (gzip)
include /etc/logrotate.dLoad the per-service configurations

The three distributions do not ship the same active values, measured on 2026-09-30: Debian 13 has no su root adm line, and AlmaLinux 10 turns on dateext, so its archives carry the date (-20260930) instead of a number. Read the file of your own machine rather than the one of a tutorial.

The include /etc/logrotate.d line must stay last: global options declared after it do not apply to the files already included. The file shipped by the distribution reminds you of it in a comment, on its third line.

Per-service configuration

Each service places its file in /etc/logrotate.d/, and this file belongs to the package: a manual change will be flagged as a conflict at the next update. Here is the file shipped by the nginx package on Debian 13, copied as is.

Terminal window
cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}

Two details are worth a look. The prerotate block runs the scripts dropped by other packages into httpd-prerotate, which lets a statistics tool, for example, analyse the log before it is archived. And invoke-rc.d nginx rotate triggers the rotate action of the init script, whose rotate_logs function sends the USR1 signal to nginx (start-stop-daemon --stop --signal USR1): it is not a full reload, just a reopening of the log files.

Essential directives

Frequency

These directives trigger nothing by themselves: they describe a condition checked when logrotate runs, that is once a day through the systemd timer. Writing daily therefore does not mean "at midnight sharp", but "at most once a day, when it runs". The size line is apart: the manual says it is mutually exclusive with the time criteria, and that the last directive written wins.

DirectiveRotation
dailyEvery day
weeklyEvery week
monthlyEvery month
size 100MWhen the file exceeds 100 MB

Retention

The two directives below answer different questions and combine very well. rotate limits the number of archives, which bounds the disk space; maxage limits their age, which answers a compliance requirement ("do not keep logs beyond N days"). One limit to know: the manual states that the age is checked only when the log rotates. A file that notifempty skips because it stayed empty no longer rotates, and so keeps its archives, maxage included: for these dormant logs, only a scheduled purge (a find -mtime in a timer) bounds the age.

DirectiveEffect
rotate NKeep N archives
maxage NDelete archives older than N days

Compression

Compression is the most profitable lever on text logs: an access.log often shrinks by a factor of 10 or more. The directive to understand is delaycompress, which postpones compression by one cycle: the manual says it serves when a program cannot be told to close its file and so keeps writing for a few moments into the old one. It only has an effect combined with compress.

DirectiveEffect
compressCompress the archives (gzip)
delaycompressPostpone compression by one cycle (useful if a service still writes)
compresscmd /usr/bin/bzip2Use bzip2 instead of gzip
nocompressDisable compression

Changing the command with compresscmd also changes the extension: measured on logrotate 3.21 and 3.22, compresscmd /usr/bin/bzip2 produces a .1.bz2 archive, really compressed with bzip2, without any other directive. The manual says so: the compressext extension "follows that of the configured compression command" by default. compressext therefore only serves to impose another extension, and uncompresscmd must follow the same command so that the reading tools can open the archives.

Creating the new file

The create directive sets the permissions and the owner of the empty file logrotate puts in place of the rotated one:

create 0640 root adm

It creates the new file with permissions 0640, owner root, group adm. The service can keep writing immediately.

Security

These three directives avoid the two most frequent failures in production: a missing file that makes logrotate exit with an error, and a directory whose owner is not root. su user group answers the second case and its absence is the exact cause of the message error: skipping "..." because parent directory has insecure permissions, which logrotate completes itself with "Set "su" directive in config file" (measured on the three distributions). Treat it as mandatory as soon as the logs live outside /var/log.

DirectiveEffect
missingokDo not report an error if the file does not exist
notifemptyDo not rotate an empty file
su root admRun the rotation as this user/group (required if the directory has special permissions)

Pre- and post-rotation scripts

The prerotate and postrotate blocks run shell commands before and after the rotation, typically to make the service reopen its log files:

/var/log/myapp/*.log {
daily
rotate 7
compress
missingok
notifempty
create 0640 myapp myapp
sharedscripts
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}
BlockWhen
prerotate ... endscriptBefore the rotation
postrotate ... endscriptAfter the rotation
firstaction ... endscriptBefore the first rotation (only once)
lastaction ... endscriptAfter the last rotation (only once)

sharedscripts guarantees that the script runs only once, even if several files match the pattern, instead of reloading the service once per file.

Test and run

Test without applying (debug mode)

The -d flag is the first command to run on any new or changed policy:

Terminal window
sudo logrotate -d /etc/logrotate.d/nginx

The -d flag simulates the rotation and shows what would be done, without changing any file; logrotate --help describes it as "Don't do anything, just test and print debug messages". Use it systematically before putting a new configuration into production.

In the output, look for three things: the <file>:<n> unknown option line (prefixed with warning: or error: depending on the version) that flags a misspelled directive, the considering log lines that show the files retained, and the conclusion per file (log needs rotating or log does not need rotating). A pattern that matches nothing stays unexpanded: considering log /var/log/myapp/*.log, glob included, without a conclusion. On a file logrotate has never seen, the conclusion is log does not need rotating (log has already been rotated): it records this first pass as its starting point, which is normal.

Passed alone, a file of /etc/logrotate.d/ is read without /etc/logrotate.conf: the global options do not apply. Measured on AlmaLinux 10, whose global configuration turns on dateext, logrotate -f /etc/logrotate.d/<file> produces a numbered .1 archive and not a dated one. To test in real conditions, pass /etc/logrotate.conf.

Force the rotation

-f ignores the frequency and size conditions, and immediately rotates everything that matches the configuration file passed as argument. It is the validation command of a new policy, not an operations command: each run consumes one notch of the rotate counter, so chaining it five times erases your five oldest archives.

Terminal window
# Force the rotation of every file
sudo logrotate -f /etc/logrotate.conf
# Force the rotation of a single service
sudo logrotate -f /etc/logrotate.d/nginx

Verbose mode

The -v flag shows the details of each rotation performed, useful to follow a forced rotation step by step:

Terminal window
sudo logrotate -v /etc/logrotate.conf

Automatic triggering

On modern distributions, logrotate is triggered by a systemd timer (and no longer by cron). Here is the one of Ubuntu 24.04:

Terminal window
systemctl cat logrotate.timer
[Unit]
Description=Daily rotation of log files
Documentation=man:logrotate(8) man:logrotate.conf(5)
[Timer]
OnCalendar=daily
AccuracySec=1h
Persistent=true
[Install]
WantedBy=timers.target

Two directives explain the behaviour observed on a server. AccuracySec=1h gives systemd a one-hour margin to group this wake-up with others; Debian 13 and AlmaLinux 10 carry RandomizedDelaySec=1h instead, a random delay that spreads the rotations of a fleet of machines (measured on 2026-09-30). In both cases, the exact time varies from one day and one server to the next, which is normal. Persistent=true catches up on the missed run if the machine was off at the scheduled time, which keeps a server switched on intermittently from piling up logs that never rotate.

Terminal window
# Check that the timer is active
systemctl status logrotate.timer
# See the next deadline and the last run
systemctl list-timers logrotate.timer

Write a complete policy

Here is an example of a policy for a business application, from the file creation to the validation of the first rotation:

  1. Create the configuration file

    Terminal window
    sudo nano /etc/logrotate.d/myapp
  2. Write the policy

    /var/log/myapp/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp myapp
    su myapp myapp
    sharedscripts
    postrotate
    systemctl reload myapp > /dev/null 2>&1 || true
    endscript
    }
  3. Test the configuration

    Terminal window
    sudo logrotate -d /etc/logrotate.d/myapp

    Check: the output must show the files that would be rotated, without a syntax error.

  4. Force a first rotation to validate

    Terminal window
    sudo logrotate -f /etc/logrotate.d/myapp
    ls -la /var/log/myapp/

    Check: a .1 file must appear, uncompressed: delaycompress postpones compression to the next cycle. Measured: a second -f produces .1 and .2.gz.

Troubleshooting

The symptoms below are told apart by a simple criterion: the rotation did not happen at all (first lines) or it happened but its result is surprising (next lines). Always start by checking the state of the timer, because a rotation that never runs produces exactly the same symptoms as an invalid configuration.

SymptomProbable causeSolution
Logs never rotatedTimer disabledsystemctl enable --now logrotate.timer
error: skipping "..." because parent directory has insecure permissionsParent directory writable by a group other than rootAdd su <user> <group> to the block
unknown option '...' -- ignoring lineTypo, or directive missing from this versionFix it, and compare with logrotate --version
Logs rotated but the service writes into the old oneNo postrotateAdd a postrotate block with systemctl reload
error: ... duplicate log entrySame file in 2 configsCheck /etc/logrotate.d/ for duplicates
Uncompressed archivescompress missingAdd compress to the configuration
A .1 file but no .1.gzdelaycompress activeNormal, compression applies at the next cycle
Terminal window
# Quick diagnosis
systemctl status logrotate.timer # Timer active?
sudo logrotate -d /etc/logrotate.conf # Config errors?
ls -la /var/log/nginx/ # Effective rotation?
journalctl -u logrotate.service # Last run?

Best practices

The first two lines will save you incidents, the next ones will save you redoing the work. If you keep only one, keep the last: logrotate bounds the retention, not the throughput. A service that writes 20 GB a day will fill /var well before the daily run of the timer, and only a cap on the application side or an aggressive size will change anything.

  • Always test with -d before changing a policy in production
  • Use delaycompress for services that may still write into the old file for a few seconds
  • Add sharedscripts + postrotate to reload the service after rotation
  • Set restrictive permissions with create, logs often contain sensitive information (IPs, errors, paths)
  • Configure maxage alongside rotate, to delete at each rotation the archives that are too old even if the counter is not reached
  • Separate the configurations in /etc/logrotate.d/, one file per service, never everything in logrotate.conf
  • Monitor disk space, logrotate does not solve the problem if logs grow faster than the rotation

Key points

  • logrotate manages text log files, journald manages the binary journal of systemd. The two are complementary
  • The global configuration lives in /etc/logrotate.conf, the per-service configurations in /etc/logrotate.d/
  • The key directives: daily/weekly, rotate N, compress, delaycompress, create, postrotate
  • logrotate -d simulates the rotation without changing anything, to use before each change; passed alone, a file of logrotate.d/ ignores the global options
  • logrotate -f forces the rotation, useful to validate a new configuration
  • The postrotate block reloads the service so that it writes into the new file
  • On modern systems, logrotate is triggered by a systemd timer (logrotate.timer), no longer by cron

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn