Ubuntu 24.04: logrotate isn’t rotating — the one config mistake that keeps biting people

Was this helpful?

Nothing says “Friday evening” like a disk filling up because a log file decided it’s actually a database now. You check /etc/logrotate.d/, you see a stanza for the service, and yet the file keeps growing. No rotations, no compress, no mercy.

On Ubuntu 24.04, the most common reason logrotate “isn’t rotating” is not systemd, not a kernel bug, not cosmic rays. It’s a single config mistake: you forgot to specify a rotation trigger (daily/weekly/monthly/hourly or size) and you assumed some global default would save you. It won’t. Logrotate will read your stanza, shrug politely, and do nothing—forever.

The one mistake: no trigger, no rotation

Logrotate doesn’t rotate “because a file exists” or “because I wrote a config file.” It rotates when a stanza tells it when to rotate. That “when” is a rotation trigger:

  • daily, weekly, monthly, yearly, or (on many distros) hourly
  • size (and friends like minsize, maxsize)

If your per-service file in /etc/logrotate.d/ doesn’t specify a trigger, you’re relying on inheritance. And this is where people get bitten: Ubuntu’s global defaults may not provide a schedule in a way that applies to your stanza (or the file isn’t being read, or you’re overriding it unintentionally). Net result: logrotate runs, sees no reason to rotate, updates nothing, and exits 0 like it did you a favor.

What the broken config looks like

This is the classic “looks fine in a code review, fails in production” stanza. Notice what’s missing:

cr0x@server:~$ sudo sed -n '1,120p' /etc/logrotate.d/myapp
/var/log/myapp/myapp.log {
  rotate 7
  compress
  missingok
  notifempty
  create 0640 myapp adm
  postrotate
    systemctl kill -s HUP myapp.service
  endscript
}

That config has rotate 7, which only says “keep seven old logs” if rotation happens. It does not say when rotation happens. If you don’t specify daily (or similar) or size, logrotate can’t make a rotation decision.

What the fixed config looks like

Add an explicit trigger. Pick one. Don’t make the reader guess.

cr0x@server:~$ sudo sed -n '1,120p' /etc/logrotate.d/myapp
/var/log/myapp/myapp.log {
  daily
  rotate 7
  compress
  missingok
  notifempty
  create 0640 myapp adm
  postrotate
    systemctl kill -s HUP myapp.service
  endscript
}

If you prefer size-based rotation (often better for chatty apps):

cr0x@server:~$ sudo sed -n '1,120p' /etc/logrotate.d/myapp
/var/log/myapp/myapp.log {
  size 200M
  rotate 10
  compress
  delaycompress
  missingok
  notifempty
  create 0640 myapp adm
}

Make the trigger explicit even if you think it’s “obvious.” Production is where obvious assumptions go to get audited.

Fast diagnosis playbook (first/second/third)

When logrotate “isn’t rotating,” you want to locate the bottleneck fast: is it scheduling, parsing, eligibility, permissions, or postrotate failure?

First: confirm logrotate is actually being invoked

  • Check the systemd timer/service status and last run time.
  • Check journal logs for logrotate runs and errors.

Second: run logrotate in debug mode for the specific config

  • Use -d (debug) and -v (verbose) to see the decision tree.
  • Look for lines like “log does not need rotating” and why.

Third: check the state file and the trigger logic

  • Inspect /var/lib/logrotate/status (or distro variant) to see what logrotate thinks it last rotated.
  • Validate your stanza contains a trigger (daily/weekly/size, etc.).
  • Confirm file paths match reality (including wildcards) and permissions allow rotation.

If you do only one thing: run logrotate with -d -v and read it like a flight recorder. It’s usually embarrassingly honest.

How logrotate actually decides to rotate (Ubuntu 24.04 reality)

Logrotate’s job is simple: for each log file, decide whether rotation is needed; if yes, rename/truncate/copy, optionally compress, and run scripts. The devil is the state file and the triggers.

Ubuntu 24.04 scheduling: systemd timers, not cron vibes

On modern Ubuntu, logrotate is typically launched via a systemd timer (not a classic cron.daily job). That changes where you look when you’re debugging “it never runs.” It also changes how “missed” runs behave: timers can catch up depending on configuration.

The state file is the memory logrotate trusts

Logrotate is not a real-time daemon. It runs, makes decisions, and exits. To avoid rotating everything every time, it stores last-rotation timestamps in a state file (commonly /var/lib/logrotate/status). If that file says a log rotated “today,” a daily stanza won’t rotate again just because you’re anxious.

