How To

How To Truncate or Empty Log Files in Linux

Deleting a 2 GB log with rm freed nothing on our test server. Used space read 5.0G before the delete and 5.0G after it, because a tail process still had the file open and the kernel keeps the blocks until the last reader lets go. That’s how most people first try to empty a log file in Linux, and it fails without an error. To truncate log files safely, you empty them in place, so the inode survives and the writer keeps writing.

Original content from computingforgeeks.com - post 3991

This guide covers truncate -s 0, the sudo redirect trap, getting space back from a log you already deleted, emptying many logs at once, and handing the job to logrotate or journald. Every command below was run on Debian 13 with GNU coreutils 9.7 in October 2026, with a live writer appending to the log during the truncate tests. The few exceptions are marked untested where they appear.

Use truncate -s 0 to empty a log file

truncate -s 0 is the method to reach for first, because it sets the file’s size to zero without removing or recreating it. The process writing the log keeps its open file descriptor, the disk blocks are released at once, and nobody has to restart anything. You need write permission on the file, which for anything under /var/log means sudo. The commands in this guide are plain coreutils, bash and systemd, so they carry over to Ubuntu, the RHEL family and Fedora, although Debian is the only distribution we ran them on.

sudo truncate -s 0 /var/log/demoapp/app.log

To see what that does to a file that is in use, we ran it on a 2.1 GB log while a loop appended a heartbeat line every second. The size and disk checks on each side of the truncate show what you should expect on your own server.

ls -lh /var/log/demoapp/app.log; df -h / | tail -1; sudo truncate -s 0 /var/log/demoapp/app.log; ls -l /var/log/demoapp/app.log; df -h / | tail -1; sleep 3; ls -l /var/log/demoapp/app.log; cat /var/log/demoapp/app.log

Watch the used column on the root filesystem, then the size of the file three seconds later. The first two lines are the state before the truncate, and the next two come straight after it.

-rw-r--r-- 1 root root 2.1G Oct 20 10:10 /var/log/demoapp/app.log
/dev/sda1        40G  5.1G   33G  14% /
-rw-r--r-- 1 root root 0 Oct 20 10:10 /var/log/demoapp/app.log
/dev/sda1        40G  3.1G   35G   8% /
-rw-r--r-- 1 root root 57 Oct 20 10:10 /var/log/demoapp/app.log
10:10:19 heartbeat
10:10:20 heartbeat
10:10:21 heartbeat

Used space dropped from 5.1G to 3.1G the moment the file hit zero, and three seconds later it held 57 bytes of fresh heartbeat lines at the start of the file. That is the healthy result: the writer didn’t notice, and new lines begin at offset 0. If you see the file jump straight back to its old size instead, the application isn’t opening its log in append mode, and the section on how the application writes its log tells you what to do about it.

Test the other ways to empty a file

Most of the shell shortcuts you’ll find for emptying a file do the same thing as truncate. They open the existing file with truncation and write nothing, so the inode survives. We filled seven copies of a 100,000-line file in /tmp and emptied each one a different way, as a normal user in bash. Create the test files first, so you can repeat the comparison yourself.

cd /tmp; for m in truncate colon redirect echo echon dd cp; do seq 1 100000 > t-$m.log; done

Each file starts at 588895 bytes. Empty them with the seven methods from the same shell, since the file names are relative to /tmp.

truncate -s 0 t-truncate.log
: > t-colon.log
> t-redirect.log
echo "" > t-echo.log
echo -n > t-echon.log
dd if=/dev/null of=t-dd.log status=none
cp /dev/null t-cp.log

We recorded size and inode with stat -c "%n %s %i" before and after. Every inode stayed the same, so none of these methods replaced the file, and six of the seven came out at zero bytes. The odd one is the echo "" form that a lot of older guides still recommend, because echo always adds a newline unless you pass -n.

MethodBeforeAfterInode before / after
truncate -s 0 file5888950390 / 390
: > file5888950391 / 391
> file5888950392 / 392
echo "" > file5888951393 / 393
echo -n > file5888950394 / 394
dd if=/dev/null of=file status=none5888950395 / 395
cp /dev/null file5888950396 / 396

