From Detection to Deletion: Safely Removing Duplicate Files

Remove duplicates natively, or export a cleanup plan to run yourself

What You'll Learn
  • How to choose the right keep-strategy for each set of duplicates
  • Removing duplicates directly with duplicates remove — dry-run preview, guardrails, and what recovers if you change your mind
  • Exporting removal plans as paths, JSON, rclone, PowerShell, or Bash, for when you want to run the deletion yourself
  • Executing an exported plan safely with a review-first workflow
  • Verifying results and re-scanning to confirm the space is reclaimed

You have already scanned your remotes and confirmed that duplicates exist. Now comes the part that actually frees up space: deciding which copy of each file to keep, and removing the rest without putting a single original file at risk. FileFortress gives you two ways to do that — delete the redundant copies directly with duplicates remove, or export a removal plan and run it yourself with rclone or a script. This guide covers the keep-strategy decision both paths share, then walks through each in turn.

If you are still unsure how duplicates are found and grouped, start with the conceptual Duplicate Management workflow guide, then return here to act on what you found. That guide covers the detection mechanics in depth; this one picks up at the decision point and carries you all the way through to verified, reclaimed storage.

Two Ways to Remove Duplicates

FileFortress can delete the redundant copies of a duplicate group directly — duplicates remove on the CLI, or the desktop GUI's Remove Duplicates button — both guarded by a dry-run preview, hash-verified and same-remote defaults, and a confirmation step before anything is deleted. If you would rather review a plan and run the deletion yourself, FileFortress can instead export a removal plan — a list of paths or a ready-to-run script — for you to execute with rclone or a shell. Either way, nothing is deleted without a preview you can inspect first.

Prerequisites

Before removing anything, make sure your local index is current and trustworthy. A removal plan — native or exported — is only as accurate as the scan it was built from.

  • A completed scan of every remote you want to clean. Duplicate detection works across all configured remotes, so an out-of-date remote can hide or invent matches.
  • Hashes available, if you want guaranteed matches. Hash verification needs file hashes, which come either from provider metadata or from local hashing via the tools run command (the built-in FileHasher tool).
  • rclone or a shell, only if you plan to export. Native removal needs nothing extra; exporting a plan hands you data that you still need a way to act on.

FileFortress supports Google Drive, OneDrive, AWS S3, Backblaze B2, and Local storage, and duplicate detection spans all of them at once — so a single plan can de-duplicate files that are scattered across multiple clouds.

1. Detect: Name & Size vs. Hash

Detection produces two kinds of groups, and the distinction matters enormously once you start deleting. Name & Size matching is a fast heuristic: two files with the same name and the same byte count are probably identical. Hash verification compares an MD5 or SHA256 checksum and tells you, with certainty, that the bytes are the same.

# Find duplicates across all configured remotes
filefortress find duplicates

# Restrict to hash-confirmed groups only (safest)
filefortress find duplicates --hash-verified-only

The summary separates the two: a count of Name & Size groups, a count of hash-verified groups, a guaranteed space-savings figure for the hash groups, and a larger potential-savings figure that includes the heuristic matches. Both removal paths below default to the hash-verified set — treat that guaranteed number as the one you can stand behind.

Hashes do not appear by magic. They come from one of two sources: provider metadata, when a cloud storage API exposes a checksum for the file, or local hashing, when FileFortress downloads a file and computes the hash itself through the FileHasher tool. If --hash-verified-only returns fewer groups than you expected, it usually means many of your files simply do not have hashes yet. Running local hashing first widens the set of guaranteed matches and, in turn, the amount of space you can safely reclaim. The enrichment guide explains exactly where each kind of hash originates.

Confidence: Hash vs. Name-Only

Two files can share a name and size and still differ in content — think of report.pdf at exactly 2 MB in two different folders. Both removal paths leave these name+size-only matches out by default; including them takes a deliberate opt-in (--include-name-size-matches for native removal, plain find duplicates without --hash-verified-only for export), not something that happens by accident.

2. Review the Groups

Before you commit to a strategy, look at the actual groups. There are two convenient ways to do this. In the desktop GUI, open the Duplicates page to browse groups visually, expand each one, and see exactly which remote every copy lives on. On the command line, the interactive explorer walks you through the same data.