Triggers are not optional

Logrotate doesn’t infer a rotation schedule from rotate N, compress, or create. Those are actions. Triggers are separate. Missing trigger is the silent failure mode because it’s not “invalid syntax.” It’s just “no condition satisfied.”

Paraphrased idea from Richard Cook: “Success hides the system’s sharp edges; failure reveals how work is really done.” That’s logrotate debugging in one sentence.

Joke #1: Logrotate is like a pager rotation: if you never define the schedule, someone still suffers—just not the software.

Practical tasks: commands, outputs, decisions (12+)

Below are concrete tasks I actually use in incident response. Each includes: the command, what output means, and the decision you make from it.

Task 1: Confirm the timer exists and is enabled

cr0x@server:~$ systemctl status logrotate.timer
● logrotate.timer - Daily rotation of log files
     Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled; preset: enabled)
     Active: active (waiting) since Sat 2025-12-28 07:10:22 UTC; 2h 14min ago
    Trigger: Sun 2025-12-29 00:00:00 UTC; 14h left
   Triggers: ● logrotate.service

Meaning: If it’s enabled and “Active (waiting),” scheduling is likely fine.

Decision: If it’s disabled/missing, fix scheduling first. If enabled, move to service execution/journal checks.

Task 2: Confirm the last service run and exit code

cr0x@server:~$ systemctl status logrotate.service
● logrotate.service - Rotate log files
     Loaded: loaded (/usr/lib/systemd/system/logrotate.service; static)
     Active: inactive (dead) since Sat 2025-12-28 00:00:04 UTC; 9h ago
    Process: 1042 ExecStart=/usr/sbin/logrotate /etc/logrotate.conf (code=exited, status=0/SUCCESS)

Meaning: Status 0 means logrotate didn’t crash; it may still have rotated nothing.

Decision: If status is non-zero, inspect logs; if success, proceed to debug mode for why it skipped rotation.

Task 3: Inspect journal for logrotate messages

cr0x@server:~$ journalctl -u logrotate.service -n 50 --no-pager
Dec 28 00:00:01 server systemd[1]: Starting logrotate.service - Rotate log files...
Dec 28 00:00:04 server systemd[1]: logrotate.service: Deactivated successfully.
Dec 28 00:00:04 server systemd[1]: Finished logrotate.service - Rotate log files.

Meaning: No errors here, but also no detail. That’s normal; logrotate logs to stdout only when verbose/debug is used.

Decision: Run manual debug next.

Task 4: Dry-run logrotate with verbose + debug

