rmstale is a cross-platform command-line tool to remove stale files and empty directories recursively below a given directory.
- Features
- Install instructions
- Usage instructions
- Trust model / Security
- Contribution
- Release Process
- License
- Remove files and empty directories older than a specified age
- Cross-platform: Windows, Linux, macOS
- Dry-run mode for safe testing
- Extension-based file filtering
- Logs errors (e.g., permission issues)
- Designed for automation and scheduling
Some example use cases:
- Set on a schedule to clear old files from your temporary directories
- Set on a schedule to remove downloaded files from your downloads directory
choco install rmstalewinget install danstis.rmstaleVisit the releases page for the latest version, or use:
# Fetch the latest release tag from GitHub
latest_version=$(curl -s https://api.github.com/repos/danstis/rmstale/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')
# Download the latest version tarball
curl -L -o rmstale.tar.gz "https://github.com/danstis/rmstale/releases/download/$latest_version/rmstale_${latest_version#v}_linux_amd64.tar.gz"
# Extract and install
sudo tar -xzf rmstale.tar.gz -C /usr/local/bin rmstale
# Cleanup
rm rmstale.tar.gzVisit the releases page for the latest version, or use the commands below.
# Fetch the latest release tag from GitHub
latest_version=$(curl -s https://api.github.com/repos/danstis/rmstale/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')
# Download the latest version tarball for Apple Silicon
curl -L -o rmstale.tar.gz "https://github.com/danstis/rmstale/releases/download/$latest_version/rmstale_${latest_version#v}_Darwin_arm64.tar.gz"
# Extract and install
sudo tar -xzf rmstale.tar.gz -C /usr/local/bin rmstale
# Cleanup
rm rmstale.tar.gz# Fetch the latest release tag from GitHub
latest_version=$(curl -s https://api.github.com/repos/danstis/rmstale/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')
# Download the latest version tarball for Intel
curl -L -o rmstale.tar.gz "https://github.com/danstis/rmstale/releases/download/$latest_version/rmstale_${latest_version#v}_Darwin_x86_64.tar.gz"
# Extract and install
sudo tar -xzf rmstale.tar.gz -C /usr/local/bin rmstale
# Cleanup
rm rmstale.tar.gz- Download the latest binary for your OS from the GitHub releases page.
- Place the downloaded file into a directory in your PATH.
| Flag | Description | Default |
|---|---|---|
| -a, --age | Period in days before an item is considered stale. (REQUIRED) | (required) |
| -d, --dry-run | Runs the process in dry-run mode. No files will be removed, but the tool will log the files that would be deleted. | false |
| -e, --extension | Filter files for a defined file extension. This flag only applies to files, not directories. | (empty) |
| -p, --path | Path to a folder to process. Resolved to an absolute, cleaned path before use. Refused when it equals a protected filesystem root (see --allow-system-paths). |
system temp dir |
| --allow-system-paths | Permit rmstale to operate on protected system roots (/, C:\, /etc, /usr, /var, /boot, /proc, /sys, or the current user's home directory). Sub-paths remain reachable by default. |
false |
| --prune-empty-dirs | Remove empty directories even if they are not stale. | false |
| -v, --version | Displays the version of rmstale that is currently running. | false |
| -y, --confirm | Allows for processing without confirmation prompt, useful for scheduling. | false |
rmstale refuses to descend into exact-match protected filesystem roots unless
--allow-system-paths is set. The protected roots are:
/(filesystem root on Linux/macOS) andC:\(drive root on Windows)/etc,/usr,/var,/boot,/proc,/sys(Linux/macOS system roots)- the current user's home directory (
os.UserHomeDir())
Sub-paths of those roots — for example /var/tmp, /home/user/data, or
C:\Users\<user>\AppData\Local\Temp — remain reachable without the override so
legitimate staging areas keep working out of the box. Scheduled jobs that
genuinely need to clean files inside a protected root should pass
--allow-system-paths after reviewing the consequences (a typo in the path
could destroy system state).
rmstale --version
# rmstale v1.2.3rmstale --age 14 --path c:\temp
# WARNING: Will remove files and folders recursively below 'c:\temp' older than 14 days. Continue?: y
# -Removing 'C:\Temp\amc2E40.tmp.LOG1'...
# ...rmstale --age 14 --path /tmp
# WARNING: Will remove files and folders recursively below '/tmp' older than 14 days. Continue?: y
# -Removing '/tmp/oldfile1'...
# ...rmstale --age 30 --path ~/Downloads --dry-run
# [DRY RUN] '/home/user/Downloads/oldfile.txt' would be removed...
# ...Any errors encountered during the deletion process (e.g., permission issues) will be logged.
rmstale decides whether a file or directory is stale from its modification time (ModTime) as reported by the operating system. This metadata is trusted but not verified: on every supported platform the file owner can change it freely using tools like touch -d, os.Chtimes, PowerShell's LastWriteTime, or the file properties dialog. Nothing rmstale reads about a file is cryptographically signed or tamper-evident.
In practice this means:
- A file that should be removed can be pinned past the cleanup window by setting its
ModTimeto the future (e.g.touch -d "2099-01-01" file). - Conversely, a file can be forced into deletion by back-dating its
ModTime, which also lets an empty parent directory be pruned on the next run.
Recommendations for operators running rmstale against shared or untrusted directories (CI runners, scratch storage, multi-tenant /tmp directories, HPC workspaces, etc.):
- Run rmstale as a single, dedicated user that owns the directories being cleaned up. Treat that user's
ModTimewrites as the only ones you trust. - Run rmstale from a privileged context (e.g. a scheduled task, cron job, or service account) so that any tampering by other users with write access to the metadata cannot bypass the policy.
- Avoid running rmstale against directories writable by users whose files you would not want to keep or delete; the
ModTimeit sees is the one those users set. - Use
--dry-runfirst when pointing rmstale at a new path to confirm the age threshold and ownership assumptions match your expectations.
Feedback, issues, and contributions are welcome! For bugs, issues, or feature requests, please create an issue on the GitHub issues page.
Want to contribute? Please:
- Fork the repo and clone it locally
- Use Go 1.24 or above
- Format code with
gofmt -wand check withgolangci-lint - Add or update tests (
go test ./...) - Follow the CONTRIBUTING.md guidelines
- Create a pull request with a clear description
This project follows Semantic Versioning. To create a new release:
- Ensure all changes are committed to the main branch via a pull request
- The release pipeline will automatically generate a tag if the build number is a clean semver version (e.g.,
v1.2.3) - Alternatively, manually tag a commit with a version number (
v1.2.3) and push the tag - The CI/CD pipeline will build and publish the release
This project is licensed under the MIT License.