# Default summary view
filefortress find duplicates --view summary

# Launch the interactive explorer to browse groups and files
filefortress find duplicates

The interactive explorer lets you page through Name & Size groups, hash-verified groups, and a "top duplicate groups" list sorted by how much space each group would reclaim. Drilling into a group shows every copy with its remote, full path, size, and last-modified date — the exact attributes the keep-strategies act on. Spending a few minutes here pays off, because it is where you confirm that the groups represent files you genuinely want to de-duplicate.

The "top duplicate groups" list deserves special attention. Because it is sorted by reclaimable space, it surfaces the handful of groups that account for most of your wasted storage. A single 4 GB video duplicated three times reclaims far more than a thousand tiny text files, so reviewing the top of the list first lets you make the biggest gains with the least risk. It also tends to expose the groups where a careful keep-strategy choice matters most — large media files are exactly where keeping the wrong copy is most costly.

3. Choose a Keep-Strategy

For every duplicate group, FileFortress keeps exactly one file and marks the rest for removal. The --keep-strategy option decides which copy survives, and it is the same option whether you remove natively or export a plan. The default is oldest, which is a sensible, conservative choice for most libraries.

Strategy Keeps When to Use It
oldest The earliest-modified copy The safe default. Preserves the original you first saved and treats later copies as redundant.
newest The most recently modified copy When the latest copy reflects the canonical version and older ones are stale leftovers.
first The first copy encountered in the group When you want a deterministic, predictable pick and modification dates are unreliable.
smallest The copy with the fewest bytes For Name & Size groups where you suspect a compressed or trimmed version is the keeper.
largest The copy with the most bytes When the bigger file is the higher-fidelity original (full-resolution photos, uncompressed exports).
by-remote The copy on a remote you name Consolidating onto one provider — keep everything on your primary cloud and prune the rest.

The by-remote strategy is special: it requires a companion --keep-remote value naming the remote whose copy should always survive. If you ask for by-remote without --keep-remote, or you name a remote that does not exist, the command stops with a clear error instead of guessing.

# Keep the newest copy in each group
filefortress find duplicates --export-format paths --keep-strategy newest

# Consolidate onto one provider: keep whatever lives on "My Drive"
filefortress duplicates remove --keep-strategy by-remote --keep-remote "My Drive"

4. Preview Before You Act

Never jump straight to a destructive action. Whichever path you choose, look at a preview first — it reports how many groups were processed, how many files are marked for removal, and how much space the plan will reclaim. If the file count or reclaimed-space figure looks wildly off, your scan, strategy, or detection mode needs another look before anything runs.

Native removal previews automatically. duplicates remove defaults to a dry run, so running it with no extra flags is inherently safe:

# Dry-run is the default — this previews the plan and deletes nothing
filefortress duplicates remove --keep-strategy oldest

The export path previews the same way when you leave off an output file:

# Preview the plan as a plain list of paths, printed to the screen
filefortress find duplicates --export-format paths --hash-verified-only

# Read the summary line: groups processed, files to delete, space to reclaim

Scan the list for anything that looks like a unique original rather than a redundant copy. Only once the preview matches your intent should you move toward execution — confirming the native run, or saving the export to a file.

5. Remove Duplicates Natively

The fastest path from here is duplicates remove — it deletes the redundant copies of a duplicate group directly. It shares its keep-strategy options with find duplicates, and it acts on the exact same plan the preview showed you, not a separately derived one.

# Preview only — the default, and always safe to run
filefortress duplicates remove --keep-strategy oldest

# Review the plan, then confirm interactively
filefortress duplicates remove

# Execute non-interactively, e.g. from a script or cron
filefortress duplicates remove --non-interactive --keep-strategy oldest --confirm