cr0x@server:~$ sudo logrotate -d -v /etc/logrotate.conf
reading config file /etc/logrotate.conf
including /etc/logrotate.d
reading config file /etc/logrotate.d/myapp
Handling 1 logs
rotating pattern: /var/log/myapp/myapp.log  after 1 days (7 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/myapp/myapp.log
  log does not need rotating (log has been already rotated)

Meaning: This tells you what logrotate thinks the trigger is (“after 1 days”) and why it’s skipping (“already rotated”).

Decision: If the trigger line is missing or weird, inspect the stanza. If “already rotated,” check the state file.

Task 5: Run debug for a single file config (tight scope)

cr0x@server:~$ sudo logrotate -d -v /etc/logrotate.d/myapp
reading config file /etc/logrotate.d/myapp
error: /etc/logrotate.d/myapp:2 unknown option 'rotato'

Meaning: Per-file debug quickly catches typos and parsing errors.

Decision: Fix syntax errors before chasing runtime behavior.

Task 6: Validate the stanza includes a trigger (the “one mistake” check)

cr0x@server:~$ sudo egrep -n 'daily|weekly|monthly|yearly|hourly|size|minsize|maxsize' /etc/logrotate.d/myapp || echo "NO TRIGGER FOUND"
NO TRIGGER FOUND

Meaning: No trigger keywords found. This is the classic “rotate but never rotates” misconfig.

Decision: Add daily or size explicitly and rerun debug.

Task 7: Inspect the state file entry for your log

cr0x@server:~$ sudo grep -F '/var/log/myapp/myapp.log' /var/lib/logrotate/status
"/var/log/myapp/myapp.log" 2025-12-28-0:0:0

Meaning: Logrotate believes it rotated at midnight today. If your expectation is “it should rotate now,” your expectation is the bug.

Decision: If the timestamp looks wrong, investigate time changes, state corruption, or duplicate paths. Don’t casually delete the state file in production.

Task 8: Confirm the actual file size and modification time

cr0x@server:~$ ls -lh --time-style=long-iso /var/log/myapp/myapp.log
-rw-r----- 1 myapp adm 12G 2025-12-28 09:19 /var/log/myapp/myapp.log

Meaning: File is huge and still being written. If you use time-based rotation only, you might be “correct” but operationally doomed.

Decision: Consider size or maxsize in addition to time triggers.

Task 9: Check who owns the log and directory permissions

cr0x@server:~$ namei -l /var/log/myapp/myapp.log
f: /var/log/myapp/myapp.log
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxrwxr-x root syslog log
drwxr-x--- myapp adm myapp
-rw-r----- myapp adm myapp.log

Meaning: Log directory is not accessible to “others”; logrotate runs as root (usually), so that’s fine. But if you configured su incorrectly, permissions can block rotation.

Decision: If permissions don’t allow renaming/creating, fix ownership/mode or use the correct su user group directive.

Task 10: Detect postrotate script failures (the hidden reason nothing changes)

cr0x@server:~$ sudo logrotate -v /etc/logrotate.d/myapp
reading config file /etc/logrotate.d/myapp
rotating pattern: /var/log/myapp/myapp.log  after 1 days (7 rotations)
considering log /var/log/myapp/myapp.log
  log needs rotating
rotating log /var/log/myapp/myapp.log, log->rotateCount is 7
renaming /var/log/myapp/myapp.log to /var/log/myapp/myapp.log.1
creating new /var/log/myapp/myapp.log mode = 0640 uid = 123 gid = 4
running postrotate script
error: error running shared postrotate script for '/var/log/myapp/myapp.log '

Meaning: The rotation may have happened, but your signal/reload step failed. Some apps keep writing to the old inode; others need HUP to reopen logs.

Decision: Fix postrotate; otherwise you’ll “rotate” and still fill disk because the process writes to an unlinked file descriptor.

Task 11: Check for the dreaded “deleted but still open” situation

cr0x@server:~$ sudo lsof +L1 | head
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NLINK     NODE NAME
myapp    2210 myapp   7w   REG  252,0 12884901888     0  393502 /var/log/myapp/myapp.log (deleted)

Meaning: The file is deleted/rotated, but the process still writes to the old file descriptor. Disk usage won’t drop.

Decision: Fix your reopen strategy: HUP, reload, or use copytruncate as a last resort (with eyes open).

Task 12: Verify timer actually fired when you think it did

cr0x@server:~$ systemctl list-timers --all | grep -F logrotate
Sun 2025-12-29 00:00:00 UTC 14h left Sat 2025-12-28 00:00:01 UTC 9h ago logrotate.timer logrotate.service

Meaning: Last run was at midnight; next at midnight. If your log exploded at 09:00, time-based daily rotation won’t save you.

Decision: Add size thresholds or increase run frequency (carefully).

Task 13: Force a one-time rotation (safely) to validate mechanics

cr0x@server:~$ sudo logrotate -f -v /etc/logrotate.d/myapp
reading config file /etc/logrotate.d/myapp
rotating pattern: /var/log/myapp/myapp.log  forced from command line (7 rotations)
considering log /var/log/myapp/myapp.log
  log needs rotating
renaming /var/log/myapp/myapp.log to /var/log/myapp/myapp.log.1
creating new /var/log/myapp/myapp.log mode = 0640 uid = 123 gid = 4

Meaning: Mechanics work: rename/create/permissions are good.

Decision: If forced rotation works but scheduled rotation doesn’t, it’s a trigger/state/schedule issue, not permissions.

Task 14: Check include ordering and whether your file is even read

cr0x@server:~$ sudo grep -nE '^(include|#include)|/etc/logrotate\.d' /etc/logrotate.conf
12:include /etc/logrotate.d

Meaning: If the include is missing, nothing in /etc/logrotate.d matters.

Decision: Restore the include line if someone “simplified” the global config.

Task 15: Test config parse without running rotations (sanity check)

cr0x@server:~$ sudo logrotate -d /etc/logrotate.conf | tail -n 20
considering log /var/log/myapp/myapp.log
  Now: 2025-12-28 09:24
  Last rotated at 2025-12-28 00:00
  log does not need rotating

Meaning: Debug mode shows “Now” and “Last rotated.” If those are nonsense, follow the time/state trail.

Decision: If “Last rotated” is in the future, you likely have time skew, a restored VM snapshot, or a copied state file.

Common mistakes: symptoms → root cause → fix

1) “I set rotate 30 but it never rotates”

