Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 9 min read

Configuring logrotate to Stop Runaway Log Growth

Write and debug logrotate configs that keep Linux log directories from filling the disk, using AI as a fast junior pair to draft and test rotation rules.

  • #linux
  • #logrotate
  • #logging
  • #disk

The first time a disk-full alert woke me at 3 a.m., the culprit was not a runaway database or a misconfigured backup. It was a single application that had been writing a 40 GB log file for three weeks because nobody had ever pointed logrotate at it. I spent the next twenty minutes truncating a file I could not even open in a text editor, then promised myself I would never let an unmanaged log directory bite me again. logrotate is one of those unglamorous tools that quietly prevents outages, and getting its configuration right is mostly about understanding a handful of directives and testing them before you trust them. This post walks through that configuration end to end, including the sharp edges that the man page glosses over.

How logrotate Actually Runs

A common misconception is that logrotate is a daemon constantly watching your files. It is not. It is a batch program that runs, does its work, and exits. On most modern distributions it is triggered by a systemd timer rather than the old /etc/cron.daily entry. You can confirm what is driving it on your box:

systemctl list-timers logrotate.timer
systemctl cat logrotate.timer

The timer typically fires once a day and calls logrotate /etc/logrotate.conf. That single invocation reads the main config and every drop-in file in /etc/logrotate.d/, then decides which logs are due for rotation. Because it only runs once a day by default, a size-based rule will not rotate the instant a file crosses the threshold; it rotates at the next scheduled run when the size is exceeded. If you genuinely need sub-daily rotation for a noisy service, change the timer’s OnCalendar to run hourly rather than reaching for a hand-rolled cron job.

The Global Config and Drop-In Pattern

/etc/logrotate.conf holds the defaults and a trailing include /etc/logrotate.d line. Resist the urge to edit logs directly into the main file. Instead, give each application its own drop-in. A minimal /etc/logrotate.conf looks like this:

weekly
rotate 4
create
dateext
include /etc/logrotate.d

Then a per-app file at /etc/logrotate.d/myapp:

/var/log/myapp/*.log {
    daily
    rotate 14
    size 100M
    missingok
    notifempty
    compress
    delaycompress
    create 0640 myapp myapp
}

Each directive earns its place. rotate 14 keeps fourteen old copies before deleting the oldest. daily plus size 100M means rotate at the daily run, but only if the file has grown past 100 MB, so quiet days do not churn through your retention window. missingok stops logrotate from erroring when a log does not exist yet. notifempty skips rotation for empty files. compress gzips rotated logs, and delaycompress defers compression by one cycle so the most recent rotated file stays readable for tailing and tooling that may still hold a handle on it. create 0640 myapp myapp recreates the live log with explicit permissions and ownership immediately after rotation.

Pro Tip: Add dateext so rotated files are named with a date suffix like myapp.log-20260617 instead of the numeric myapp.log.1 shuffle. Date-stamped names are far easier to correlate with incidents and they avoid the rename churn that confuses some log shippers.

create + postrotate vs. copytruncate

This is the decision that trips people up, so it deserves its own section. There are two ways logrotate can hand a fresh file to a running process.

The default and preferred approach is create: logrotate renames the current log out of the way, creates a new empty file, and then signals the process to reopen its file handle. For something like nginx you wire this up with a postrotate script:

/var/log/nginx/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
    endscript
}

sharedscripts matters here: without it, the postrotate block runs once per matched file. With it, the block runs a single time after all matching logs are rotated, so nginx gets exactly one USR1 signal instead of one per access and error log.

The alternative is copytruncate, which copies the current log to the rotated name and then truncates the original in place to zero bytes. You reach for this when the application has no mechanism to reopen its log file and you cannot signal it:

/var/log/legacyapp/app.log {
    daily
    rotate 7
    compress
    copytruncate
    missingok
    notifempty
}

The caveat that bites people: copytruncate has an inherent race. Between the copy and the truncate, any lines the application writes are lost, because they land in the region that gets truncated away. For a busy log this can mean dropped entries on every rotation. Use create plus a reopen signal whenever the application supports it, and treat copytruncate as the compromise for software you cannot otherwise control.

Testing Before You Trust

Never deploy a logrotate rule and walk away. The debug flag does a dry run, parsing your config and printing exactly what it would do without touching a single file:

logrotate -d /etc/logrotate.d/myapp

Read that output carefully. It tells you which files matched the glob, whether the size or age threshold was met, and which scripts would fire. If it says “log does not need rotating,” that is the engine doing its job, not an error.

When you want to actually exercise the rotation immediately rather than wait for the timer, force it:

logrotate -f /etc/logrotate.d/myapp

I treat logrotate -d the way I treat a terraform plan: it is the moment to catch a glob that is too greedy or a postrotate that references the wrong PID file. This is also exactly the kind of task where an AI assistant earns its keep. I will paste a log sample and the service’s behavior into a tool like Claude or ChatGPT and ask it to draft the drop-in, then immediately run the result through logrotate -d myself. The AI is a fast junior engineer: it produces a credible first draft in seconds, but it does not know that your legacy daemon ignores SIGHUP, and it will happily suggest copytruncate without mentioning the race. Keep a human in the loop on every config that touches production logging, and never hand the AI live server access or production credentials to “just fix it” for you.

The State File

logrotate is stateless between runs except for one file: /var/lib/logrotate/logrotate.status. This is where it records the last rotation time for every log it manages. Inspecting it answers the perennial “why didn’t this rotate?” question:

cat /var/lib/logrotate/logrotate.status

Each line pairs a log path with the timestamp of its last rotation. If a daily rule has not fired, this file shows you whether logrotate thinks it already rotated recently. If you are testing and want a clean slate, you can point logrotate at an alternate state file with -s rather than editing the real one:

logrotate -d -s /tmp/test.status /etc/logrotate.d/myapp

That keeps your experiments from polluting the production state and accidentally suppressing a real rotation.

Pro Tip: If a log mysteriously stops rotating after you fix a broken config, check the status file. A failed run earlier in the day may have stamped a recent timestamp, and logrotate will wait a full cycle before trying again. You can force it past this with logrotate -f.

Catching Growth Before the Disk Alert

The whole point is to stay ahead of the disk. A quick audit of where your space is going:

du -sh /var/log/* | sort -rh | head

Pair this with monitoring so a stuck log triggers a warning long before the volume hits 100 percent. If you have a monitoring-alerts workflow wired up, a simple disk-usage threshold on /var/log is the cheapest insurance you can buy. And when something does slip through, having a documented incident-response runbook means the on-call engineer is truncating the right file instead of guessing at 3 a.m.

For drafting the configs themselves, I keep a small library of vetted logrotate templates and reusable prompts. Browse the prompts collection or grab a curated prompt pack if you want a head start, and lean on the broader Linux admin guides for the surrounding tooling.

Conclusion

logrotate is not complicated, but it is unforgiving of assumptions. The directives that matter most are the ones that decide retention, compression, and how the live file is handed back to the process. Draft your config, run logrotate -d until the dry run says exactly what you intend, force a single real rotation to confirm, and let an AI assistant accelerate the boilerplate while you stay the human reviewer on anything touching production. Do that, and the next disk-full page you get will be for something genuinely interesting, not a log file nobody remembered to rotate.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.