You can confirm the leftover byte with od, which prints every character in the file. It’s a quick way to tell an empty file from one that only looks empty in cat.

od -c /tmp/t-echo.log

It is one newline and nothing else. Without -n, echo always ends its output with one.

0000000  \n
0000001

One byte won’t hurt disk space, but it does leave a blank first line that log parsers and wc -l will count. We’d pick truncate -s 0 for scripts and runbooks because it reads clearly, works the same under sudo, and has a -c flag that matters once globs are involved. The redirect forms are fine when you clear a file you own from an interactive shell.

Run truncate with sudo on root-owned logs

Logs under /var/log belong to root, and that is where the redirect shortcuts break. sudo only elevates the command after it, while your own unprivileged shell opens the > redirect before sudo starts. The result is a permission error and an untouched log.

bash: /var/log/demoapp/app.log: Permission denied

Both of these look like they should work, and neither does. The first is a bare redirect and the second the colon form, each with sudo in front.

sudo > /var/log/demoapp/app.log; echo "rc=$?"; sudo : > /var/log/demoapp/app.log

The shell refuses to open the file and returns 1. The log keeps every byte. Check the size afterwards, because a quiet terminal does not tell you it worked.

bash: line 1: /var/log/demoapp/app.log: Permission denied
rc=1
bash: line 1: /var/log/demoapp/app.log: Permission denied

The fix is to make the privileged process the one that opens the file. Any of these three emptied the root-owned log in our test, and you should default to the first because it needs no extra shell.

sudo truncate -s 0 /var/log/demoapp/app.log
sudo sh -c ": > /var/log/demoapp/app.log"
: | sudo tee /var/log/demoapp/app.log

The sh -c form moves the redirect inside a root shell. The tee form works because tee itself runs as root and opens the file for writing, and it returned 0 without printing anything, since there was nothing to copy.

truncate: cannot open for writing: Operation not permitted

If root itself can’t empty a log, check for the append-only attribute. Some audit and security setups set chattr +a on their logs so the content can’t be rewritten, and that blocks truncation for every user, root included. lsattr shows it as an a in the flags.

lsattr /var/log/demoapp/audit.log

On our file the flags looked like this. Both truncate and a root shell redirect were refused with the same reason.

-----a--------e------- /var/log/demoapp/audit.log
truncate: cannot open '/var/log/demoapp/audit.log' for writing: Operation not permitted
sh: 1: cannot create /var/log/demoapp/audit.log: Operation not permitted

Somebody set that attribute on purpose, so find out why before you remove it. If emptying the file is approved, drop the flag and truncate, which worked in our test. The third line restores the flag so the protection stays in place.

sudo chattr -a /var/log/demoapp/audit.log
sudo truncate -s 0 /var/log/demoapp/audit.log
sudo chattr +a /var/log/demoapp/audit.log

The same attribute is behind most “Operation not permitted” errors on files root should be able to touch. The guide on fixing Operation not permitted when deleting a file covers the immutable flag as well, which you clear the same way.

Find a deleted log that still holds space

If you already removed a big log with rm and the disk is still full, the file is not really gone. rm only removes the directory entry. The kernel keeps the data until the last process holding the file closes it, so the space comes back when that process exits or when you truncate the file through /proc. We reproduced it with a 2 GB copy of the log open in tail -f.

df -h / | tail -1; sudo rm /var/log/demoapp/worker.log; df -h / | tail -1

Used space did not move after the delete. The 2 GB is still allocated but no longer reachable by name, so ls and du will not show it to you. Only df still counts it.

/dev/sda1        40G  5.0G   33G  14% /
/dev/sda1        40G  5.0G   33G  14% /

Restarting the process that holds the file frees the space, but you can get it back without a restart. It takes four steps, in this order:

  1. Install lsof if it’s missing
  2. Find the process holding the deleted file
  3. Read its descriptor number under /proc
  4. Truncate that descriptor link

The Debian 13 cloud image does not ship lsof, and our first attempt ended with sudo: lsof: command not found. Install it first.

sudo apt-get install -y -qq lsof