Symptom: File grows forever; logrotate reports success; no rotated files exist.

Root cause: No trigger directive (daily/weekly/size).

Fix: Add a trigger explicitly. Prefer size for high-volume logs; keep time-based for retention alignment.

2) “Logrotate runs, but says ‘log does not need rotating’ while the file is huge”

Symptom: A 20G log file; debug says not rotating.

Root cause: You configured only daily and it already rotated today; or the state file says it did.

Fix: Add maxsize or size. Or rotate more frequently. Don’t fight the state file; design around it.

3) “Rotated files exist, but disk usage never drops”

Symptom: You see myapp.log.1, yet df stays ugly.

Root cause: Process still holds open the old (deleted) inode; logs go to (deleted) file descriptor.

Fix: Ensure the process reopens logs (HUP/reload) or use copytruncate if the app can’t reopen (accepting loss/race risk).

4) “Manual logrotate -f works, scheduled rotation doesn’t”

Symptom: Forced rotation rotates; daily timer does nothing.

Root cause: Schedule not firing, timer disabled, include missing, or state says it already rotated.

Fix: Check systemctl list-timers, verify /etc/logrotate.conf includes /etc/logrotate.d, inspect /var/lib/logrotate/status.

5) “Rotation errors: permission denied”

Symptom: Debug/verbose shows rename/create failing.

Root cause: Wrong owner/mode; log directory not writable; using su incorrectly; or logs on a weird mount with restrictions.

Fix: Fix filesystem permissions. If using su, ensure user/group match the log file owner and directory permissions allow rotation.

6) “It rotates, but the app stops logging”

Symptom: After rotation, log file stays empty and app keeps running.

Root cause: App continues writing to old fd; new file exists but is unused; postrotate doesn’t signal correctly.

Fix: Use correct postrotate action: HUP, reload, or service-specific reopen command. Validate with lsof.

7) “I used wildcards and nothing happens”

