WSL2 provides great disk performance, but it requires storing the files separately in a virtual disk that is not accessible by OneDrive. WSL2 can be backed up with wsl –export Debian to a VHD or TGZ, but that is a complete disk backup of 20gb or more – not scalable for hourly backups.

With this approach, we use Windows Task Scheduler to trigger robocopy to incrementally sync directories from WSL2 to Onedrive’s native FS, so incremental copies are fast ( 1 s per 10k files), and OneDrive sync time remains negligible.

It’s also useful for snapshotting subdirectories to TGZ for offline or cloud archiving.

Robocopy

robocopy is a built-in tool that recognizes file size & modification times to copy changes incrementally. /xd param is used to exclude node_modules (wasteful JS library artifacts)

This script syncs a project directory /home/USERNAME/sotion within WSL2 to $HOME\OneDrive\Documents\sotion on Windows

# backup wsl contents on a schedule
# set & truncate $logfile
$logfile="$HOME\backup-wsl.log.txt"
if ((Get-Item $logfile).length -gt 128000){
    Remove-Item $logfile
}
$src="\\wsl.localhost\Debian\home\${env:Username}\sotion"
$dest=$(Join-Path ${env:OneDrive} "Documents\Sotion")
Write-EventLog  -LogName Application -Source "Backup-WSL" -EventID 3001 -Message "Starting Backup"
$t0=(Get-Date)
robocopy $src $dest /W:1 /R:0 /E /xd node_modules  /NFL /NDL /LOG+:$logfile
$exitcoderobo=$LASTEXITCODE
$t1=(Get-Date)
$delta =($t1-$t0)
$message="Finished Backup. Duration = $delta"
if($exitcoderobo -ne 0){
    $message="ERROR: Finished Backup. Backup failed. Duration = $delta . Check $logfile for error"
}
Write-EventLog  -LogName Application -Source "Backup-WSL" -EventID 3001 -Message $message
exit $exitcoderobo

Trigger the script to test

pwsh --noprofile  backup-wsl\backup-wsl.ps1

Create the Scheduled Task

$time = New-ScheduledTaskTrigger -Daily -At 2pm 
$user = (whoami)
$action = New-ScheduledTaskAction -Execute "${env:ProgramFiles}\PowerShell\7\pwsh.exe" -Argument "-noninteractive -nologo -noprofile $HOME\scripts\backup-wsl\backup-wsl.ps1"
Register-ScheduledTask -TaskName "Backup-WSL" -Trigger $Time -User $User -Action $action

Trigger the script to create the task schedule

 pwsh -noprofile .\backup-wsl\schedule-backup.ps1 

Create the Event Log

New-EventLog -LogName Application -Source "Backup-WSL"

Monitoring

Runs are logged to Windows Event Viewer. You can view them using the Event Viewer app or Get-EventLog

> get-eventlog -Source backup-wsl -LogName Application -Newest 1 |Select-Object message

Message
-------
ERROR: Finished Backup. Backup failed. Duration = 00:00:01.8787580 . Check C:\Users\xxx\backup-wsl.log.txt for err

Full Example

See this Gist