How To

Find and Delete Files Older Than X Days or Minutes in Linux

Most answers to “find files older than 7 days” hand you find -mtime +7, and on our test box it skipped two files over a week old. The find command counts a file’s age in whole days and drops the remainder, so +7 really means eight days or older. A log you wrote 191 hours ago (seven days and 23 hours) is not matched.

Original content from computingforgeeks.com - post 74982

Below are the tests that match what you mean and a safe way to find and delete files older than 30 days, 7 days or a few minutes. You’ll also see the -delete ordering mistake that wiped a whole directory in our test, and a systemd-tmpfiles rule that runs the cleanup daily. Every command ran on Debian 13 with GNU findutils 4.10.0 in October 2026, against files backdated with touch so each age is known to the hour.

Set up a test directory with known ages

Never try a new delete expression on real data first. A scratch directory with files of known age shows you exactly where each test draws its line, and it costs nothing to rebuild. The touch -d option accepts relative times, so this creates eight logs from 1 to 241 hours old and two temp files at 4 and 6 minutes.

mkdir -p ~/logs && cd ~/logs
for h in 1 30 160 167 169 191 193 241; do touch -d "$h hours ago" app-${h}h.log; done
touch -d "4 minutes ago" m4.tmp; touch -d "6 minutes ago" m6.tmp

List the modification times next to the clock so you can check every result below against it.

ls -l --time-style=+%F_%H:%M | awk "{print \$6, \$7}"; date +%F_%H:%M

The clock read 03:48 on October 18, and each file sits exactly where its name says. The 160h and 167h files are just under seven days old, while 169h and 191h are just over it.


2026-10-11_11:48 app-160h.log
2026-10-11_04:48 app-167h.log
2026-10-11_02:48 app-169h.log
2026-10-10_04:48 app-191h.log
2026-10-10_02:48 app-193h.log
2026-10-18_02:48 app-1h.log
2026-10-08_02:48 app-241h.log
2026-10-16_21:48 app-30h.log
2026-10-18_03:44 m4.tmp
2026-10-18_03:42 m6.tmp
2026-10-18_03:48

The last line is the clock. Each file name carries its real age in hours, so app-169h.log sits just over seven days old and app-167h.log just under it.

Find files older than X days with -mtime

To find files older than 7 days, use -mtime +6 or, if you want the cutoff to the minute, -mmin +10080. Do not use -mtime +7 unless “eight days or more” is really what you need. Run the three day-based tests and the minute-based one against the sandbox:

cd ~/logs
find . -type f -mtime +7 | sort
find . -type f -mtime +6 | sort
find . -type f -mtime 7 | sort
find . -type f -mmin +10080 | sort

The capture shows each test and the files it matched. Look at where the 169h and 191h files appear, because they are the ones that decide whether your cleanup is a day late.

find -mtime +7, +6 and -mmin +10080 results compared on Debian 13

Each test matched these files in our sandbox, and the last column says what that works out to in hours.

TestMatchedEffective rule
-mtime +7193h, 241h192 hours (8 days) or older
-mtime +6169h, 191h, 193h, 241h168 hours (7 days) or older
-mtime 7169h, 191hbetween 168 and 192 hours
-mmin +10080169h, 191h, 193h, 241hmore than 10080 minutes (7 x 24 x 60)

The -mtime 7 row is the giveaway. Those two files are 7 days and 1 hour and 7 days and 23 hours old, and find files both of them under “7”, because it divides the age by 24 hours and ignores the fraction. So +7 means a whole-day count above 7, so the first file it can match is 8 days old. The GNU findutils manual documents this rounding, but almost nobody reads that part. The same shift applies to any number: for files older than 30 days, -mtime +29 is the precise form and -mtime +30 waits an extra day.

For a nightly cleanup of rotated logs, a one-day delay rarely matters, and plenty of scripts run +7 or +30 for years without anyone noticing. Where it bites is retention promises (“we keep seven days of uploads”) and disk-full emergencies, when you need the space the extra day is holding (find the heavy directories first with ncdu, then prune by age). In those cases we’d write the minutes out, because -mmin leaves no day-rounding question to argue about.

Find files older than X minutes with -mmin

-mmin works the same way as -mtime but counts minutes, and it has no whole-day rounding: -mmin +5 means strictly more than 5 minutes. That makes it the right test for short-lived files such as upload chunks, lock files and session data. Find the temp files older than 5 minutes:

find . -type f -mmin +5 -name "*.tmp"

Only the 6-minute file comes back. The 4-minute one is still young enough to be in use, which is exactly the behaviour you want from a temp-file sweep.

./m6.tmp

Keep the -name or -type f filter in every one of these commands. Without -type f, directories match as well, and once -delete is in the command find will remove any old directory that happens to be empty. Our find command guide covers the other tests (size, owner, permissions) you can combine with these.

Check the list before you delete

The safe habit is a dry run. Nothing else changes between the two runs below, so what you reviewed is what gets removed, unless a file crosses the age line in the seconds between the runs. It takes one extra command:

  1. Run the expression with -print at the end
  2. Read every path in the output
  3. Run the same line with -delete inserted before -print