Symptom: Stanza references /var/log/myapp/*.log; no rotations occur.

Root cause: Pattern matches nothing; permissions prevent glob expansion in the directory; or logs are in a different path than you think.

Fix: Confirm matches with ls. Use explicit paths for critical logs.

8) “After an image restore, logrotate won’t rotate for days”

Symptom: Debug says last rotated in the future.

Root cause: State file copied from another machine/time; VM snapshot restored; clock changes.

Fix: Correct time first. Then adjust state entry carefully (edit just the affected line), or force rotate once with -f and confirm state updates.

Three corporate mini-stories from the trenches

Incident #1: the wrong assumption (“rotate 14 means every 14 days, right?”)

A mid-sized SaaS company had a neat little internal service that emitted JSON logs. It ran fine for months because nobody looked at the node’s root filesystem utilization unless something paged. The service got a feature that doubled log volume—nothing dramatic, just more context fields.

Two weeks later, an on-call engineer saw disk usage on a production node at 97%. They found /var/log/internal/worker.log at tens of gigabytes. There was a logrotate stanza with rotate 14, compress, create, and notifempty. It looked “configured.” Everyone assumed “rotate 14” means “rotate every 14 days.” It doesn’t.

The timer was running. Logrotate exited 0. No rotations. The configuration had no daily, no weekly, no size. It was a policy without a trigger—like writing “keep 14 backups” without ever running the backup.

They added daily and maxsize 500M, forced a rotation once to get breathing room, and then fixed their monitoring thresholds to alert on log volume growth before the filesystem panic.

The postmortem was polite but sharp: “We assumed logrotate had a default schedule. We didn’t test rotation behavior in staging with realistic log volume.” That’s the whole story, and it repeats everywhere.

Incident #2: the optimization that backfired (copytruncate everywhere)

A regulated enterprise had a legacy Java app that didn’t reopen logs cleanly on HUP. Someone “solved” this years ago with copytruncate. It worked well enough that it became the default pattern for new services—copytruncate for everything, always.

Then the company moved workloads to faster NVMe and cranked up throughput. Their busiest service started writing multiple gigabytes per hour. Rotation began taking long enough that the window of truncation mattered, and they started losing log lines. Not “maybe”; provably. Their audit trail had gaps right at rotation boundaries.

Worse, compression ran immediately, hammering CPU during peak traffic. The “optimization” created a performance cliff: during rotation, CPU spikes caused increased request latency, which caused retries, which caused more logs, which caused larger rotations. A nice self-inflicted feedback loop.

They rolled back: switched to rename-based rotation (no copytruncate) and fixed the app to reopen logs reliably on a signal. They also added delaycompress to move compression away from the critical moment. It wasn’t glamorous work. It was correct work.

Joke #2: “We used copytruncate to avoid changing the app” is the logging equivalent of fixing a leak by buying a bigger mop.

Incident #3: the boring practice that saved the day (explicit triggers + forced tests)

A small platform team ran dozens of internal services, mostly Go and Python, on Ubuntu. They had one habit that looked annoyingly pedantic: every logrotate stanza had an explicit trigger and a comment saying why. No inheritance. No relying on /etc/logrotate.conf behavior. Every stanza: daily or size (often both via maxsize), always intentional.

They also had a release checklist item: after deploying a new service, run logrotate -d -v against its stanza and paste the output snippet in the change record. That felt like bureaucracy until a base image change altered some global defaults and a few services landed on new nodes.

Services with explicit triggers rotated normally. Services with inherited behavior (from other teams’ configs) became weird: some rotated weekly when people expected daily; some rotated only when size hit a default threshold that no one remembered setting. The platform team didn’t have an incident. They had a mildly annoying Tuesday.

The difference wasn’t magic. It was the boring practice of making “when to rotate” explicit, then testing behavior once, the same way you’d test a firewall rule or a backup. That’s what professionalism looks like in ops: preventing surprises, not heroically reacting to them.

Interesting facts & context (why this keeps happening)

  1. Logrotate predates systemd by decades. It grew up in a cron-driven world, and the shift to systemd timers changed where people look when it fails.
  2. The state file is the core of logrotate’s “memory.” Without it, time-based rotation would either rotate constantly or require filesystem metadata heuristics.
  3. rotate N is retention, not scheduling. This is the single most common conceptual mistake—because the word “rotate” is doing too much work.
  4. Many distros ship a global config that looks like it sets defaults. People assume those defaults include a trigger. Sometimes they do; sometimes your stanza overrides them; sometimes your file isn’t included at all.
  5. Journald reduced reliance on file logs for some services. But plenty of apps still write to files (compatibility, compliance, or because the container image came from 2016 and nobody wants to touch it).
  6. Copytruncate became popular because it “works” with stubborn apps. It also introduces races and potential loss under load because writing can occur while copying.
  7. Compression used to be expensive enough to schedule carefully. On modern CPUs it’s cheaper, but it still matters when you compress multi-gigabyte logs during peak hours.
  8. Renaming logs is atomic; truncation isn’t. The rename-based approach aligns with how Unix file descriptors behave and tends to be safer when apps reopen logs properly.
  9. Logrotate configs are deceptively permissive. Many misconfigurations are not syntax errors; they’re logic errors that result in “doing nothing successfully.”

Checklists / step-by-step plan

Step-by-step: get rotation working for one problematic log

  1. Confirm scheduling exists.

    cr0x@server:~$ systemctl status logrotate.timer
    ● logrotate.timer - Daily rotation of log files
         Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled; preset: enabled)
         Active: active (waiting) since Sat 2025-12-28 07:10:22 UTC; 2h 14min ago
        Trigger: Sun 2025-12-29 00:00:00 UTC; 14h left
       Triggers: ● logrotate.service

    Decision: If disabled, enable it. If enabled, continue.

  2. Prove your config is included.

    cr0x@server:~$ sudo grep -n 'include /etc/logrotate.d' /etc/logrotate.conf
    12:include /etc/logrotate.d

    Decision: If missing, restore the include and retest.

  3. Run debug for that stanza.

    cr0x@server:~$ sudo logrotate -d -v /etc/logrotate.d/myapp | sed -n '1,120p'
    reading config file /etc/logrotate.d/myapp
    Handling 1 logs
    considering log /var/log/myapp/myapp.log
      log does not need rotating

    Decision: If it doesn’t mention a schedule/size, you likely have no trigger.

  4. Add an explicit trigger.

    Pick one: time-based (daily) or volume-based (size / maxsize). For high-volume logs, size-based is usually the grown-up choice.

  5. Force rotation once to validate mechanics.

    cr0x@server:~$ sudo logrotate -f -v /etc/logrotate.d/myapp | tail -n 30
    considering log /var/log/myapp/myapp.log
      log needs rotating
    renaming /var/log/myapp/myapp.log to /var/log/myapp/myapp.log.1
    creating new /var/log/myapp/myapp.log mode = 0640 uid = 123 gid = 4

    Decision: If forced rotate fails, fix permissions/paths before blaming schedules.

  6. Ensure the app writes to the new log.

    cr0x@server:~$ sudo lsof /var/log/myapp/myapp.log | head
    COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    myapp   2210 myapp   7w   REG  252,0   123456  401 /var/log/myapp/myapp.log

    Decision: If it still writes to a deleted inode, fix postrotate or app reopen behavior.

Operational checklist: what I expect in a production-grade stanza

  • An explicit trigger: daily or size/maxsize.
  • Explicit retention: rotate N plus compress and usually delaycompress.
  • Correct creation: create MODE USER GROUP or rely on app to create safely (choose intentionally).
  • Safe reopen strategy: postrotate signal/reload, validated with lsof.
  • Not relying on inheritance unless you’ve tested it and can explain it to someone half-asleep.
  • Size guardrails for high-volume services: maxsize prevents daily rotations from being too late.

FAQ

1) Why does logrotate exit with success even when it rotates nothing?

Because “no logs needed rotating” is a valid outcome. Logrotate is a policy engine, not a “must rotate something” job. The problem is when you expected rotation but didn’t provide a trigger or your state/criteria says “not yet.”

2) Is the “one config mistake” really just missing daily or size?

Yes, in the practical sense: missing a trigger (or thinking rotate N is a trigger) is the most common cause of silent non-rotation. Syntax errors usually scream; missing triggers whisper.

3) Should I use time-based or size-based rotation?

For low to moderate volume, daily is fine. For high volume, use size or maxsize (often alongside daily) so you don’t wait until midnight to stop a runaway log.

4) What’s the difference between size, minsize, and maxsize?

size rotates when the file exceeds that size (regardless of time schedule). minsize adds a minimum size threshold to time-based rotation. maxsize forces rotation when size exceeds a limit even if time-based criteria aren’t met yet.

5) When should I use copytruncate?

When the application cannot reopen logs and you cannot fix it quickly. It’s a compatibility hack with tradeoffs: potential log loss at rotation boundaries and extra IO. Prefer rename + signal/reopen for correctness.

6) Why does disk usage stay high after rotation?

Because the process can keep writing to a deleted file descriptor. The filesystem only frees space when the last handle closes. Use lsof +L1 to detect it and fix your reopen strategy.

7) Can I delete /var/lib/logrotate/status to “reset” things?

You can, but it’s blunt. It can cause unexpected rotations across many logs on next run. Safer: edit only the affected entry, or force rotation for a specific config with logrotate -f and verify behavior.

8) Why does my logrotate stanza work in one environment but not another?

Different global defaults, different include behavior, different timers, different file ownership, and different application reopen behavior. The fix is boring: make triggers explicit, test with -d -v, and validate with lsof.

9) I added daily but it still doesn’t rotate. What next?

Check the state file entry and debug output. If it says “already rotated,” you’re waiting for the next day. If the timer isn’t firing, fix systemd scheduling. If permissions fail, fix ownership/mode or su.

10) Should I rotate logs hourly on Ubuntu 24.04?

Only if you truly need it. Hourly rotation increases churn (more files, more compression work, more metadata). If your problem is “disk fills before midnight,” maxsize is often the better lever.

Next steps (what to change Monday morning)

If you’re dealing with logrotate not rotating on Ubuntu 24.04, do these in order:

  1. Make the trigger explicit in every custom stanza: add daily or size/maxsize. Stop relying on inheritance.
  2. Run logrotate -d -v on the exact file you care about and read the reasoning lines. Don’t guess.
  3. Check the state file entry for the log and confirm it matches your expectation of “last rotated.” Fix time skew before anything else.
  4. Validate reopen behavior with lsof +L1. If you see (deleted), your rotations are cosmetic.
  5. Add size guardrails for chatty services. Daily rotation is not a safety system; it’s a calendar event.

The goal isn’t “logs rotate.” The goal is “logs rotate predictably, without losing events, without surprise CPU spikes, and without waking you up.” That’s the standard.

← Previous
MySQL vs MariaDB: temp tables on disk—how to stop them for real
Next →
ZFS zpool iostat -w: Understanding Workload Patterns in Real Time

Leave a comment