The same guardrails apply no matter which surface you use — CLI, interactive prompt, or the desktop GUI's Remove Duplicates button on the Duplicates page, since all three call the same underlying safety logic:

  • Dry-run by default. Nothing is deleted until you pass --confirm, accept the interactive prompt, or confirm in the GUI modal.
  • Hash-verified groups only, by default. Add --include-name-size-matches to also remove groups matched by name and size alone — a deliberate choice, not a habit.
  • Same-remote copies only, by default. A file duplicated across two clouds is often a deliberate backup, not waste. Add --across-remotes once you have confirmed otherwise.
  • The last copy of a group is never deleted. The keeper is re-verified to still exist immediately before its siblings are removed; a group whose keeper has vanished is skipped entirely.
  • Provider trash is preferred over permanent deletion wherever the provider offers one. Google Drive and OneDrive both recover for roughly 30 days unless you pass --permanent.
Google Drive Needs a Write-Capable Connection

The default, one-click Google Drive connection only requests read access — enough to scan and download, but not enough to delete or trash a file. To remove Google Drive duplicates natively, connect that remote through Bring-Your-Own OAuth with a write-enabled scope. Otherwise, export a plan instead (next section).

See the full duplicates remove command reference for every option, recoverability by provider, and more examples.

6. Or: Export a Plan and Run It Yourself

Prefer full manual control — reading a script line by line, editing out individual files, or feeding the plan into your own tooling? Export a removal plan instead. This is also the path to reach for on the default (read-only) Google Drive connection, and for any workflow where you want an audit artifact independent of FileFortress's own execution.

The --export-format option turns the duplicates into an artifact you can act on. There are five formats, each suited to a different downstream workflow.

Format What You Get
paths A plain list of the files marked for removal — ideal for review or feeding into your own tooling.
json Structured data with group and summary detail — best for auditing or programmatic processing.
rclone rclone delete commands — the cleanest way to remove files directly from cloud remotes.
powershell A PowerShell script — natural on Windows or for Local storage cleanup.
bash A Bash script — natural on macOS and Linux.

Use --output-file (or its short form -o) to write the plan to disk instead of the console. When you save to a file, FileFortress also prints a summary of groups processed, files to delete, and space to reclaim, so you get a confirmation receipt for the artifact.

# Export an rclone plan for hash-verified duplicates, keeping the oldest
filefortress find duplicates \
  --export-format rclone \
  --hash-verified-only \
  --keep-strategy oldest \
  -o cleanup.sh

# Export a JSON audit record of the same decision
filefortress find duplicates \
  --export-format json \
  --hash-verified-only \
  --output-file duplicates-plan.json

If you want the kept file recorded alongside the ones being removed, add --include-keep-file. The kept file is written as a comment, so it never becomes a delete target — it is there purely so you can see, for each group, which copy survived. That makes a script far easier to audit before you run it.

7. Execute the Exported Plan

This step only applies if you exported a plan in the previous section rather than using native removal — if you already ran duplicates remove --confirm, the deletion already happened and you can skip ahead to verifying it.

You now hold a plan, not a fait accompli. The execution step is where the deletion actually happens, and it is entirely in your hands. The golden rule is review first, run second. Because the export hands off a static artifact rather than deleting anything itself, there is a clean boundary between detection and destruction: everything up to this point is reversible simply by choosing not to run the plan.

Using rclone

For cloud remotes, the rclone export gives you delete commands you can read line by line. Open the file, confirm the targets are the redundant copies you expected, and then run it. rclone performs the deletion against the provider — FileFortress is no longer involved at this point.

# 1. Read the plan end-to-end before doing anything
cat cleanup.sh

# 2. Run it only after you are satisfied with every target
bash cleanup.sh

Using a Shell Script

The PowerShell and Bash exports are ordinary scripts. Review them the same way, then execute with your shell. Because they are plain text, you can also delete or comment out any individual line you are unsure about, turning the generated plan into a curated one.

Mind the --include-keep-file Output

When you export with --include-keep-file, the kept copy appears in the output as a comment. Do not uncomment those lines or convert them into delete commands while editing — doing so would target the very file you intended to preserve. The comment is a reference, not a to-do.

Whichever path you take, consider running against one group or one remote first as a small-scale trial. A successful trial deletion builds confidence before you turn the full plan loose on thousands of files.

8. Verify and Re-scan