On RHEL, Rocky Linux or Fedora the equivalent is sudo dnf install lsof, which we did not run for this guide.

lsof +L1 lists open files with a link count below one, which means deleted. On its own it is noisy, because every daemon still running an old binary after a package upgrade shows up as txt or mem lines. You only care about logs, so filter for the log directory.

sudo lsof -nP +L1 | grep -E "^COMMAND|/var/log"

The tail process, PID 10462, holds descriptor 3 on the deleted file, and the size column still reads 2147483933 bytes.

COMMAND     PID USER  FD   TYPE DEVICE   SIZE/OFF NLINK   NODE NAME
tail      10462 root   3r   REG    8,1 2147483933     0 133891 /var/log/demoapp/worker.log (deleted)

Every open descriptor is a link under /proc/<PID>/fd, and writing to that link reaches the deleted file. Take the PID straight from the PID column of the lsof output, because a name search can match more than one process. Then list its descriptors to confirm the number before you truncate anything. Replace 10462 with the PID from your own output.

P=10462
sudo ls -l /proc/"$P"/fd

Descriptor 3 points at worker.log (deleted), matching the 3r in the FD column. Truncate the link, with a disk check on each side so you can see the result.

df -h / | tail -1; sudo truncate -s 0 /proc/"$P"/fd/3; df -h / | tail -1

Our capture below found the same PID with pgrep -x tail, which is safe only when exactly one process has that name. It shows the whole sequence on the lab server, ending with used space falling from 3.1G to 1.1G.

Truncate log file held open after rm: lsof +L1 and /proc fd free 2G on Debian 13

The 2 GB came back while tail kept running, and a second lsof +L1 showed the same deleted entry at size 0. The entry only disappears once the process closes the file or exits, which costs nothing now that it’s empty. If you need to find which log is eating the disk in the first place, ncdu shows where space really goes. A gap between what it counts and what df reports is the usual sign of a deleted file that’s still open.

truncate: cannot open ‘/proc/10462’ for writing: Is a directory

We hit this on an early attempt, which used pgrep -f "tail -f /var/log/demoapp/worker.log" to find the PID. The -f flag matches the full command line. The shell running our own command line contained the same text, so the variable held two PIDs, and the path split on the newline into /proc/10462 and 10641/fd/.

truncate: cannot open '/proc/10462' for writing: Is a directory
truncate: cannot open '10641/fd/' for writing: No such file or directory

Paste the PID from the lsof output, or match the process name exactly with pgrep -x when only one such process exists, and the path resolves to a single descriptor. Quote "$P" and check it with echo before you truncate through it, because a wrong path here fails loudly but a wrong PID could point at a different file.

Truncate log files in bulk

Emptying a whole directory of logs is where the quiet failures pile up, because two shell rules decide which files the command actually sees. The first rule is that ** doesn’t recurse in bash unless globstar is on, and it’s off by default. Our test tree held four logs:

  • /var/log/demoapp/a.log
  • /var/log/demoapp/app.log
  • /var/log/demoapp/nested/b.log
  • /var/log/demoapp/nested/deep/c.log

Check what the recursive pattern expands to before you hand it to truncate. echo shows the expansion without touching any file.

shopt globstar; echo /var/log/demoapp/**/*.log

Only one of the four files matched, because with globstar off, ** behaves like a single * and matches exactly one directory level.

globstar       	off
/var/log/demoapp/nested/b.log

After shopt -s globstar, the same pattern matched all four. The second rule bites harder. A glob expands in your own shell, as your user, before sudo starts. In a directory only root can read, nothing matches, so bash passes the literal string *.log through and truncate creates a file with that name.

