Safely Defragment Selected XFS Files with xfs_fsr
A database file that keeps getting slower to read is often just badly fragmented, and xfs_fsr fixes that. It copies the file's data to temporary space and swaps in tidier extents. You will finish with a controlled run against a chosen XFS file or mount point, a clear runtime limit, and a way to confirm what happened. It is not a general filesystem repair tool.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow a few minutes to inspect the target and start a small run. A real defragmentation may take much longer, depending on file size, extent count, free space and storage speed. The examples below match the Ubuntu xfsprogs 6.6.0 installation used for this guide.
Before you start
- Confirm the target is XFS. Run this as your normal user:
findmnt --types xfs --output TARGET,SOURCE,FSTYPE,OPTIONS
xfs_fsr -V
The first command lists mounted XFS filesystems. The second should report xfs_fsr version 6.6.0 on the stated installation. If the target is not listed by findmnt, stop: xfs_fsr applies only to XFS filesystems.
- Check free space and identify the exact path.
df -hT /srv/xfs-data
findmnt --target /srv/xfs-data
Replace /srv/xfs-data with a real path. The path may be a file inside an XFS mount, or the mount point itself. The command needs enough free space to make a temporary copy of each file it improves. Quotas can also prevent the copy even when df appears healthy.
1. Choose the scope
Start with one known file. A file argument makes one pass over that regular file and does not use the persistent whole-filesystem state. This is the easiest scope to review and repeat:
sudo xfs_fsr -v -- /srv/xfs-data/path/to/large-file.db
sudo is commonly required because the operation changes filesystem allocation, although the precise permission outcome depends on the file and mount. The -- separates options from the path. With -v, expect cryptic information about files being considered. A busy file may produce a file-busy warning and be skipped; close applications using the file and retry if that matters.
To process all regular files on one mounted XFS filesystem, pass its mount point instead:
sudo xfs_fsr -v -- /srv/xfs-data
This is broader and can run for a long time. A filesystem argument does not mean "optimise only the directory entry": it tells xfs_fsr to traverse the filesystem and process its regular files.
2. Limit a whole-filesystem run
For a scheduled maintenance window, use -t with no explicit file or filesystem arguments. The default global run lasts up to 7200 seconds, or two hours, and uses up to 10 passes. For a shorter trial, set a small time limit and a pass count:
sudo xfs_fsr -v -t 900 -p 1
This asks for one global pass and up to 900 seconds. With no positional arguments, the command reads the filesystem list from /etc/mtab, considers mounted filesystems, and skips entries that do not specify read-write access. It records where it stopped in /var/tmp/.fsrlast_xfs, so a later no-argument run can continue from that point.
Do not combine that form with a target when you expect the timer or state file to apply. The -m, -t and -f options have no meaning when filesystems or files are supplied on the command line. For a targeted run, the command makes one pass through each supplied regular file or filesystem and does not read or write the persistent state file.
3. Watch the run and troubleshoot
Use -v when you need per-file information. Use -d only while diagnosing a problem, because it prints even more low-level, cryptic information. The -g option sends messages to syslog. It is also the default when standard output is not a terminal, so a job launched by a scheduler may not show its messages in the terminal or job log.
sudo xfs_fsr -v -t 60 -p 1 -- /srv/xfs-data/path/to/large-file.db
- If a file is skipped, check that it is a regular file, is not currently mapped in memory, and has sufficient free space and quota for the temporary copy.
- Some files are ignored outright. Symbolic links to ordinary files, FIFOs and Unix-domain sockets are ignored, with a warning for a command-line name. Files marked no-defrag are skipped; an XFS administrator can inspect or change that flag with the
chattrcommand inxfs_io, but do not clear an intentional policy flag just to force a run. - Temporary files are hidden by design. For a targeted run, the temporary file for a command-line file is created in its parent directory and starts with
.fsr. Temporary files for a whole-device run are kept at the root of that device. They are unlinked when created, so another process cannot read them by name, though the operation still consumes free space while it runs.
4. Know the safety boundaries
Warning
Do not start a whole-machine run casually. The no-argument form can traverse every mounted filesystem listed in /etc/mtab. The manpage specifically advises that system partitions such as /, /boot and /usr generally do not need this treatment. Boot files used by lilo need extra care: if they are moved, the boot loader may need to be rerun before rebooting. Exclude such files rather than discovering this after a maintenance window.
Defragmentation changes allocation, not file contents, but it is still a storage-intensive operation. Schedule it away from heavy I/O, keep a current backup, and stop before the run if free space is marginal. There is no single undo command: if you need to reverse the allocation layout, restore the affected data from backup or create a new copy on a suitably prepared filesystem.
5. Verify the result
There is no summary percentage in the interface described by this version of xfs_fsr. Verification is therefore operational: confirm that the command completed without a space, busy-file or permission warning, and check the target's filesystem and space afterwards.
findmnt --target /srv/xfs-data/path/to/large-file.db
df -hT /srv/xfs-data
For a repeatable maintenance record, save the exact command, start and finish times, target path, and any verbose warnings. If you need more evidence about extents, inspect the file with an appropriate XFS-aware diagnostic tool available on your system; do not treat a clean exit alone as proof that every file was reorganised.
Done means
- Target confirmed. The target is a mounted XFS filesystem and the installed version is known.
- Capacity checked. You checked free space and quota before starting.
- Scope deliberate. You used an explicit file or mount point, or deliberately reviewed the no-argument scope.
- Window respected. The runtime and pass count fit the maintenance window.
- Warnings kept. You reviewed verbose output and retained any warnings.
- Result checked. You checked the mount and free space after the run.