Automated Backups: Restic, Backblaze B2, and Git
Set up encrypted cloud backups and automatic git version control for your notes. Covers Windows 11, macOS, and Linux.
Setup time: ~45 minutes with Claude Code assistance (not counting initial backup upload, which runs in the background and may take hours/days depending on your data size and internet speed).
A practical guide to backing up your computer to the cloud using restic and Backblaze B2. Your files are encrypted before leaving your machine, deduplicated to save space, and stored offsite for ~$6/TB/month.
Why This Setup?
- Encrypted: Your data is encrypted with a password before upload. Backblaze can’t read it.
- Deduplicated: Only changed portions of files are uploaded. A 10GB file with 1MB of changes = 1MB upload.
- Versioned: Keep 30 days of snapshots. Accidentally delete something? Restore from yesterday.
- Cheap: ~$6/TB/month. 100GB costs ~60 cents/month.
- Cross-platform: Same tool works on Windows, macOS, and Linux.
What about OneDrive/iCloud/Google Drive?
Those sync services are great for access across devices, but they’re not proper backups:
- Delete a file? It’s deleted everywhere.
- Ransomware encrypts your files? Synced everywhere.
- They don’t keep long version histories.
Use restic + B2 in addition to your sync service, not instead of it.
Overview
- Create a Backblaze B2 account and bucket
- Install restic
- Configure what to back up (and what to skip)
- Run your first backup
- Automate daily backups
Pick your OS: Windows 11 | macOS | Linux
Part 1: Backblaze B2 Setup (All Platforms)
1.1 Create Account
- Go to backblaze.com/b2
- Sign up for B2 Cloud Storage (not “Personal Backup” โ that’s a different product)
- Verify your email
1.2 Create Bucket
Buckets โ Create a Bucket
Settings:
- Bucket Name:
mybackup-abc123(must be globally unique โ add random characters) - Files in Bucket: Private
- Default Encryption: Disable (restic handles encryption client-side)
- Object Lock: Disable
- Bucket Name:
Note the S3 Endpoint shown (e.g.,
s3.us-west-004.backblazeb2.com)
1.3 Set Lifecycle Policy
- Click your bucket โ Lifecycle Settings
- Set to: “Keep only the last version of the file”
Why this matters: When using B2’s S3-compatible API, restic “hides” old file versions rather than deleting them. B2 keeps hidden versions indefinitely by default, which wastes storage. This lifecycle rule automatically deletes hidden versions after one day, freeing up space. (Restic handles its own versioning via snapshots โ B2’s file versioning is redundant.)
1.4 Create Application Key (S3-Compatible)
We need S3-compatible credentials. See Backblaze’s S3-compatible API documentation for details.
App Keys โ Add a New Application Key
Settings:
- Name:
restic-backup - Allow access to Bucket(s): Select your specific bucket
- Type of Access: Read and Write
- Allow List All Bucket Names: Unchecked
- Name:
SAVE THESE IMMEDIATELY (shown only once):
- keyID โ this becomes your
AWS_ACCESS_KEY_ID - applicationKey โ this becomes your
AWS_SECRET_ACCESS_KEY
- keyID โ this becomes your
Store these in your password manager. You’ll use them with AWS environment variable names (that’s how S3-compatible APIs work).
Why S3-Compatible Mode?
Backblaze B2 offers two connection methods: native B2 (b2:bucket) and S3-compatible (s3:endpoint/bucket). This guide uses S3-compatible mode because restic’s documentation recommends it:
“Due to issues with error handling in the current B2 library that restic uses, the recommended way to utilize Backblaze B2 is by using its S3-compatible API.”
The S3 mode has better error handling and is more reliable for automated backups.
Windows 11
2.1 Install Restic
Option A: Scoop
# Install Scoop if you don't have it
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
# Install restic
scoop install restic
Option B: WinGet (recommended for system-wide backups)
winget install --exact --id restic.restic --scope Machine
This installs to %ProgramFiles% and adds restic to your PATH.
Option C: Manual download
- Download from github.com/restic/restic/releases
- Get the
restic_X.X.X_windows_amd64.zipfile - Extract
restic.exetoC:\Program Files\restic\ - Add
C:\Program Files\restic\to your PATH
2.2 Create Configuration
Create a folder for your backup config:
mkdir "$env:USERPROFILE\.config\restic"
Create the environment file %USERPROFILE%\.config\restic\env.ps1:
$env:RESTIC_REPOSITORY = "s3:s3.us-west-004.backblazeb2.com/YOUR-BUCKET-NAME"
$env:RESTIC_PASSWORD = "YOUR_SECURE_PASSWORD_HERE"
$env:AWS_ACCESS_KEY_ID = "YOUR_B2_KEY_ID"
$env:AWS_SECRET_ACCESS_KEY = "YOUR_B2_APPLICATION_KEY"
Replace:
us-west-004with your region from the bucket pageYOUR-BUCKET-NAMEwith your actual bucket nameYOUR_SECURE_PASSWORD_HEREwith a strong password (this encrypts your backups โ save it in your password manager)- The AWS variables with your B2 credentials
2.3 Create Exclude File
Create %USERPROFILE%\.config\restic\excludes.txt:
# Windows system files
$RECYCLE.BIN
System Volume Information
pagefile.sys
hiberfil.sys
swapfile.sys
*.tmp
*.temp
~$*
# Caches
AppData\Local\Temp
AppData\Local\Microsoft\Windows\INetCache
AppData\Local\Microsoft\Windows\Explorer\thumbcache*
AppData\Local\Packages\*\LocalCache
AppData\Local\Packages\*\TempState
# Browser caches (bookmarks/passwords ARE backed up)
AppData\Local\Google\Chrome\User Data\*\Cache
AppData\Local\Google\Chrome\User Data\*\Code Cache
AppData\Local\Google\Chrome\User Data\*\GPUCache
AppData\Local\BraveSoftware\Brave-Browser\User Data\*\Cache
AppData\Local\BraveSoftware\Brave-Browser\User Data\*\Code Cache
AppData\Local\Microsoft\Edge\User Data\*\Cache
AppData\Local\Mozilla\Firefox\Profiles\*\cache2
# Development
node_modules
.venv
__pycache__
*.pyc
# Large app caches
AppData\Local\Steam\steamapps
AppData\Local\Discord\Cache
AppData\Roaming\Spotify\Data
# OneDrive (already in cloud)
OneDrive
2.4 Initialise Repository
Open PowerShell:
# Load environment
. "$env:USERPROFILE\.config\restic\env.ps1"
# Initialise (one-time setup)
restic init
You’ll see a repository ID โ save this alongside your password.
2.5 First Backup
Recommended: Exclude large files
Add --exclude-larger-than 300M to skip files over 300MB. Large files (videos, ISOs, game assets) are often replaceable and expensive to store in the cloud. Adjust the threshold to your needs.
restic backup $env:USERPROFILE --exclude-file="$env:USERPROFILE\.config\restic\excludes.txt" --exclude-larger-than 300M --verbose
This single flag can dramatically reduce your backup size and B2 costs.
Optional: Test with a dry run first
. "$env:USERPROFILE\.config\restic\env.ps1"
restic backup $env:USERPROFILE --exclude-file="$env:USERPROFILE\.config\restic\excludes.txt" --dry-run -vv
This shows what would be backed up without uploading anything.
Run the actual backup:
# Load environment
. "$env:USERPROFILE\.config\restic\env.ps1"
# Back up your user folder
restic backup $env:USERPROFILE --exclude-file="$env:USERPROFILE\.config\restic\excludes.txt" --verbose
First backup may take hours depending on data size and upload speed. Subsequent backups only upload changes.
2.6 Automate with Task Scheduler
Create the backup script %USERPROFILE%\.config\restic\backup.ps1:
# Load environment
. "$env:USERPROFILE\.config\restic\env.ps1"
# Run backup (skip files >300MB)
restic backup $env:USERPROFILE --exclude-file="$env:USERPROFILE\.config\restic\excludes.txt" --exclude-larger-than 300M --quiet
# Clean up old snapshots (keep 7 daily, 4 weekly, 6 monthly)
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Create the scheduled task:
- Open Task Scheduler (search in Start menu)
- Click Create Task (not “Create Basic Task”)
- General tab:
- Name:
Restic Backup - Check “Run whether user is logged on or not”
- Check “Run with highest privileges”
- Name:
- Triggers tab โ New:
- Begin the task: On a schedule
- Daily, at 2:00 PM (or whenever your computer is usually on)
- Check “Enabled”
- Actions tab โ New:
- Action: Start a program
- Program:
powershell.exe - Arguments:
-ExecutionPolicy Bypass -File "%USERPROFILE%\.config\restic\backup.ps1"
- Conditions tab:
- Uncheck “Start only if the computer is on AC power” (if you want laptop backups on battery)
- Settings tab:
- Check “Run task as soon as possible after a scheduled start is missed”
- Click OK, enter your Windows password
2.7 Verify It’s Working
. "$env:USERPROFILE\.config\restic\env.ps1"
restic snapshots
You should see your backup snapshots listed.
macOS
3.1 Install Restic
Option A: Homebrew (recommended)
brew install restic
Option B: Manual download
# Download latest release
curl -LO https://github.com/restic/restic/releases/download/v0.17.3/restic_0.17.3_darwin_arm64.bz2
# Extract (use darwin_amd64 for Intel Macs)
bunzip2 restic_*.bz2
chmod +x restic_*
sudo mv restic_* /usr/local/bin/restic
3.2 Create Configuration
mkdir -p ~/.config/restic
Create ~/.config/restic/env.sh:
export RESTIC_REPOSITORY="s3:s3.us-west-004.backblazeb2.com/YOUR-BUCKET-NAME"
export RESTIC_PASSWORD="YOUR_SECURE_PASSWORD_HERE"
export AWS_ACCESS_KEY_ID="YOUR_B2_KEY_ID"
export AWS_SECRET_ACCESS_KEY="YOUR_B2_APPLICATION_KEY"
Secure the file:
chmod 600 ~/.config/restic/env.sh
Replace the placeholder values with your actual credentials.
3.3 Create Exclude File
Create ~/.config/restic/excludes.txt:
# macOS system
.Trash
.Spotlight-V100
.fseventsd
.DS_Store
.AppleDouble
.LSOverride
._*
# Caches
Library/Caches
Library/Logs
Library/Application Support/Google/Chrome/*/Cache
Library/Application Support/Google/Chrome/*/Code Cache
Library/Application Support/BraveSoftware/Brave-Browser/*/Cache
Library/Application Support/BraveSoftware/Brave-Browser/*/Code Cache
Library/Application Support/Firefox/Profiles/*/cache2
Library/Application Support/Slack/Cache
Library/Application Support/Spotify/PersistentCache
Library/Application Support/discord/Cache
Library/Containers/*/Data/Library/Caches
# Development
node_modules
.venv
__pycache__
*.pyc
.npm
.cargo
# Large/replaceable
Downloads/*.dmg
Downloads/*.iso
Movies
Music/Music/Media
*.photoslibrary
# iCloud (already in cloud)
Library/Mobile Documents
3.4 Initialise Repository
source ~/.config/restic/env.sh
restic init
Save the repository ID shown.
3.5 First Backup
Recommended: Exclude large files
Add --exclude-larger-than 300M to skip files over 300MB. Large files (videos, ISOs, game assets) are often replaceable and expensive to store in the cloud.
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --verbose
This single flag can dramatically reduce your backup size and B2 costs.
Optional: Test with a dry run first
source ~/.config/restic/env.sh
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --dry-run -vv
This shows what would be backed up without uploading anything.
Run the actual backup:
source ~/.config/restic/env.sh
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --verbose
3.6 Automate with launchd
Create the backup script ~/.config/restic/backup.sh:
#!/bin/bash
source ~/.config/restic/env.sh
# Run backup (skip files >300MB)
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --quiet
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Make it executable:
chmod +x ~/.config/restic/backup.sh
Create the launchd plist ~/Library/LaunchAgents/com.restic.backup.plist:
<?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">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.restic.backup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>$HOME/.config/restic/backup.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>14</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/tmp/restic-backup.log</string>
<key>StandardErrorPath</key>
<string>/tmp/restic-backup.log</string>
</dict>
</plist>
Note: Replace $HOME with your actual home path (e.g., /Users/yourname). launchd doesn’t expand variables in plists.
Load it:
launchctl load ~/Library/LaunchAgents/com.restic.backup.plist
3.7 Grant Full Disk Access
For restic to back up all your files, grant Terminal (or your terminal app) Full Disk Access:
- System Settings โ Privacy & Security โ Full Disk Access
- Click +, add Terminal (or iTerm, etc.)
- You may need to restart Terminal
3.8 Verify It’s Working
source ~/.config/restic/env.sh
restic snapshots
Linux
4.1 Install Restic
Debian/Ubuntu:
sudo apt update && sudo apt install restic
Fedora:
sudo dnf install restic
Arch:
sudo pacman -S restic
Manual download (any distro):
# Download latest release (check https://github.com/restic/restic/releases for current version)
curl -LO https://github.com/restic/restic/releases/download/v0.17.3/restic_0.17.3_linux_amd64.bz2
# Extract and install
bunzip2 restic_*.bz2
chmod +x restic_*
sudo mv restic_* /usr/local/bin/restic
4.2 Create Configuration
mkdir -p ~/.config/restic
Create ~/.config/restic/env.sh:
export RESTIC_REPOSITORY="s3:s3.us-west-004.backblazeb2.com/YOUR-BUCKET-NAME"
export RESTIC_PASSWORD="YOUR_SECURE_PASSWORD_HERE"
export AWS_ACCESS_KEY_ID="YOUR_B2_KEY_ID"
export AWS_SECRET_ACCESS_KEY="YOUR_B2_APPLICATION_KEY"
Secure the file:
chmod 600 ~/.config/restic/env.sh
Replace the placeholder values with your actual credentials.
4.3 Create Exclude File
Create ~/.config/restic/excludes.txt:
# System
.cache
.local/share/Trash
.thumbnails
*.tmp
*.temp
*~
# Browser caches (bookmarks/passwords ARE backed up)
.config/google-chrome/*/Cache
.config/google-chrome/*/Code Cache
.config/google-chrome/*/GPUCache
.config/BraveSoftware/Brave-Browser/*/Cache
.config/BraveSoftware/Brave-Browser/*/Code Cache
.mozilla/firefox/*/cache2
.mozilla/firefox/*/storage
# App caches
.config/Slack/Cache
.config/discord/Cache
.config/spotify/Data
.var/app/*/cache
# Development
node_modules
.venv
__pycache__
*.pyc
.npm
.cargo/registry
target
# Flatpak app data (often large, app-specific)
.var/app/*/cache
# Large/replaceable
Downloads/*.iso
Downloads/*.AppImage
*.log
4.4 Initialise Repository
source ~/.config/restic/env.sh
restic init
Save the repository ID shown.
4.5 First Backup
Recommended: Exclude large files
Add --exclude-larger-than 300M to skip files over 300MB. Large files (videos, ISOs, game assets) are often replaceable and expensive to store in the cloud.
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --verbose
Optional: Test with a dry run first
source ~/.config/restic/env.sh
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --dry-run -vv
This shows what would be backed up without uploading anything.
Run the actual backup:
source ~/.config/restic/env.sh
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --verbose
4.6 Automate with systemd
Create the backup script ~/.config/restic/backup.sh:
#!/bin/bash
source ~/.config/restic/env.sh
# Run backup (skip files >300MB)
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M --quiet
# Clean up old snapshots (keep 7 daily, 4 weekly, 6 monthly)
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Make it executable:
chmod +x ~/.config/restic/backup.sh
Create the systemd service ~/.config/systemd/user/restic-backup.service:
[Unit]
Description=Restic backup to Backblaze B2
[Service]
Type=oneshot
ExecStart=%h/.config/restic/backup.sh
Create the timer ~/.config/systemd/user/restic-backup.timer:
[Unit]
Description=Daily restic backup
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1800
[Install]
WantedBy=timers.target
Enable and start the timer:
mkdir -p ~/.config/systemd/user
# (copy the service and timer files above)
systemctl --user daemon-reload
systemctl --user enable --now restic-backup.timer
Notes:
Persistent=trueruns a missed backup when the computer next wakes (laptop-friendly)RandomizedDelaySec=1800staggers the backup within a 30-minute window (avoids thundering herd if you have multiple machines)%hexpands to your home directory in systemd unit files
4.7 Verify It’s Working
# Check timer status
systemctl --user list-timers restic-backup.timer
# Check last run
journalctl --user -u restic-backup.service -n 20
# View snapshots
source ~/.config/restic/env.sh
restic snapshots
Restoring Files
List Your Backups
# macOS/Linux
source ~/.config/restic/env.sh
restic snapshots
# Windows PowerShell
. "$env:USERPROFILE\.config\restic\env.ps1"
restic snapshots
Restore a Specific File
# Find which snapshots contain your file
restic find "important-document.docx"
# Restore from latest backup
restic restore latest --target ~/restore-test --include "Documents/important-document.docx"
Restore Everything (Disaster Recovery)
On a fresh machine:
- Install restic
- Create env file with your credentials
- Run:
source ~/.config/restic/env.sh
restic restore latest --target /
Costs
| Data Size | Monthly Cost |
|---|---|
| 50 GB | ~$0.30 |
| 100 GB | ~$0.60 |
| 500 GB | ~$3.00 |
| 1 TB | ~$6.00 |
- Upload: Free
- Download: First 3ร your storage is free, then $0.01/GB
- API calls: Essentially free for personal use
Maintenance
Check Backup Health
Quick check (run monthly):
source ~/.config/restic/env.sh # or PowerShell equivalent
restic check
This validates the repository structure (snapshots, metadata, pack files).
Thorough check (run quarterly):
restic check --read-data
This downloads and verifies all backed-up data. Takes longer and uses bandwidth, but confirms your data is actually intact.
Partial check (compromise):
restic check --read-data-subset=10%
Randomly verifies 10% of your data โ useful for large repositories.
View Backup Statistics
restic stats
restic stats latest
Manual Backup
Run anytime you want an immediate backup:
source ~/.config/restic/env.sh
restic backup ~ --exclude-file="$HOME/.config/restic/excludes.txt" --exclude-larger-than 300M
Manual Prune (If Not Automated)
If you’re not running automatic pruning, periodically clean up old snapshots:
# Preview what would be removed (recommended first)
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --dry-run
# Actually remove old snapshots and reclaim space
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
# Verify repository integrity after pruning
restic check
Optional: Automatic Git Commits for Your Notes
If you use Obsidian, Logseq, or any markdown-based notes system, git provides fine-grained version history that restic’s daily snapshots can’t match. Accidentally delete a paragraph? Git has it from 10 minutes ago.
Why Git + Restic?
They solve different problems:
- Git: Tracks every change to text files, lets you see exactly what changed and when
- Restic: Backs up everything (including large files git ignores) to the cloud
Use both.
Windows: Git Setup
Install git:
winget install --id Git.Git -e --source winget
Initialise your vault:
cd "$env:USERPROFILE\Documents\Obsidian Vault" # Your vault path
git init
git config user.email "autobackup@local"
git config user.name "Auto-Backup"
Create .gitignore (track only markdown):
# Ignore everything
*
# But track markdown
!*.md
# Track Obsidian config
!.obsidian/
!.obsidian/**
# Track folder structure
!*/
# Track this file
!.gitignore
# Ignore workspace (local state)
.obsidian/workspace*.json
# Ignore trash
.trash/
Create hourly commit script %USERPROFILE%\.config\restic\git-commit.ps1:
$vaultPath = "$env:USERPROFILE\Documents\Obsidian Vault" # Your vault path
Set-Location $vaultPath
# Check if there are changes
$status = git status --porcelain
if (-not $status) {
Write-Host "No changes to commit"
exit 0
}
# Commit all changes
git add -A
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm"
git commit -m "Auto-backup: $timestamp"
Write-Host "Committed changes at $timestamp"
Schedule every 10 minutes: Create a Task Scheduler task using the same process as the backup task but with:
- Program:
powershell.exe - Arguments:
-ExecutionPolicy Bypass -File "%USERPROFILE%\.config\restic\git-commit.ps1" - Trigger: Daily, repeat every 10 minutes
macOS: Git Setup
Install git (usually pre-installed, or):
xcode-select --install
Initialise your vault:
cd ~/Documents/Obsidian\ Vault # Your vault path
git init
git config user.email "autobackup@local"
git config user.name "Auto-Backup"
Create .gitignore (same content as Windows above).
Create hourly commit script ~/.config/restic/git-commit.sh:
#!/bin/bash
VAULT_PATH="$HOME/Documents/Obsidian Vault" # Your vault path
cd "$VAULT_PATH" || exit 1
# Check if there are changes
if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
echo "No changes to commit"
exit 0
fi
# Commit all changes
git add -A
TIMESTAMP=$(date "+%Y-%m-%d %H:%M")
git commit -m "Auto-backup: $TIMESTAMP"
echo "Committed changes at $TIMESTAMP"
Make executable:
chmod +x ~/.config/restic/git-commit.sh
Schedule every 10 minutes: Create ~/Library/LaunchAgents/com.git.autocommit.plist:
<?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">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.git.autocommit</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>/Users/YOURUSERNAME/.config/restic/git-commit.sh</string>
</array>
<key>StartInterval</key>
<integer>600</integer>
<key>StandardOutPath</key>
<string>/tmp/git-commit.log</string>
<key>StandardErrorPath</key>
<string>/tmp/git-commit.log</string>
</dict>
</plist>
Replace YOURUSERNAME, then load:
launchctl load ~/Library/LaunchAgents/com.git.autocommit.plist
Linux: Git Setup
Install git (usually pre-installed, or):
# Debian/Ubuntu
sudo apt install git
# Fedora
sudo dnf install git
Initialise your vault:
cd ~/Documents/Obsidian\ Vault # Your vault path
git init
git config user.email "autobackup@local"
git config user.name "Auto-Backup"
Create .gitignore (same content as Windows/macOS above).
Create commit script ~/.config/restic/git-commit.sh:
#!/bin/bash
VAULT_PATH="$HOME/Documents/Obsidian Vault" # Your vault path
cd "$VAULT_PATH" || exit 1
# Check if there are changes
if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
exit 0
fi
# Commit all changes
git add -A
TIMESTAMP=$(date "+%Y-%m-%d %H:%M")
git commit -m "Auto-backup: $TIMESTAMP"
Make executable:
chmod +x ~/.config/restic/git-commit.sh
Schedule every 10 minutes with systemd:
Create ~/.config/systemd/user/git-autocommit.service:
[Unit]
Description=Auto-commit git changes
[Service]
Type=oneshot
ExecStart=%h/.config/restic/git-commit.sh
Create ~/.config/systemd/user/git-autocommit.timer:
[Unit]
Description=Auto-commit git changes every 10 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=10min
[Install]
WantedBy=timers.target
Enable:
systemctl --user daemon-reload
systemctl --user enable --now git-autocommit.timer
Recovering from Git
# See recent commits
git log --oneline -20
# See what changed in a commit
git show abc1234
# Restore a deleted file
git checkout HEAD~1 -- "path/to/file.md"
# See file at a specific time
git show HEAD~5:"path/to/file.md"
Optional: Weekly Filesystem Manifests
A manifest is a snapshot of all your files โ including those excluded from backup (like large videos). It records file paths, sizes, and SHA256 hashes. This serves two purposes:
- Know what you had: If a large file is lost, you at least know it existed and what it was
- Detect tampering: If ransomware encrypts files, hash mismatches reveal the damage โ even before you notice
Why SHA256, not MD5? MD5 is cryptographically broken โ collisions can be deliberately crafted. Sophisticated ransomware could theoretically replace a file with a collision that has the same MD5 hash. SHA256 prevents this. The speed difference is negligible for personal use.
Windows Manifest Script
Create %USERPROFILE%\.config\restic\manifest.ps1:
# Weekly filesystem manifest generator
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$outputDir = "$env:USERPROFILE\.config\restic\manifests"
$manifestFile = "$outputDir\manifest_$timestamp.tsv"
# Create output directory
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
# Generate manifest (this may take 10-30 minutes)
Write-Host "Generating filesystem manifest..."
Get-ChildItem -Path $env:USERPROFILE -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
try {
$hash = (Get-FileHash -Path $_.FullName -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
"$($_.Length)`t$($_.LastWriteTime.ToString('yyyy-MM-dd_HH:mm:ss'))`t$hash`t$($_.FullName)"
} catch {
# Skip files we can't read
}
} | Out-File -FilePath $manifestFile -Encoding UTF8
# Compress to save space
Compress-Archive -Path $manifestFile -DestinationPath "$manifestFile.zip" -Force
Remove-Item $manifestFile
# Keep only last 8 manifests
Get-ChildItem "$outputDir\manifest_*.zip" | Sort-Object LastWriteTime -Descending | Select-Object -Skip 8 | Remove-Item -Force
Write-Host "Manifest saved: $manifestFile.zip"
Schedule it weekly: Use Task Scheduler (same process as the backup task), set to run weekly on Sunday at 3 AM.
macOS Manifest Script
Create ~/.config/restic/manifest.sh:
#!/bin/bash
# Weekly filesystem manifest generator
TIMESTAMP=$(date "+%Y-%m-%d_%H-%M-%S")
OUTPUT_DIR="$HOME/.config/restic/manifests"
MANIFEST_FILE="$OUTPUT_DIR/manifest_$TIMESTAMP.tsv"
mkdir -p "$OUTPUT_DIR"
echo "Generating filesystem manifest..."
# Header
echo -e "SIZE\tMODIFIED\tSHA256\tPATH" > "$MANIFEST_FILE"
# Generate manifest (this may take 10-30 minutes)
find "$HOME" -type f -print0 2>/dev/null | while IFS= read -r -d '' file; do
size=$(stat -f%z "$file" 2>/dev/null || echo "0")
mtime=$(stat -f "%Sm" -t "%Y-%m-%d_%H:%M:%S" "$file" 2>/dev/null || echo "unknown")
hash=$(shasum -a 256 "$file" 2>/dev/null | cut -d' ' -f1 || echo "error")
printf "%s\t%s\t%s\t%s\n" "$size" "$mtime" "$hash" "$file"
done >> "$MANIFEST_FILE"
# Compress
gzip -f "$MANIFEST_FILE"
# Keep only last 8 manifests
ls -t "$OUTPUT_DIR"/manifest_*.tsv.gz 2>/dev/null | tail -n +9 | xargs -r rm -f
echo "Manifest saved: $MANIFEST_FILE.gz"
Make it executable:
chmod +x ~/.config/restic/manifest.sh
Schedule it weekly: Create ~/Library/LaunchAgents/com.restic.manifest.plist:
<?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">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.restic.manifest</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>/Users/YOURUSERNAME/.config/restic/manifest.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Weekday</key>
<integer>0</integer>
<key>Hour</key>
<integer>3</integer>
</dict>
<key>StandardOutPath</key>
<string>/tmp/restic-manifest.log</string>
<key>StandardErrorPath</key>
<string>/tmp/restic-manifest.log</string>
</dict>
</plist>
Replace YOURUSERNAME with your actual username, then load it:
launchctl load ~/Library/LaunchAgents/com.restic.manifest.plist
Linux Manifest Script
Create ~/.config/restic/manifest.sh:
#!/bin/bash
# Weekly filesystem manifest generator
TIMESTAMP=$(date "+%Y-%m-%d_%H-%M-%S")
OUTPUT_DIR="$HOME/.config/restic/manifests"
MANIFEST_FILE="$OUTPUT_DIR/manifest_$TIMESTAMP.tsv"
mkdir -p "$OUTPUT_DIR"
echo "Generating filesystem manifest..."
# Header
echo -e "SIZE\tMODIFIED\tSHA256\tPATH" > "$MANIFEST_FILE"
# Generate manifest (this may take 10-30 minutes)
find "$HOME" -type f -print0 2>/dev/null | while IFS= read -r -d '' file; do
size=$(stat -c%s "$file" 2>/dev/null || echo "0")
mtime=$(date -d @"$(stat -c %Y "$file" 2>/dev/null)" +"%Y-%m-%d_%H:%M:%S" 2>/dev/null || echo "unknown")
hash=$(sha256sum "$file" 2>/dev/null | cut -d' ' -f1 || echo "error")
printf "%s\t%s\t%s\t%s\n" "$size" "$mtime" "$hash" "$file"
done >> "$MANIFEST_FILE"
# Compress
gzip -f "$MANIFEST_FILE"
# Keep only last 8 manifests
ls -t "$OUTPUT_DIR"/manifest_*.tsv.gz 2>/dev/null | tail -n +9 | xargs -r rm -f
echo "Manifest saved: $MANIFEST_FILE.gz"
Make it executable:
chmod +x ~/.config/restic/manifest.sh
Schedule weekly with systemd:
Create ~/.config/systemd/user/restic-manifest.service:
[Unit]
Description=Generate filesystem manifest
[Service]
Type=oneshot
ExecStart=%h/.config/restic/manifest.sh
Create ~/.config/systemd/user/restic-manifest.timer:
[Unit]
Description=Weekly filesystem manifest
[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Enable:
systemctl --user daemon-reload
systemctl --user enable --now restic-manifest.timer
What the Manifest Captures
Each line contains:
- Size (bytes)
- Modified time
- SHA256 hash
- Full path
The compressed manifest is typically 5-50MB depending on file count. Keep it with your backups โ restic will back up the manifest directory automatically.
Comparing Manifests
To detect changes between two manifests:
# Extract and compare (macOS/Linux)
zdiff manifest_old.tsv.gz manifest_new.tsv.gz | head -50
Or use a diff tool on the extracted TSV files. Look for:
- Hash changes without expected modification time changes (possible tampering)
- Mass hash changes on the same date (possible ransomware)
- Missing files you didn’t delete
Troubleshooting
Understanding Exit Codes
Restic uses exit codes to indicate what happened:
- 0: Success โ backup completed normally
- 1: Fatal error โ no snapshot created
- 3: Partial success โ some files couldn’t be read, but snapshot was created
Exit code 3 is common if some files are locked by other applications. The backup still succeeds for everything else.
“repository does not exist”
Check your RESTIC_REPOSITORY path. The format is:
s3:s3.REGION.backblazeb2.com/BUCKET-NAME
“invalid credentials”
Verify AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY match your B2 application key exactly.
Backup is slow
First backup uploads everything โ this is normal. Subsequent backups only upload changes and should be much faster.
To limit bandwidth during work hours, add --limit-upload 5000 (5 MB/s) to your backup command.
Task Scheduler / launchd not running
Windows: Check Task Scheduler โ Task History. Common issues:
- PowerShell execution policy blocking the script
- Wrong path to script
macOS: Check /tmp/restic-backup.log for errors. Common issues:
- Needs Full Disk Access
$HOMEnot expanded in plist (use full path)
Key Points
- Save your password. If you lose it, your backups are unrecoverable. Put it in your password manager.
- Test restores. Periodically restore a file to verify your backups work.
- First backup takes time. Let it run overnight if needed.
- This complements sync services. Use OneDrive/iCloud for convenience, restic for disaster recovery.