Two-Way Syncing a Windows Desktop to a Storage Box with rclone bisync and Task Scheduler

Two Windows laptops, one SMB storage box, no OneDrive: rclone bisync every 10 minutes via Task Scheduler, with the resync, filter and PST gotchas I hit.

Share
Two-Way Syncing a Windows Desktop to a Storage Box with rclone bisync and Task Scheduler

A family member had two Windows laptops, a storage box full of old stuff, and a desktop that had quietly been living inside OneDrive for years. One laptop's Desktop was redirected to a personal OneDrive, the other's to a business OneDrive that hadn't been connected in months. Nobody knew which files were where. The goal was simple: one place for the files (the storage box), and the Desktop on each laptop synced to it automatically, both ways, without anyone having to think about it.

I didn't want another sync client with a tray icon. What I set up instead is rclone's bisync, a small PowerShell wrapper and a scheduled task that runs every 10 minutes.

TL;DR: Store the box credentials with cmdkey so the task can reach the share unattended. Run rclone bisync <Desktop> <\\box\share\folder> with --conflict-resolve newer and a filter file that excludes *.pst, *.ost, ~$*, Thumbs.db and desktop.ini. The first run (and every run after a filter change) needs --resync, so the wrapper keeps a marker file. Give each laptop its own target folder. Get Outlook data files out of any synced folder before you start.

The layout

On the box:

\\u123456.your-storagebox.de\backup\
  Data\              <- sorted archive, "work folder" lives here
  Outlook\           <- old mailbox exports, not synced
  alice\Desktop\     <- sync target laptop 1
  alice\Desktop-NB2\ <- sync target laptop 2

The two laptops sync into different folders on purpose. If both bisync the same target, laptop 2 sees laptop 1's changes as remote changes and pulls them, laptop 1 sees laptop 2's deletions and applies them, and you end up with two Desktops slowly merging into one. That might be what you want for a shared folder, but not for "my Desktop". Shared stuff goes into the work folder on the box, and both Desktops just have a shortcut to it.

Credentials: cmdkey, as the same user

The box is plain SMB. rclone treats a UNC path as a local path, so no rclone remote config is needed. Windows just has to be able to open the share without a prompt:

cmdkey /add:u123456.your-storagebox.de /user:u123456 /pass
# prompts for the password, stores it in Credential Manager for THIS user

Two things that bit me:

  • Credential Manager entries are per user. The scheduled task has to run as the same user that ran cmdkey, otherwise it runs with no credentials and fails silently every 10 minutes.
  • System error 86 when testing with net use means wrong password. I first tried a sub-account of the box, got 86 and wasted time looking for a permission issue. It was the password.

Quick test:

Test-Path '\\u123456.your-storagebox.de\backup\alice'

The filter file

C:\rclone\desktop-filter.txt:

- *.pst
- *.ost
- ~$*
- Thumbs.db
- desktop.ini

~$* are Office lock files. Syncing them creates conflicts every time someone has a document open. desktop.ini and Thumbs.db are Windows noise that differs per machine. PST and OST get their own section below.

The wrapper script

C:\Users\alice\desktop-sync.ps1:

$rclone  = 'C:\rclone\rclone.exe'          # full path, the task has no useful PATH
$src     = [Environment]::GetFolderPath('Desktop')
$dst     = '\\u123456.your-storagebox.de\backup\alice\Desktop'
$filters = 'C:\rclone\desktop-filter.txt'
$marker  = Join-Path $env:LOCALAPPDATA 'desktop-sync.ok'
$log     = Join-Path $env:LOCALAPPDATA 'desktop-sync.log'

$rcArgs = @(
  'bisync', $src, $dst,
  '--conflict-resolve', 'newer',
  '--filters-file', $filters,
  '--log-file', $log, '--log-level', 'INFO'
)

# first run, or after a filter change: bisync needs --resync once
if (-not (Test-Path $marker)) { $rcArgs += '--resync' }

& $rclone @rcArgs

if ($LASTEXITCODE -eq 0 -and -not (Test-Path $marker)) {
  New-Item -ItemType File -Path $marker | Out-Null
}

--conflict-resolve newer means that if the same file changed on both sides between two runs, the newer one wins. rclone keeps the other version renamed as a conflict copy by default, so nothing silently disappears. For one person working on one laptop at a time that's the right call.

I use the full path to rclone.exe on purpose. On laptop 2 I had just installed rclone into C:\rclone, and a scheduled task doesn't care what you added to PATH in your terminal five minutes ago.

Gotcha: --resync, and why the marker file exists

bisync keeps listings of both sides from the last successful run and compares against them. On the very first run there are no listings, so it refuses to work until you pass --resync. That run just merges both sides together and records the baseline.

The catch: changing the filter file also forces a resync. bisync stores a hash of the filters file, and if the filters change it stops and tells you to run --resync again, since the old listings no longer match what's in scope. If you add a pattern later and forget this, the task fails every 10 minutes and nothing gets synced.

That's what the marker file is for. To force a resync on the next run, delete %LOCALAPPDATA%\desktop-sync.ok. The marker only gets created after a successful run, so a failed first resync gets retried automatically.

