Skip to content

Instantly share code, notes, and snippets.

@anotherlab
Created February 16, 2026 05:38
Show Gist options
  • Select an option

  • Save anotherlab/0bfae255f41f87a83e31ec077bf3f606 to your computer and use it in GitHub Desktop.

Select an option

Save anotherlab/0bfae255f41f87a83e31ec077bf3f606 to your computer and use it in GitHub Desktop.
Automated WSL Distribution Backup Script
<#
.SYNOPSIS
Automated WSL Distribution Backup Script
.DESCRIPTION
This script automatically backs up the default WSL distribution to a compressed archive
and copies it to a specified destination. It performs the following operations:
1. Detects the default WSL distribution
2. Cleans the distribution name to remove problematic characters
3. Creates a date-stamped backup filename (YYYY-MM-DistroName)
4. Exports the WSL distribution to a TAR file in the temp directory
5. Compresses the TAR file to 7z format for space efficiency
6. Copies the compressed backup to the specified destination
7. Cleans up old backup files from temp directory
.PARAMETER Destination
The destination path where the backup file will be copied.
Can be a local path, UNC network path, or mapped drive.
.EXAMPLE
.\backup-wsl2.ps1 -Destination "C:\Backups"
.\backup-wsl2.ps1 -Destination "\\server\backups\wsl"
.NOTES
Requirements:
- WSL2 must be installed and configured
- 7-Zip must be installed at standard location (C:\Program Files\7-Zip\7z.exe)
- Sufficient disk space in temp directory for TAR export
- Write permissions to destination path
#>
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Destination
)
# ================================================================================================
# HELPER FUNCTIONS
# ================================================================================================
# Gets the default WSL distribution name by parsing 'wsl --list' output
# This function is faster than starting a new WSL instance, but still relies on parsing text output
function Get-DefaultWSLDistributionQuick {
$results = wsl --list --all 2>$null
foreach ($p in $results) {
$line = $p.Trim()
$len = $line.IndexOf(' (Default)')
if ($len -ge 0) {
return $line.Substring(0, $len)
}
}
return $null
}
# Generates a standardized archive filename using current date and distribution name
# Format: YYYY-MM-DistributionName (e.g., "2026-02-Ubuntu-24.04")
function Get-ArchiveFileName {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$DistroName
)
# Get current date, formatted as YYYY-MM, and combine with distribution name
return (Get-Date).ToString("yyyy-MM") + "-" + $DistroName
}
# Constructs the full path to a TAR archive file in the system temp directory
# Used to create temporary export files before compression
function Get-TarArchivePath {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ArchiveName
)
# Get the temp folder path and combine with archive name and .tar extension
return Join-Path -Path $env:TEMP -ChildPath "$ArchiveName.tar"
}
# Exports a WSL distribution to a TAR file using 'wsl --export' command
# Includes comprehensive error handling and validation
function Export-WSLDistribution {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$DistributionName,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ArchiveName
)
try {
# Get the full tar path using the existing function
$tarPath = Get-TarArchivePath -ArchiveName $ArchiveName
Write-Host "Exporting WSL distribution '$DistributionName' to '$tarPath'..."
# Execute wsl --export command
$result = wsl --export $DistributionName $tarPath 2>&1
if ($LASTEXITCODE -ne 0) {
throw "WSL export for '$DistributionName' to '$tarPath' failed with exit code $LASTEXITCODE. Error: $result"
}
if (Test-Path $tarPath) {
Write-Host "Export completed successfully: $tarPath"
return $tarPath
} else {
throw "Export appeared to succeed but tar file was not created at: $tarPath"
}
}
catch {
Write-Error "Failed to export WSL distribution '$DistributionName': $($_.Exception.Message)"
throw
}
}
# Removes old backup files (.tar and .7z) from temp directory that match the distribution name
# Searches for files with current century prefix (e.g., "20") to avoid deleting unrelated files
function Remove-OldWSLBackups {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$DistributionName
)
try {
# Get first 2 digits of current year
$yearPrefix = (Get-Date).Year.ToString().Substring(0, 2)
# Build search patterns for both .tar and .7z files
$tarPattern = "$yearPrefix*$DistributionName*.tar"
$zipPattern = "$yearPrefix*$DistributionName*.7z"
Write-Host "Searching for old backups matching patterns: $tarPattern, $zipPattern"
# Get matching files in temp directory for both extensions
$tarBackups = Get-ChildItem -Path $env:TEMP -Filter $tarPattern -File -ErrorAction SilentlyContinue
$zipBackups = Get-ChildItem -Path $env:TEMP -Filter $zipPattern -File -ErrorAction SilentlyContinue
# Combine both results
$oldBackups = @($tarBackups) + @($zipBackups)
if ($oldBackups.Count -eq 0) {
Write-Host "No old backup files found to delete."
return
}
Write-Host "Found $($oldBackups.Count) old backup file(s) to delete:"
foreach ($file in $oldBackups) {
Write-Host " Deleting: $($file.FullName)"
Remove-Item -Path $file.FullName -Force -ErrorAction Stop
Write-Host " Successfully deleted: $($file.Name)"
}
Write-Host "Cleanup completed successfully."
}
catch {
Write-Error "Failed to remove old WSL backups: $($_.Exception.Message)"
throw
}
}
# Compresses a TAR file to 7z format using 7-Zip for space efficiency
# 7z typically provides better compression ratios than ZIP for large WSL exports
function Compress-WSLBackup {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$TarPath
)
try {
# Get the zip path by changing the extension of the tar path to .7z
$zipPath = [System.IO.Path]::ChangeExtension($TarPath, ".7z")
# Need to use Start-Process to call 7z.exe, and wait for it to finish before checking if the file was created
start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList "a", "-t7z", $zipPath, $TarPath -Wait -NoNewWindow
if (Test-Path $zipPath) {
Write-Host "Compression completed successfully: $zipPath"
return $zipPath
} else {
throw "Compression appeared to succeed but 7-Zip file was not created at: $zipPath"
}
}
catch {
Write-Error "Failed to compress WSL backup: $($_.Exception.Message)"
throw
}
}
# ================================================================================================
# MAIN EXECUTION LOGIC
# ================================================================================================
# Step 1: Detect the default WSL distribution
$defaultDistro = Get-DefaultWSLDistributionQuick
if (-not $defaultDistro) {
throw "Unable to determine the default WSL distribution."
} else {
# Step 2: Clean the distribution name to remove problematic characters
# WSL output can contain BOM, control characters, or extra whitespace that breaks commands
$defaultDistro = ($defaultDistro -replace '[\x00-\x1F\x7F-\x9F]', '' -replace '\s+', ' ').Trim()
# Step 3: Generate standardized filenames for the backup process
$archiveName = Get-ArchiveFileName -DistroName $defaultDistro
$tarPath = Get-TarArchivePath -ArchiveName $archiveName
# Step 4: Export WSL distribution and compress (currently commented for testing)
$tarPath = Export-WSLDistribution -DistributionName $defaultDistro -ArchiveName $archiveName
# Step 5: Compress the TAR file to 7z format for efficient storage
$zipPath = Compress-WSLBackup -TarPath $tarPath
# Step 6: Copy the compressed backup to the specified destination
Write-Host "Copying backup to destination: $Destination"
Copy-Item -Path $zipPath -Destination $Destination
# Step 6: Clean up old backup files from temp directory to free disk space
Remove-OldWSLBackups -DistributionName $defaultDistro
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment