Mirrors all your GitHub repositories to a local drive. Discovers every repo you have access to (personal, private, org), creates bare mirror clones, and keeps them current with incremental fetches.
- Bare mirror clones with all refs and branches
- Incremental updates — never re-downloads everything
- Atomic staging so failed clones don't corrupt existing backups
- Resume interrupted syncs
- Concurrent workers
- Git LFS support
- Linux, macOS, Windows
Download a binary from the Releases page, or build from source:
make build # outputs to bin/- Set your GitHub PAT:
export GITHUB_MIRROR_PAT=ghp_... - Copy and edit the config:
cp mirror-config.example.json mirror-config.json - Run:
github-hdd-mirror --config ./mirror-config.json
You can also run github-hdd-mirror with no flags — it will use ./mirror-config.json if it exists, or mirror to the current directory.
See mirror-config.example.json for all options.
| Field | Required | Default | Description |
|---|---|---|---|
mirror_dir |
Yes | — | Where bare mirror repos are stored |
stage_dir |
No | <mirror_dir>/.stage |
Temp staging dir (must be on same filesystem) |
workers |
No | 4 |
Concurrent mirror goroutines |
lfs_enabled |
No | false |
Run git lfs fetch --all after each sync |
log_file_path |
No | "" (stdout) |
Path to JSON Lines log file |
The GitHub PAT is read from the GITHUB_MIRROR_PAT environment variable (or a .env file in the working directory). See .env.example for details.
The tool runs a single sync and exits — scheduling is handled by your OS. Pick the section for your platform below.
crontab -eAdd a line:
0 */6 * * * cd /opt/github-mirror && ./github-hdd-mirror --config ./mirror-config.json >> /var/log/github-mirror-cron.log 2>&1The cd ensures the .env file is found. Alternatively, pass the PAT inline:
0 */6 * * * GITHUB_MIRROR_PAT=ghp_xxx /opt/github-mirror/github-hdd-mirror --config /opt/github-mirror/mirror-config.json >> /var/log/github-mirror-cron.log 2>&1Verify your crontab is active:
crontab -lmacOS uses launchd instead of cron. Create a plist file:
nano ~/Library/LaunchAgents/com.github-hdd-mirror.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.github-hdd-mirror</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/github-hdd-mirror</string>
<string>--config</string>
<string>/opt/github-mirror/mirror-config.json</string>
</array>
<key>WorkingDirectory</key>
<string>/opt/github-mirror</string>
<key>EnvironmentVariables</key>
<dict>
<key>GITHUB_MIRROR_PAT</key>
<string>ghp_YOUR_TOKEN_HERE</string>
</dict>
<key>StartInterval</key>
<integer>21600</integer>
<key>StandardOutPath</key>
<string>/var/log/github-mirror.log</string>
<key>StandardErrorPath</key>
<string>/var/log/github-mirror.log</string>
</dict>
</plist>Load it:
launchctl load ~/Library/LaunchAgents/com.github-hdd-mirror.plistTo unload:
launchctl unload ~/Library/LaunchAgents/com.github-hdd-mirror.plistStartInterval is in seconds — 21600 = 6 hours.
Option A: GUI
- Open Task Scheduler (
taskschd.msc) - Click "Create Basic Task"
- Name it
GitHub HDD Mirror - Trigger: Daily, repeat every 6 hours
- Action: Start a Program
- Program:
C:\tools\github-hdd-mirror.exe - Arguments:
--config C:\tools\mirror-config.json - Start in:
C:\tools
- Program:
- Finish and check "Open the Properties dialog" to set the environment variable
To set GITHUB_MIRROR_PAT, edit the task and wrap it in a batch script instead:
@echo off
set GITHUB_MIRROR_PAT=ghp_YOUR_TOKEN_HERE
C:\tools\github-hdd-mirror.exe --config C:\tools\mirror-config.json >> C:\tools\logs\mirror.log 2>&1Then point the task at the batch script.
Option B: PowerShell (one-liner setup)
$action = New-ScheduledTaskAction `
-Execute "C:\tools\github-hdd-mirror.exe" `
-Argument "--config C:\tools\mirror-config.json" `
-WorkingDirectory "C:\tools"
$trigger = New-ScheduledTaskTrigger -Daily -At "03:00AM"
# Repeat every 6 hours for the duration of 1 day
$trigger.Repetition = (New-ScheduledTaskTrigger -Once -At "00:00" -RepetitionInterval (New-TimeSpan -Hours 6) -RepetitionDuration (New-TimeSpan -Days 1)).Repetition
Register-ScheduledTask -TaskName "GitHub HDD Mirror" -Action $action -Trigger $trigger -Description "Mirror GitHub repos to local drive"Set the PAT as a system environment variable:
[System.Environment]::SetEnvironmentVariable("GITHUB_MIRROR_PAT", "ghp_YOUR_TOKEN_HERE", "User")| Schedule | Linux/macOS cron | launchd (seconds) | Windows |
|---|---|---|---|
| Every hour | 0 * * * * |
3600 |
Repeat every 1 hour |
| Every 6 hours | 0 */6 * * * |
21600 |
Repeat every 6 hours |
| Daily at 3 AM | 0 3 * * * |
Use StartCalendarInterval |
Daily at 03:00 |
| Twice daily | 0 0,12 * * * |
43200 |
Repeat every 12 hours |