Start with the dry run against the sandbox. If the list holds anything you did not expect, fix the tests before going further:

cd ~/logs
find . -type f -name "*.log" -mmin +10080 -print

Four logs are past seven days. Notice the order isn’t sorted, because find prints entries in the order the directory stores them. Pipe it through sort if you want to compare lists.

./app-169h.log
./app-193h.log
./app-191h.log
./app-241h.log

Now delete them. Keeping -print after -delete gives you a record of what went, which is worth having in a cron mail or a log:

find . -type f -name "*.log" -mmin +10080 -delete -print
ls

The same four names print, and ls confirms that only the newer files are left:

./app-169h.log
./app-193h.log
./app-191h.log
./app-241h.log
app-160h.log
app-167h.log
app-1h.log
app-30h.log
m4.tmp
m6.tmp

Every file younger than the cutoff is still there, and nothing outside the match list was touched.

Keep -delete at the end of the expression

find evaluates its expression left to right for every file, and -delete is an action, not an option. Put it before your tests and it runs on every file before the tests are even checked. We tried that on a copy of the sandbox with one extra file, keep.conf, that should have survived.

cp -a ~/logs ~/trap; touch ~/trap/keep.conf
find ~/trap -delete -name "*.log"; echo rc=$?; ls -la ~/trap

find exited 0 with no warning, and the directory was gone along with everything in it, including the config file.

rc=0
ls: cannot access '/home/josphat/trap': No such file or directory

That is the most expensive mistake on this page, because nothing tells you it happened until something is missing. So -delete goes last, after every -name, -type and age test. Build the command with -print, check the output, then add -delete just before -print, as in the run above, never earlier in the expression. Also note that -delete switches on -depth, so find processes a directory’s contents before the directory itself.

Use -daystart and -newermt for calendar cutoffs

Sometimes “old” means a calendar date rather than a number of hours, such as “anything from before today”, no matter what time the job runs. Two tests do that. The -daystart option measures ages from the start of today instead of from now, and -newermt compares against a timestamp you give it, with ! flipping it to “not newer than”. The two are easy to get off by one, so we tested them with a file from each of the last three days:

mkdir -p ~/ds && cd ~/ds
touch -d "yesterday 12:00" y.log; touch -d "2 days ago 12:00" d2.log; touch -d "today 01:00" t.log
find . -type f -daystart -mtime +0 | sort
find . -type f -daystart -mtime +1 | sort
find . -type f ! -newermt "$(date -d yesterday +%F) 00:00" | sort
find . -type f ! -newermt "$(date +%F) 00:00" | sort

The run was at 05:47 on October 18, with d2.log dated October 16 at noon, y.log October 17 at noon and t.log 01:00 today. The four tests printed these results; the -- lines are labels we echoed between the tests in the lab:

-- -daystart -mtime +0
./d2.log
./y.log
-- -daystart -mtime +1
./d2.log
-- ! -newermt yesterday 00:00
./d2.log
-- ! -newermt today 00:00
./d2.log
./y.log

So -daystart -mtime +0 means “modified before midnight today” and matches ! -newermt with today’s date, while -daystart -mtime +1 moves the cutoff back to midnight yesterday. Neither one touched t.log, the file from earlier this morning. If the job should keep yesterday’s files, use +1, not +0, and run the -print version once on a day when you can check the result.

We’d pick -newermt for anything a person reads later, because the cutoff is written in the command and there is no rounding rule to remember. The -daystart form suits scheduled jobs, where the date has to move on its own every day. The full set of time tests (-atime, -ctime, -newer) is covered in our LPIC-1 chapter on locating files.

Delete files older than 30 days and empty directories

Archive trees often sort files into one folder per month, so deleting old files leaves empty folders behind. Handle it in two passes: files first, then the directories that are now empty. This sets up two month folders with a 40-day-old file in each:

cd ~/logs
mkdir -p old/2026-09 old/2026-08
touch -d "40 days ago" old/2026-09/a.gz old/2026-08/b.gz

Remove the files older than 30 days with -exec rm, then delete the empty directories. The {} + ending passes many files to one rm call instead of starting rm once per file, and -mindepth 1 stops find from deleting the old directory you pointed it at:

find ./old -type f -mtime +30 -exec rm -v {} +
find ./old -mindepth 1 -type d -empty -delete -print
ls -R old

Both files went, then both month folders, and old itself is still there and empty. Leave out -mindepth 1 and find would remove old too once it was empty.

removed './old/2026-08/b.gz'
removed './old/2026-09/a.gz'
./old/2026-08
./old/2026-09
old:

-delete and -exec rm {} + do the same job here. We use -delete by default because it never forks rm at all, and keep -exec for when you need a flag like rm -v or want to swap in mv to an archive. The older -exec rm {} \; form still works but starts one process per file, which is slow on directories with tens of thousands of entries. For more on moving and removing files safely, see the files and directories chapter of our LPIC-1 series.

Run find as root on /var/log