sudo truncate -s 0 /var/log/private-app/*.log; echo rc=$?; sudo ls -l /var/log/private-app/

The exit code says success, yet secret.log still holds its 3893 bytes, and a new empty file called *.log sits next to it.

rc=0
total 4
-rw-r--r-- 1 root root    0 Oct 20 10:12 *.log
-rw-r--r-- 1 root root 3893 Oct 20 10:12 secret.log

Let root expand the glob by quoting the whole command into sh -c. Run that way, secret.log dropped to 0 bytes.

sudo sh -c "truncate -s 0 /var/log/private-app/*.log"

Add -c (--no-create) to any truncate that takes a glob as well. After we removed the stray file and repeated the broken command as sudo truncate -c -s 0 /var/log/private-app/*.log, it still returned 0 but created nothing, so a pattern that matches nothing can’t litter the directory. It does not fix the expansion, though, which is why the sh -c wrapper comes first.

For anything recursive or selective, find is more predictable than a glob, because it walks the tree as root and lets you filter by size. This empties every .log over 100 KB at any depth and leaves small ones alone.

sudo find /var/log/demoapp -type f -name "*.log" -size +100k -exec truncate -s 0 {} +

All three large files went from 288894 bytes to 0, from the top-level a.log down to nested/deep/c.log, and the 380-byte app.log was skipped.

-rw-r--r-- 1 root root   0 Oct 20 10:12 /var/log/demoapp/a.log
-rw-r--r-- 1 root root 380 Oct 20 10:12 /var/log/demoapp/app.log
-rw-r--r-- 1 root root   0 Oct 20 10:12 /var/log/demoapp/nested/b.log
-rw-r--r-- 1 root root   0 Oct 20 10:12 /var/log/demoapp/nested/deep/c.log

Run the same find with -exec ls -l {} + first, so you see the list before anything is emptied. Keep the path narrow to the application’s own directory. A broad -name "*log" across all of /var/log also empties the auth and system logs that other services write, and those are the ones you’d want during an incident. The find command guide has more filters, such as -mtime for logs that haven’t changed in a week.

Keep the last lines of a log

Sometimes you want the space back but still need the most recent entries for an open incident. Copy the tail to a temporary file and write it back over the log with cat, which keeps the same inode, so a writer holding the file isn’t cut off. A mv of the temp file over the log would swap in a new inode and leave the application writing to the old, invisible one.

sudo sh -c 'T=$(mktemp) && tail -n 1000 /var/log/demoapp/a.log > "$T" && cat "$T" > /var/log/demoapp/a.log && rm -f "$T"'

Our lab ran the same commands with a fixed /tmp/a.keep path. The mktemp version avoids clobbering a file someone else left there, and running it inside the root shell means root owns the temp file. With stat -c "%s bytes inode %i" before and after, the file shrank from 588895 to 6001 bytes, and the inode stayed 133913.

588895 bytes inode 133913
6001 bytes inode 133913
99001
99002

The first two lines of the result are 99001 and 99002, which are exactly the last thousand lines of the original 100,000. Lines a writer appends between the tail and the cat are lost, so on a busy log keep that window short and run the whole thing as one command, as shown.

Check how the application writes its log

Truncation only works cleanly when the writer opened the log in append mode. A process using O_APPEND always writes at the current end of the file, so after a truncate it starts again at byte 0. A process that opened the file for plain writing keeps its own offset and carries on from there, so the kernel fills the gap with a hole. We tested both with a writer that holds the file open for its whole life. With exec 3>> (append) and 10 MB of base64 data a second, the log was 82M before the truncate, and two seconds after it the two size commands agreed.

ls -lh /var/log/demoapp/held.log; sudo du -h /var/log/demoapp/held.log; sudo head -c 16 /var/log/demoapp/held.log | od -c | head -1

Both report 28M, and the file still starts with base64 text, so the writer simply carried on from byte 0. You get the space back and a log you can still read.

-rw-r--r-- 1 root root 28M Oct 20 10:30 /var/log/demoapp/held.log
28M	/var/log/demoapp/held.log
0000000   O   a   0   T   x   G   Y   x   D   S   f   G   Y   X   h   /

Then we repeated it with a writer that opened the file once with exec 3> instead, writing the same data. Two seconds after truncate -s 0, the sizes disagreed.

ls -lh /var/log/demoapp/batch.log; sudo du -h /var/log/demoapp/batch.log

ls reports the apparent size, and du reports the blocks actually allocated. On a normal log the two agree.

-rw-r--r-- 1 root root 136M Oct 20 10:11 /var/log/demoapp/batch.log
28M	/var/log/demoapp/batch.log

The file is sparse: 136M long, 28M on disk, and the start of it is nothing but NUL bytes. Reading the first 64 bytes shows it.

sudo head -c 64 /var/log/demoapp/batch.log | od -c | head -3

Every byte is \0. The * line means od collapsed the repeated rows of zeros.

0000000  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000100

Disk space is still recovered, so this is not an emergency, but anything that reads the file from the top meets a block of binary zeros before the first real line. You can check how a process opened its log before you truncate by reading the flags in /proc/<PID>/fdinfo.

P=$(pgrep -f "exec 3>/var/log/demoapp/batch.log" | head -1); sudo cat /proc/$P/fdinfo/3 | head -3

The flags field is octal. 0100001 is write-only with large-file support, and there’s no 02000 append bit in it, which is why the writer kept its 169979724-byte offset.

pos:	169979724
flags:	0100001
mnt_id:	29

For a writer like that, truncating is the wrong fix. Your options, from best to last resort:

  • open the log with append (>> in shell scripts, not >)
  • use the application’s reopen signal, or restart it after rotation
  • truncate or copytruncate, accepting the sparse file

Our heartbeat loop never showed the problem either, because it reopened the file with >> for every line. If you write the application yourself, that one character is the whole fix.

Shrink the systemd journal instead of truncating it

Never truncate the .journal files under /var/log/journal. They are structured binary files with their own headers, and journald has a supported way to shrink them. Start by checking how much space the journal takes.

sudo journalctl --disk-usage

After we pushed six batches of random test data through logger, it reported this. That figure is the starting point for the vacuum commands below.

Archived and active journals take up 69.3M in the file system.

Vacuuming only deletes archived journal files and never the active ones. Rotate first, so the current data moves into an archive, then vacuum down to a size.

sudo journalctl --rotate; sudo journalctl --vacuum-size=50M; sudo journalctl --disk-usage

journald names each file it deletes and the total it freed, which here was 35.5M. The 0B lines are journal directories with nothing archived to remove, and the 41M line is the final --disk-usage, printed out of order because the commands mix stdout and stderr.

Vacuuming done, freed 0B of archived journals from /run/log/journal.
Vacuuming done, freed 0B of archived journals from /var/log/journal.
Deleted archived journal /var/log/journal/270c874bbd574897a173170275b4931c/system@c497d599bf1b48bba7375a768588d75a-0000000000000001-00065e42aca306ae.journal (2.5M).
Archived and active journals take up 41M in the file system.
Deleted archived journal /var/log/journal/270c874bbd574897a173170275b4931c/user-1000@c497d599bf1b48bba7375a768588d75a-000000000000068e-00065e42d17b7a03.journal (32.9M).
Vacuuming done, freed 35.5M of archived journals from /var/log/journal/270c874bbd574897a173170275b4931c.

sudo journalctl --vacuum-time=1s goes further and drops every archived file, which freed another 25M and left the journal at 16M of active files. Both are one-off cleanups, and the journal grows back to its default cap afterwards. To set your own limit, put it in a drop-in so a package upgrade can’t overwrite it. The stock /etc/systemd/journald.conf on Debian 13 has SystemMaxUse and SystemKeepFree commented out, and the drop-in directory doesn’t exist yet.

sudo mkdir -p /etc/systemd/journald.conf.d

Create the drop-in file. The name is up to you, as long as it ends in .conf.

sudo vim /etc/systemd/journald.conf.d/size.conf

These two lines cap the journal at 200M and tell journald to always leave 1G of the filesystem free, whichever limit is hit first.

[Journal]
SystemMaxUse=200M
SystemKeepFree=1G

Restart journald and read its startup line, which states the limit it applied. Restarting it does not cost you messages already written to disk.

sudo systemctl restart systemd-journald
sudo journalctl -b -u systemd-journald --no-pager | tail -2

“max 200M” confirms the drop-in was read. The 184M free figure is the room left under the new cap.

Oct 20 10:13:28 deb01 systemd-journald[11718]: Journal started
Oct 20 10:13:28 deb01 systemd-journald[11718]: System Journal (/var/log/journal/270c874bbd574897a173170275b4931c) is 16M, max 200M, 184M free.

The journald.conf reference lists the other limits, such as MaxRetentionSec for time-based retention. For reading what is left in the journal, the journalctl filtering guide covers units, priorities and time ranges.

Set up logrotate with copytruncate

Manual truncation is for the day a disk fills up. To stop it happening again, logrotate’s copytruncate mode does the same in-place truncate on a schedule, after first copying the content to a rotated file. It suits an application that can’t be told to reopen its log. The Debian 13 cloud image we used didn’t ship logrotate at all, so there was no /etc/logrotate.conf and nothing rotating application logs until we installed it.

sudo apt-get install -y logrotate

On RHEL, Rocky Linux or Fedora the command is sudo dnf install logrotate. We did not run it, and we did not check whether those images already include the package.

The package ships a systemd timer that runs logrotate, and logrotate only looks at your logs when that timer fires. Check that it’s scheduled.

systemctl list-timers logrotate.timer --no-pager | head -2

The NEXT column shows the first run, with nothing in LAST yet, because the timer had never fired on this fresh install.

NEXT                        LEFT LAST PASSED UNIT            ACTIVATES
Wed 2026-10-21 00:46:45 UTC  14h -         - logrotate.timer logrotate.service

The schedule matters more than most people expect, so read it from the unit itself.

systemctl cat logrotate.timer | grep -E "OnCalendar|RandomizedDelaySec|AccuracySec|Persistent"

On Debian 13 the timer fires once a day with up to an hour of random delay, and Persistent=true catches up a run missed while the machine was off.

OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

Give the application its own file under /etc/logrotate.d, so a package update to the main config never touches it.

sudo vim /etc/logrotate.d/demoapp

Add the rule for the log. The path in front of the braces is the file it manages.

/var/log/demoapp/app.log {
    rotate 7
    size 100M
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

Read from the top, the rule does five things:

  • keeps seven rotated copies
  • rotates at the next run if the log is over 100M
  • compresses old copies, one rotation late
  • skips a missing or empty log
  • copies the log, then truncates it in place

Our first lab file also carried a daily line above size, which we removed here. The logrotate man page says size and the time options are mutually exclusive and the last one listed wins. In that file size came last, so it was already in charge and daily did nothing. Be clear on what “size” means here: logrotate checks the size only when it runs. With the stock daily timer, a log that passes 100M at 09:00 waits for the next run, and maxsize with daily gives no earlier rotation either. Do a dry run first, which prints the plan without touching any file.

sudo logrotate -d /etc/logrotate.d/demoapp 2>&1 | grep -E "rotating pattern|considering|needs|does not need"

Against a small 10-line log, the trimmed rule parsed cleanly and correctly decided there was nothing to do yet. If you see a syntax error here instead, fix it now, because the timer will hit the same error at its next run.

rotating pattern: /var/log/demoapp/app.log 104857600 bytes empty log files are not rotated, (7 rotations), old logs are removed
considering log /var/log/demoapp/app.log
  log does not need rotating (log size is below the 'size' threshold)

Then run it for real, which we did against a 151M test log. You should see the same lines with your own path in them.

sudo logrotate -v /etc/logrotate.d/demoapp 2>&1 | grep -E "rotating|copying|truncating|needs|considering"

The first line shows the 104857600-byte threshold in force, which is the 100M from the config. The last two lines are the copy and the in-place truncate.

rotating pattern: /var/log/demoapp/app.log 104857600 bytes empty log files are not rotated, (7 rotations), old logs are removed
considering log /var/log/demoapp/app.log
  log needs rotating
rotating log /var/log/demoapp/app.log, log->rotateCount is 7
copying /var/log/demoapp/app.log to /var/log/demoapp/app.log.1
truncating /var/log/demoapp/app.log

Afterwards app.log was 0 bytes, app.log.1 held the 151M, and the heartbeat writer was already filling the live file again. The man page warns that copytruncate can lose the lines written between the copy and the truncate. Prefer create with a postrotate reload when your application supports it, and the rsyslog rotation guide shows that pattern for syslog files.

If a log can fill the disk in less than a day, make the timer fire more often instead of shrinking the size threshold. A drop-in keeps the change out of the packaged unit.

sudo mkdir -p /etc/systemd/system/logrotate.timer.d

Create the override file. You can give it any name that ends in .conf.

sudo vim /etc/systemd/system/logrotate.timer.d/hourly.conf

The empty OnCalendar= clears the packaged daily schedule. Without it, systemd adds the hourly entry next to the daily one instead of replacing it.

[Timer]
OnCalendar=
OnCalendar=hourly

Reload systemd, restart the timer, and check the new schedule. You need the daemon-reload, because systemd does not pick up a new drop-in on its own.

sudo systemctl daemon-reload; sudo systemctl restart logrotate.timer; systemctl list-timers logrotate.timer --no-pager

NEXT is now about an hour away instead of the next day. It reads 1h 2min rather than under an hour because the packaged RandomizedDelaySec=1h still applies to every run.

NEXT                           LEFT LAST                        PASSED UNIT            ACTIVATES
Tue 2026-10-20 11:33:55 UTC 1h 2min Tue 2026-10-20 10:13:53 UTC      - logrotate.timer logrotate.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.

Rules with size or maxsize are now checked every hour, while plain daily rules still rotate once a day because logrotate tracks when each log last rotated.

logrotate: command not found

After the install, running logrotate --version as a normal user still printed bash: line 1: logrotate: command not found. The binary is at /usr/sbin/logrotate, and a regular user’s PATH on our Debian 13 server was /usr/local/bin:/usr/bin:/bin:/usr/games, which skips /usr/sbin. Run it with sudo, which searches its own path including /usr/sbin, and it’s found. We saw this on Debian 13 and did not check other distributions. It is one of the small gaps of a minimal cloud image, along with the missing lsof, that the Debian 13 post-install checklist helps you close before a full disk forces the issue.

Pick the method for your situation

The right tool depends on whether the file still exists, who writes to it, and whether you need the space once or for good. This is the order we’d work through on a server that is running out of disk.

SituationMethodWatch out for
One large log, app still runningsudo truncate -s 0 fileSparse file if the app doesn’t append
Log is root-owned and you used >sudo sh -c ": > file" or sudo truncateThe redirect runs as your user
Already ran rm, disk still fulllsof +L1, then truncate /proc/PID/fd/Npgrep -f can return two PIDs
Many logs, nested directoriesfind ... -size +100k -exec truncate -s 0 {} +Never *log across all of /var/log
Need the latest entriestail -n 1000 to a temp file, cat it backLines written in between are lost
Truncate refused even as rootchattr -a, truncate, chattr +aFind out why the flag was set
Journal is largejournalctl --vacuum-size and SystemMaxUseNever truncate .journal files
It keeps happeninglogrotate with copytruncate or createsize and daily: the last one listed wins, and size is only checked when the timer runs

Most of the time the first row is all you need. When df and du disagree after a cleanup, go to the third row before you reach for a reboot. Once the immediate fire is out, put a logrotate rule or a journald cap in place, so the same log can’t fill your disk again.

Keep reading

Fix β€œapt-key is deprecated. Manage keyring files in trusted.gpg.d instead” Debian Fix β€œapt-key is deprecated. Manage keyring files in trusted.gpg.d instead” Best Terminal Shell Prompts for Zsh, Bash and Fish Featured Best Terminal Shell Prompts for Zsh, Bash and Fish Linux Terminal Shortcuts Cheat Sheet (Bash and zsh) Cheat Sheets Linux Terminal Shortcuts Cheat Sheet (Bash and zsh) Install lazygit on Linux: Ubuntu, Debian, Fedora and Arch Git Install lazygit on Linux: Ubuntu, Debian, Fedora and Arch Linux Commands Cheat Sheet for Ubuntu and Rocky Linux (PDF) Cheat Sheets Linux Commands Cheat Sheet for Ubuntu and Rocky Linux (PDF) Configure Powerlevel10k as your Zsh Theme on Linux / macOS Terminal Configure Powerlevel10k as your Zsh Theme on Linux / macOS

Leave a Comment

Press ESC to close