Don't put --resync in permanently. It's a merge, not a sync: deletions on one side don't propagate, so deleted files keep coming back.

The scheduled task

$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
  -Argument '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\Users\alice\desktop-sync.ps1'

$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
  -RepetitionInterval (New-TimeSpan -Minutes 10)

$settings = New-ScheduledTaskSettingsSet `
  -StartWhenAvailable `
  -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
  -MultipleInstances IgnoreNew `
  -ExecutionTimeLimit (New-TimeSpan -Hours 1)

Register-ScheduledTask -TaskName 'Desktop Sync' -Action $action -Trigger $trigger `
  -Settings $settings -User $env:USERNAME

Why these settings:

  • StartWhenAvailable: laptops sleep. If a run was missed, it runs as soon as the machine is back.
  • Battery allowed: by default tasks don't start on battery. That's exactly when a laptop is out and about, when you'd want the sync most.
  • IgnoreNew: the very first sync of a full Desktop can take longer than 10 minutes. Without this, a second bisync starts against the same listings while the first one is still running.
  • 1 hour limit: if something hangs (box unreachable, network half dead), the task gets killed instead of blocking all future runs.
  • Runs as the user, only when logged on. That matches the per-user cmdkey credentials and the per-user Desktop path.

Heads-up: -WindowStyle Hidden can still flash a console window for a split second, because the window exists before PowerShell gets to hide it. If that bugs the user, wrap the call in a tiny VBScript or use a hidden-launcher tool.

More on the Task Scheduler side in Windows Task Scheduler + PowerShell automation.

Gotcha: PST files do not belong in any synced folder

Laptop 1's active Outlook PST was sitting on the OneDrive-redirected Desktop. That's bad already with OneDrive, and with bisync it's worse: Outlook keeps the PST open and writes to it constantly, a sync tool copies a half-written file, and some day you have a corrupt mailbox on both sides.

So before any sync:

  1. Close Outlook.
  2. Move the PST to a local, non-synced folder like C:\Outlook-Data\.
  3. Open Outlook, it will complain the file is missing. Point it to the new location (or remove and re-add the data file under Account Settings → Data Files).

The *.pst / *.ost filter is the safety net, not the plan. OST files (Exchange/M365 cache) rebuild themselves from the server anyway. If you need to get mail out of an OST whose account is gone, that's a different story: see rescuing a mailbox from the OST into a PST.

Gotcha: getting the Desktop out of OneDrive

Laptop 1 had real data on the OneDrive Desktop. I moved it to a local folder first. Move-Item gave up halfway on a long path and a locked file, robocopy /MOVE handled both:

robocopy "$env:USERPROFILE\OneDrive\Desktop" C:\Desktop-Backup /E /MOVE /R:1 /W:1

The contents then got sorted onto the box, and the new Desktop only has shortcuts plus whatever she puts there from now on (which gets synced).

Laptop 2 looked full but wasn't. Almost all files on the business OneDrive Desktop were online-only placeholders: the data was in the cloud, not on the laptop. Since that laptop hadn't been used for real work, there was nothing local to rescue except one screenshot. So no hydrating, no moving, just uninstall OneDrive:

Get-AppxPackage *OneDriveSync* | Remove-AppxPackage
& "$env:SystemRoot\System32\OneDriveSetup.exe" /uninstall   # path varies by build

Then the shell folders still point into the dead OneDrive folder. Fix them under HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders (Desktop, Personal, My Pictures) back to %USERPROFILE%\Desktop and so on. Reboot afterwards. Until then [Environment]::GetFolderPath('Desktop') still returns the old path, and that's exactly what the sync script uses.

Two more traps on that laptop:

  • Deleting the placeholder folders hangs. Windows tries to contact the cloud provider for every placeholder, and that provider was just uninstalled. What worked: Set-Location C:\ first (otherwise "in use" because your own shell sits in it), Rename-Item the folder (instant), then delete it in the background: Start-Process cmd -ArgumentList '/c rd /s /q "C:\Users\alice\OneDrive-old"' -WindowStyle Hidden.
  • OneDrive stays in the Explorer sidebar after uninstall. Set System.IsPinnedToNameSpaceTree to 0 under HKCU:\Software\Classes\CLSID\{<OneDrive GUID>} (and under Wow6432Node), then restart Explorer. The personal OneDrive GUID is {018D5C66-4533-4307-9B53-224DE2ED1FE6}; business accounts get their own GUID, look it up under that key.

What it looks like now

Each Desktop is basically a shortcut to the shared work folder on the box plus a few loose files. Those loose files bisync every 10 minutes into the laptop's own folder on the box. Outlook data is local and out of the sync. And the old junk is in a holding folder on the box that I'll delete after a few weeks, once it's clear nothing is missing.

If you're mapping the same box as a drive letter for daily use too, this post covers why Windows "forgets" the password. For a server-side backup on top, restic is what I'd use. Sync is not backup: a deleted file gets deleted on both sides 10 minutes later.


Related posts