System log directories are where most people go looking for old files, and as a normal user you cannot see all of them. Some subdirectories under /var/log are readable only by root, so a search there needs sudo, or it quietly skips whole trees.

find: ‘/var/log/private’: Permission denied

Running the age search on /var/log as josphat printed errors for the directories the user cannot enter.

find /var/log -type f -mtime +1 2>&1 | grep -m2 denied

Debian 13 keeps /var/log/private (used by services running with systemd’s DynamicUser) and /var/log/unattended-upgrades closed to normal users:

find: β€˜/var/log/private’: Permission denied
find: β€˜/var/log/unattended-upgrades’: Permission denied

Files under those directories are invisible to the search, so a cleanup run as a normal user leaves them in place while reporting success for everything else. Run the same search with sudo instead of hiding the errors with 2>/dev/null, which only hides the problem:

sudo find /var/log -type f -name "*.gz" -mtime +30 | head -3
sudo find /var/log -type f -mtime +1 | wc -l

On our fresh VM the first command printed nothing, because no compressed log was a month old yet, and the second counted 4 files more than a day old. On a long-running server expect hundreds. Before you delete anything under /var/log, check what owns it: rotated files belong to logrotate, which already has a retention count, and the journal is managed by journald. Adjust those tools with our logrotate guide rather than deleting behind their backs, and keep find for application logs that nothing else cleans up.

Automate cleanup with systemd-tmpfiles

Once you know which directory needs pruning, you don’t need a cron job wrapping find. systemd already runs systemd-tmpfiles --clean once a day through a timer, and any rule you drop into /etc/tmpfiles.d/ joins that run. A rule is one line of seven fields, and for a cleanup rule you fill in six:

  • type e: clean an existing directory, never create it
  • the path, here /var/log/myapp
  • mode, user and group as - to leave them alone
  • the age, such as 7d or 12h
  • an optional argument, left off for e

Create a test directory with files aged 2, 5, 9 and 12 days, then write the rule. A single line is easier to add with tee than an editor:

sudo mkdir -p /var/log/myapp
for d in 2 5 9 12; do sudo touch -d "$d days ago" /var/log/myapp/app-${d}d.log; done
echo "e /var/log/myapp - - - 7d" | sudo tee /etc/tmpfiles.d/myapp.conf
sudo systemd-tmpfiles --clean /etc/tmpfiles.d/myapp.conf; ls /var/log/myapp

Nothing was deleted. All four files are still listed, including the 9- and 12-day ones, and systemd-tmpfiles printed no error to explain why.

app-12d.log
app-2d.log
app-5d.log
app-9d.log

The change time and the birth time blocked it. By default tmpfiles looks at several of a file’s timestamps, not only the modification time, and goes by the newest. The touch -d option can backdate the access and modification times but not the change time (ctime) or the creation (birth) time, which both read as now on a freshly created file. You can see it with stat.

stat -c "%n ctime=%z" /var/log/myapp/app-12d.log

The 12-day-old file had a ctime from a few seconds earlier, so to tmpfiles it looked brand new.

/var/log/myapp/app-12d.log ctime=2026-10-18 03:48:41.038731516 +0000

This is not only a lab quirk. Restoring files from a backup, copying them with preserved times, or running chown or chmod across a tree all reset ctime, so a default rule can sit there for a week deleting nothing. Prefix the age with m: to judge by modification time only, which is what find’s -mtime does:

echo "e /var/log/myapp - - - m:7d" | sudo tee /etc/tmpfiles.d/myapp.conf
sudo systemd-tmpfiles --clean /etc/tmpfiles.d/myapp.conf; ls /var/log/myapp

This time the 9- and 12-day files are gone and the two younger ones stay. The capture shows both runs side by side:

systemd-tmpfiles deleting files older than 7 days by mtime on Debian 13

You don’t have to schedule anything else. Check that the daily timer is active and when it fires next:

systemctl list-timers systemd-tmpfiles-clean.timer --no-pager

On our VM it last ran a couple of minutes before the test and will run again 24 hours later. If your list comes back empty, the timer is not active on that host and your rule only runs when you call it by hand.

NEXT                        LEFT LAST                              PASSED UNIT                         ACTIVATES
Mon 2026-10-19 03:46:13 UTC  23h Sun 2026-10-18 03:46:13 UTC 2min 27s ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

The demo rule now cleans /var/log/myapp every day, so delete /etc/tmpfiles.d/myapp.conf when you are done testing, or point it at your real application directory and adjust the age.

For a plain “delete files older than N days in this directory” job, this is what we’d run on any systemd host: one line in /etc/tmpfiles.d/, no script, and a timer that already exists. The m: age prefix is described in the tmpfiles.d manual; we only tested it on Debian 13 with systemd 257, so check that page against your release before relying on it on an older distro. Keep find for anything tmpfiles can’t express, such as name patterns, size limits or the empty-directory pass above, and schedule it with a timer as shown in our systemd timers guide.

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) Introduction To Ansible Automation on Linux – Understanding Ansible Linux Mint Introduction To Ansible Automation on Linux – Understanding Ansible

Leave a Comment

Press ESC to close