Thomas Boots' Personal blog


FreeFileSync Mirror your SSD to HDD on Mac OS

Published: January 10, 2026

FreeFileSync Mirror Backup (SSD → HDD) on macOS

Automated Schedule via launchd (Only Runs When Mounted)

This guide details how to set up an automated Mirror backup using FreeFileSync. The backup will run on a schedule (every 3 hours), but only if both the source SSD and destination HDD are connected.

System Configuration

  • Source (SSD): X10 Pro (Mounted at /Volumes/X10 Pro)
  • Destination (HDD): Thomas-HDD (Mounted at /Volumes/Thomas-HDD)
  • User: thomas (Home directory /Users/thomas)

⚠️ Warning: Mirror Backup is Destructive A "Mirror" sync deletes files on the destination (HDD) that are not present on the source (SSD). If you accidentally delete a file on your SSD, it will be deleted from the backup during the next run. Enable "Versioning" in FreeFileSync settings to mitigate this risk.


1. Install FreeFileSync

  1. Download and install FreeFileSync.
  2. Verify the installation path via Terminal:
    ls "/Applications/FreeFileSync.app/Contents/MacOS/FreeFileSync"
    

2. Create the Batch Job

  1. Connect both drives.
  2. Open FreeFileSync.
  3. Configure Paths:
    • Left (Source): /Volumes/X10 Pro
    • Right (Destination): /Volumes/Thomas-HDD
  4. Settings:
    • Click the Synchronization Settings (green gear icon).
    • Select Mirror.
    • (Optional) Enable Versioning here if you want to keep deleted files in a separate folder.
  5. Filters (Recommended):
    • Click the Filter icon (red funnel).
    • Add the following to the Exclude list to prevent system file errors (for me this actually happened automatically):
      /.Spotlight-V100/**
      /.Trashes/**
      /.fseventsd/**
      /.TemporaryItems/**
      
  6. Save as Batch:
    • File → Save as Batch Job...
    • Save As: SyncSettings_SSD_HDD_MIRROR.ffs_batch
    • Location: /Users/thomas/Documents/SyncJobs/ (Create this folder if needed).
    • Important: Check the box "Auto-close" (Run minimized is also a good option).

3. Create the Wrapper Script

We need a script to check if the drives are actually mounted before triggering FreeFileSync.

  1. Create a directory for your scripts:
    mkdir -p ~/Scripts
    
  2. Create and edit the script file:
    nano ~/Scripts/run_mirror_if_mounted.sh
    
  3. Paste the following code:
#!/bin/bash
set -euo pipefail

SSD_VOLUME="/Volumes/X10 Pro"
HDD_VOLUME="/Volumes/Thomas-HDD"

JOB="$HOME/Documents/SyncJobs/SyncSettings_SSD_HDD_MIRROR.ffs_batch"
FFS="/Applications/FreeFileSync.app/Contents/MacOS/FreeFileSync"

[[ -d "$SSD_VOLUME" ]] || exit 0
[[ -d "$HDD_VOLUME" ]] || exit 0

TITLE="FreeFileSync Mirror"
SUBTITLE="X10 Pro → Thomas-HDD"

if "$FFS" "$JOB" >/tmp/freefilerunsync.last.out 2>/tmp/freefilerunsync.last.err; then
  /usr/bin/osascript -e "display notification \"Sync succeeded\" with title \"$TITLE\" subtitle \"$SUBTITLE\""
else
  /usr/bin/osascript -e "display notification \"Sync FAILED (see /tmp/freefilerunsync.last.err)\" with title \"$TITLE\" subtitle \"$SUBTITLE\""
fi
  1. Save and exit (Ctrl+O, Enter, Ctrl+X).
  2. Make executable:
    chmod +x ~/Scripts/run_mirror_if_mounted.sh
    
  3. Test Run: With both drives plugged in, run:
    ~/Scripts/run_mirror_if_mounted.sh
    

4. Create the Schedule (launchd)

We will use launchd to run the script every 3 hours.

  1. Create the user LaunchAgents directory:

    mkdir -p ~/Library/LaunchAgents
    
  2. Create the property list file:

    nano ~/Library/LaunchAgents/com.thomas.freefilerunsync.plist
    
  3. Paste the following XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "[http://www.apple.com/DTDs/PropertyList-1.0.dtd](http://www.apple.com/DTDs/PropertyList-1.0.dtd)">
    <plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.thomas.freefilerunsync</string>
    
        <key>ProgramArguments</key>
        <array>
            <string>/bin/bash</string>
            <string>/Users/thomas/Scripts/run_mirror_if_mounted.sh</string>
        </array>
    
        <key>RunAtLoad</key>
        <true/>
    
        <key>StartInterval</key>
        <integer>10800</integer>
    
        <key>StandardOutPath</key>
        <string>/tmp/freefilerunsync.out</string>
        <key>StandardErrorPath</key>
        <string>/tmp/freefilerunsync.err</string>
    </dict>
    </plist>
    
  4. Save and exit (Ctrl+O, Enter, Ctrl+X).


5. Load and Activate

  1. Unload (cleanup previous versions) and Load:
    launchctl unload ~/Library/LaunchAgents/com.thomas.freefilerunsync.plist 2>/dev/null
    launchctl load ~/Library/LaunchAgents/com.thomas.freefilerunsync.plist
    
  2. Start manually to verify:
    launchctl start com.thomas.freefilerunsync
    

6. Verification and Maintenance

Checking Logs

  • Script Output:
    cat /tmp/freefilerunsync.out
    
  • Script Errors:
    cat /tmp/freefilerunsync.err
    
  • FreeFileSync HTML Logs: Located at ~/Library/Application Support/FreeFileSync/Logs/

Managing the Job

  • Stop the job: launchctl stop com.thomas.freefilerunsync
  • Disable completely: launchctl unload ~/Library/LaunchAgents/com.thomas.freefilerunsync.plist

Changing the Interval

To change the frequency, edit the StartInterval integer in the .plist file:

  • 7200 = 2 Hours
  • 10800 = 3 Hours
  • 14400 = 4 Hours

Note: You must unload and reload the plist for changes to take effect.

After editing, reload:

launchctl unload ~/Library/LaunchAgents/com.thomas.freefilerunsync.plist

launchctl load ~/Library/LaunchAgents/com.thomas.freefilerunsync.plist

Notes / Safety Tips

Mirror is destructive on the destination (it deletes files on the HDD that are not on the SSD). If you want protection from accidental deletions, enable Versioning in FreeFileSync’s sync settings. (and write to another drive, like your own pc's) Excluding macOS system folders at the drive root reduces noise and unnecessary changes. That’s it: you now have a scheduled Mirror backup from SSD → HDD that runs every few hours only when both drives are plugged in.


← Back to Blog