Cleanup is not finished when the deletion runs — it is finished when a fresh scan confirms the duplicates are gone. This applies whether you removed natively or ran an exported script. Re-scan the affected remotes so your local index reflects the deletions, then run detection again to check the numbers moved in the direction you expected.

# Re-scan your remotes so the index reflects the deletions
filefortress remotes scan

# Confirm the duplicate count and reclaimable space have dropped
filefortress find duplicates --hash-verified-only

The guaranteed space-savings figure should fall, and the groups you cleaned should no longer appear. If a group lingers, the deletion may not have completed on that provider, or the remote may not have been re-scanned yet — re-run the scan for that remote and check again. Native removal already keeps the local index in sync with every successful deletion, so a lingering group after a native run is a strong signal that the underlying provider delete failed; check the run's error summary for that file.

Keep a Record

Export a json plan alongside whichever removal path you use, and keep it. It is a precise record of which files were slated for removal, which copy was kept in each group, and how much space the operation was expected to reclaim — invaluable if anyone later asks what happened to a file.

Putting It All Together

Here are two full sequences for a cautious, repeatable cleanup — one native, one exported. Both assume you have already scanned your remotes and want guaranteed results.

Native Removal

# 1. Make sure hashes exist for guaranteed matching
filefortress tools run --remote "My Drive"

# 2. Preview the plan (dry-run is the default — deletes nothing)
filefortress duplicates remove --keep-strategy oldest

# 3. Confirm and execute
filefortress duplicates remove --keep-strategy oldest --confirm

# 4. Re-scan and confirm the duplicates are gone
filefortress remotes scan
filefortress find duplicates --hash-verified-only

Export and Run Yourself

# 1. Make sure hashes exist for guaranteed matching
filefortress tools run --remote "My Drive"

# 2. Look at what detection found
filefortress find duplicates --hash-verified-only

# 3. Preview the plan on screen before saving anything
filefortress find duplicates --export-format paths \
  --hash-verified-only --keep-strategy oldest

# 4. Save an executable rclone plan and a JSON audit record
filefortress find duplicates --export-format rclone \
  --hash-verified-only --keep-strategy oldest \
  --include-keep-file -o cleanup.sh
filefortress find duplicates --export-format json \
  --hash-verified-only --keep-strategy oldest \
  -o duplicates-plan.json

# 5. Read the script, then execute it
cat cleanup.sh
bash cleanup.sh

# 6. Re-scan and confirm the duplicates are gone
filefortress remotes scan
filefortress find duplicates --hash-verified-only

Both sequences are deliberately conservative: they hash first, restrict to guaranteed matches, preview before anything is destructive, and re-scan to verify. Once you trust the workflow, you can streamline it — but starting strict is how you learn what each strategy and format does to your data without risking anything.

Best Practices

  • Lead with hash verification. For anything you would be upset to lose, stick to the hash-verified default on both paths rather than opting in to name+size matches.
  • Match the strategy to the group. Photos and exports often favor largest; consolidation favors by-remote; archival libraries favor the default oldest.
  • Let the dry-run happen. duplicates remove previews by default for a reason — read the plan before adding --confirm, every time.
  • Treat cross-remote copies with suspicion, not convenience. The same file on two clouds is frequently a deliberate backup. Reach for --across-remotes only after you've confirmed it's actually waste.
  • Trial on a subset first. Run the cleanup against one remote or one group before unleashing the full plan.
  • Re-scan to close the loop. A cleanup is only verified once a fresh scan shows the duplicates gone.

Common Pitfalls

Deleting From a Stale Index

If files changed on a remote after your last scan, the plan can reference paths that have moved or matches that no longer hold. Always act from a current scan, and if a cleanup spans days, re-scan before the final run.

A few other traps are worth naming. Forgetting --keep-remote with the by-remote strategy stops the command — supply the remote name. Choosing smallest when the larger file is the true original will quietly discard quality. Assuming --permanent can be undone is a mistake — it skips the provider's trash entirely, and on Local storage there is no recycle bin to fall back on regardless. And running a plan (native or exported) that you have not previewed is how unique files get lost; the review-first habit is the single most reliable safeguard in this entire workflow.

Related Guides