As developers, our code is our most valuable asset. While GitHub provides a robust and reliable platform for version control and collaboration, it's crucial to have a backup strategy in place. This guide will walk you through the process of backing up not just your repositories, but also the valuable metadata associated with your projects on GitHub.
How do you back up GitHub? At a minimum, mirror-clone each repository with git clone --mirror and push it to a second remote or to storage you control. A complete backup goes further and captures the metadata GitHub keeps outside Git: issues, pull requests, wikis, releases, and Actions. For an entire account or organization, automate this against the GitHub API or run it on a schedule with a dedicated backup service.
Why You Need GitHub Backups
Despite GitHub's reliability, there are several reasons why maintaining your own backups is essential:
- Accidental Deletion: Human error can lead to accidental deletions of repositories or branches.
- Repository Corruption: Though rare, data corruption can occur.
- Service Downtime: GitHub could experience outages that temporarily limit access to your code.
- Compliance and Auditing ❗️: Certain industries and projects require regular backups for compliance purposes. If this is what brought you here, the specifics of what ISO 27001 and SOC 2 expect from your GitHub backups are worth reading alongside this guide.
What GitHub protects, and what it does not
It helps to be precise about the line GitHub draws. Under GitHub's shared responsibility model, GitHub keeps the platform available and its infrastructure redundant, but recovering your content after you delete or overwrite it is on you. Three limits are worth knowing before you rely on the platform alone:
- Redundancy is not backup. GitHub replicates data for availability, not so you can roll back to last Tuesday. A force-push that rewrites history, or a deleted branch, leaves the remote immediately with no native undo.
- Deleted repositories have a short, conditional grace period. A deleted repository can sometimes be restored for up to 90 days, but only under specific conditions (for example, limits tied to its fork network), so it is not something to depend on.
- Availability still slips. GitHub logged dozens of incidents on its status page across 2024, and outages routinely interrupt access to code and CI even when no data is permanently lost.
The point is not that GitHub is unreliable. It is that the recovery scenarios that actually bite (human error, a compromised account, an auditor asking for a point-in-time copy) all sit on your side of that line.
Understanding GitHub Data
Before diving into backup strategies, let's break down the types of data stored on GitHub:
Repositories
Repositories contain your source code, commit history, branches, and tags. This is the core of your project and the most critical data to back up.
Metadata
GitHub stores various types of metadata associated with your repositories:
- Issues and Pull Requests
- Wiki pages
- Project boards
- Releases
- Actions workflows
- Packages
- Discussions
Backing Up GitHub Repositories
Using Git Clone
The simplest way to back up a repository is by using the git clone command. This creates a local copy of your repository, including all branches and commit history.
# Clone a repository
git clone --mirror https://github.com/username/repository.git
# Navigate into the repository
cd repository.git
# Add a new remote for backup
git remote add backup https://backupserver.com/username/repository.git
# Push all branches and tags to the backup remote
git push --mirror backup
The --mirror flag ensures that all references are copied, including branches and tags.
Using multiple git remotes
Setting Up Multiple Push URLs for a Single Remote
Instead of creating multiple remotes, you can configure a single remote (typically origin) to push to multiple URLs. This method is particularly useful when you want to maintain a primary remote while ensuring backups are pushed simultaneously.
To add multiple push URLs to your origin remote:
git remote set-url --add --push origin https://primary-repo.com/user/repo.git
git remote set-url --add --push origin https://backup-repo.com/user/repo.git
These commands configure your origin remote to push to both the primary repository and the backup repository simultaneously.
To view your remote configuration:
git remote -v
You might see output like this:
origin https://primary-repo.com/user/repo.git (fetch)
origin https://primary-repo.com/user/repo.git (push)
origin https://backup-repo.com/user/repo.git (push)
Now, when you run git push origin, Git will push to both URLs automatically.
Find the original response here: git pushing code to two remotes (Stack Overflow)
GitHub API for Repository Backup
For more control and automation, you can use the GitHub API to back up repositories programmatically. Here’s a Python script to back up a repository along with its issues and pull requests.
First, install the required libraries:
pip install requests
Then, create a script:
import os
import requests
# GitHub token and repository details
GITHUB_TOKEN = 'your_github_token'
REPO_OWNER = 'username'
REPO_NAME = 'repository'
# Headers for GitHub API
headers = {
'Authorization': f'token {GITHUB_TOKEN}',
'Accept': 'application/vnd.github.v3+json',
}
# Function to back up repository
def backup_repo():
repo_url = f'https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}'
response = requests.get(repo_url, headers=headers)
with open(f'{REPO_NAME}_repo.json', 'w') as f:
f.write(response.text)
print(f'Repository metadata backed up to {REPO_NAME}_repo.json')
# Function to back up issues
def backup_issues():
issues_url = f'https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/issues'
response = requests.get(issues_url, headers=headers)
with open(f'{REPO_NAME}_issues.json', 'w') as f:
f.write(response.text)
print(f'Issues backed up to {REPO_NAME}_issues.json')
# Function to back up pull requests
def backup_pull_requests():
pulls_url = f'https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/pulls'
response = requests.get(pulls_url, headers=headers)
with open(f'{REPO_NAME}_pulls.json', 'w') as f:
f.write(response.text)
print(f'Pull requests backed up to {REPO_NAME}_pulls.json')
# Run backup functions
backup_repo()
backup_issues()
backup_pull_requests()
This script fetches all repositories for the specified account, then clones or updates each repository in the designated backup directory.
Backing Up GitHub Metadata
Issues and Pull Requests
To back up issues and pull requests, you can use the GitHub API. Here's a Python script to download all issues and pull requests for a repository:
import requests
import json
import os
API_URL = "https://api.github.com"
TOKEN = "your_personal_access_token"
REPO_OWNER = "owner"
REPO_NAME = "repo"
BACKUP_DIR = "github_backups"
def get_issues_and_prs():
headers = {
"Authorization": f"token {TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
issues_and_prs = []
page = 1
while True:
response = requests.get(
f"{API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues?state=all&page={page}&per_page=100",
headers=headers
)
if response.status_code == 200:
page_data = response.json()
if not page_data:
break
issues_and_prs.extend(page_data)
page += 1
else:
print(f"Error fetching issues and PRs: {response.status_code}")
break
return issues_and_prs
def save_issues_and_prs(data):
backup_path = os.path.join(BACKUP_DIR, f"{REPO_OWNER}_{REPO_NAME}_issues_and_prs.json")
with open(backup_path, 'w') as f:
json.dump(data, f, indent=2)
def main():
os.makedirs(BACKUP_DIR, exist_ok=True)
issues_and_prs = get_issues_and_prs()
save_issues_and_prs(issues_and_prs)
print(f"Backed up {len(issues_and_prs)} issues and pull requests")
if __name__ == "__main__":
main()
Wiki Pages
To back up wiki pages, you can clone the wiki repository:
git clone https://github.com/username/repository.wiki.git
Project Boards
Project boards can be backed up using the GitHub API. Here's a Python script to download project board data:
import requests
import json
import os
API_URL = "https://api.github.com"
TOKEN = "your_personal_access_token"
REPO_OWNER = "owner"
REPO_NAME = "repo"
BACKUP_DIR = "github_backups"
def get_project_boards():
headers = {
"Authorization": f"token {TOKEN}",
"Accept": "application/vnd.github.inertia-preview+json"
}
response = requests.get(
f"{API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/projects",
headers=headers
)
if response.status_code == 200:
return response.json()
else:
print(f"Error fetching project boards: {response.status_code}")
return []
def get_project_columns(project_id):
headers = {
"Authorization": f"token {TOKEN}",
"Accept": "application/vnd.github.inertia-preview+json"
}
response = requests.get(
f"{API_URL}/projects/{project_id}/columns",
headers=headers
)
if response.status_code == 200:
return response.json()
else:
print(f"Error fetching project columns: {response.status_code}")
return []
def save_project_boards(data):
backup_path = os.path.join(BACKUP_DIR, f"{REPO_OWNER}_{REPO_NAME}_project_boards.json")
with open(backup_path, 'w') as f:
json.dump(data, f, indent=2)
def main():
os.makedirs(BACKUP_DIR, exist_ok=True)
project_boards = get_project_boards()
for board in project_boards:
board['columns'] = get_project_columns(board['id'])
save_project_boards(project_boards)
print(f"Backed up {len(project_boards)} project boards")
if __name__ == "__main__":
main()
Releases
To back up releases, you can use the GitHub API. Here's a Python script to download release data:
import requests
import json
import os
API_URL = "https://api.github.com"
TOKEN = "your_personal_access_token"
REPO_OWNER = "owner"
REPO_NAME = "repo"
BACKUP_DIR = "github_backups"
def get_releases():
headers = {
"Authorization": f"token {TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
releases = []
page = 1
while True:
response = requests.get(
f"{API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/releases?page={page}&per_page=100",
headers=headers
)
if response.status_code == 200:
page_releases = response.json()
if not page_releases:
break
releases.extend(page_releases)
page += 1
else:
print(f"Error fetching releases: {response.status_code}")
break
return releases
def save_releases(data):
backup_path = os.path.join(BACKUP_DIR, f"{REPO_OWNER}_{REPO_NAME}_releases.json")
with open(backup_path, 'w') as f:
json.dump(data, f, indent=2)
def main():
os.makedirs(BACKUP_DIR, exist_ok=True)
releases = get_releases()
save_releases(releases)
print(f"Backed up {len(releases)} releases")
if __name__ == "__main__":
main()
Backing Up an Entire GitHub Organization
Backing up one repository is straightforward. Backing up an organization means enumerating every repository (including private ones), cloning each as a mirror, and doing it on a schedule without tripping API rate limits.
The GitHub CLI makes the enumeration easy. This script lists every repository in an organization and mirror-clones each into a dated backup folder, so every run is a self-contained snapshot:
#!/usr/bin/env bash
set -euo pipefail
ORG="your-organization"
DEST="github-backup/$ORG/$(date +%F)"
mkdir -p "$DEST"
# List up to 1000 repos as owner/name, including private ones
gh repo list "$ORG" --limit 1000 --json nameWithOwner --jq '.[].nameWithOwner' \
| while read -r repo; do
name=$(basename "$repo")
echo "Backing up $repo"
if ! git clone --mirror "https://github.com/$repo.git" "$DEST/$name.git"; then
echo "WARNING: failed to clone $repo" >&2
fi
done
A few notes for production use:
- Authenticate as a dedicated backup user or machine account with read access to the whole org. Backups then keep working when an individual leaves, and you are not throttling a human's token.
- Mirror clones capture every branch and tag, not just the default branch. That is exactly what you want in a backup.
- Include wikis by also cloning
https://github.com/$repo.wiki.gitfor repos that have them enabled.
This covers Git data for the whole organization. Issues, pull requests, and other metadata still need the API approach from the previous section, run inside the same loop.
Backing Up GitHub to S3 or S3-Compatible Storage
A backup that lives on the same laptop as your working copy is not much of a backup. The 3-2-1 rule says at least one copy should live somewhere separate, and object storage (Amazon S3, Backblaze B2, Wasabi, Cloudflare R2, or any S3-compatible bucket) is the usual home for it.
The pattern is: mirror-clone, bundle, upload. Bundling first gives you a single restorable file per repository:
# Create a single-file archive of the repository
git clone --mirror https://github.com/username/repository.git
git -C repository.git bundle create ../repository.bundle --all
# Upload it to S3
aws s3 cp repository.bundle s3://my-backup-bucket/github/repository-$(date +%F).bundle
A Git bundle restores with a plain git clone repository.bundle, which makes it a cleaner archive format than a raw tar of the .git directory.
For S3-compatible providers, rclone uses the same command shape and can sync a whole backup folder at once:
rclone sync github-backup/ b2:my-backup-bucket/github/
Keeping a copy in storage you own, with a different provider from where your code lives, is what turns a convenience copy into real disaster recovery. If you are weighing destinations, our comparison of cloud storage providers covers the cost and durability trade-offs.
Automating GitHub Backups with cron and GitHub Actions
Manual backups are the ones that stop happening. There are two straightforward ways to put the scripts above on a schedule.
Scheduled with cron
On any server or workstation that stays on, a single cron line runs the organization backup nightly and logs the result:
# Run the org backup every day at 02:00
0 2 * * * /home/backup/github-org-backup.sh >> /var/log/github-backup.log 2>&1
Scheduled with GitHub Actions
If you would rather not run a server, a scheduled GitHub Actions workflow can back a repository up to S3 on its own cron. Store your credentials as repository or organization secrets:
name: GitHub Backup to S3
on:
schedule:
- cron: "0 2 * * *" # daily at 02:00 UTC
workflow_dispatch:
jobs:
backup:
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Mirror and upload
env:
GH_TOKEN: ${{ secrets.BACKUP_PAT }}
run: |
git clone --mirror https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git repo.git
git -C repo.git bundle create repo.bundle --all
aws s3 cp repo.bundle s3://my-backup-bucket/github/${{ github.event.repository.name }}-$(date +%F).bundle
One caveat worth stating plainly: backing up GitHub data using GitHub Actions still keeps GitHub in the loop. Use it to push copies to storage you control (the S3 step above), never to a second location on GitHub itself.
Restoring from a GitHub Backup
To restore a repository from a backup:
- Create a new repository on GitHub (if needed).
- Push the backed-up repository to the new GitHub repository:
cd backup_repository.git
git push --mirror https://github.com/username/new_repository.git
For metadata, you'll need to use the GitHub API or manual processes to restore the data, depending on the type of metadata and how it was backed up.
Restoring from a Git bundle (if you archived with the S3 method above) is a single command:
git clone repository.bundle restored-repository
cd restored-repository
git remote set-url origin https://github.com/username/new-repository.git
git push --mirror origin
Restoring metadata is the hard part, because GitHub has no bulk import for issues, pull requests, or releases. You recreate them through the API from the JSON you backed up, or accept that some of it is reference-only. This asymmetry, where Git data restores perfectly and metadata does not, is the single biggest reason teams outgrow scripts once they pass a handful of repositories.
Test your restores. A backup you have never restored is a hypothesis, not a backup. Once a quarter, clone a backup into a scratch repository and confirm the history, branches, and tags are all there. Restores fail for boring reasons (an expired token, a bucket permission, a truncated upload) that only a real rehearsal surfaces.
Building a GitHub Backup Policy
Commands are only half of a backup strategy. A policy decides how often backups run, how long you keep them, and where they live, which is exactly what a compliance auditor will ask you to produce.
A workable default follows the 3-2-1 backup rule: keep at least 3 copies of your data, on 2 different types of storage, with 1 copy off-site. Applied to GitHub:
- Frequency: daily for active repositories, matched to how much work you are willing to lose. Nightly is the common baseline.
- Retention: a rolling window rather than a single copy. Keeping 7 daily, 4 weekly, and 12 monthly snapshots lets you recover from a problem you did not notice for weeks.
- Location: at least one copy in storage you own, with a different provider from GitHub. This is what protects you from an account compromise or a provider-level outage.
- Scope: repositories and metadata, across the whole organization, not just the repos one person remembered to script.
GitHub's native retention does not cover any of this: deleted data has only a short, conditional grace period, and there is no point-in-time restore. Writing the policy down and testing it is what turns "we have a script somewhere" into something you can show an ISO 27001 or SOC 2 auditor.
Github Backup Solutions
GitHub Archive Program
GitHub has its own archive program that creates long-term archives of public repositories. While this isn't a solution for private repositories or for maintaining your own backups, it's worth mentioning as part of GitHub's commitment to preserving open-source code.
Third-party Backup Tools
Several third-party tools and services offer comprehensive GitHub backup solutions:
SimpleBackups

SimpleBackups offers an automated service specifically designed for backing up GitHub repositories and metadata to any storage solution. This service stands out for its flexibility and ease of use.
Key advantages of SimpleBackups:
- Full Automation: Set up once and let SimpleBackups handle regular backups without further intervention.
- Flexible Storage Options: Back up your GitHub data to a wide range of storage solutions, allowing you to choose the most suitable option for your needs.
- Comprehensive Coverage: Backs up not just repositories, but also issues, pull requests, wikis, and other GitHub metadata.
- Customizable Schedules: Set backup frequencies that match your project's needs and activity levels.
- Easy Recovery: Simplifies the process of restoring your data when needed.
- Secure Transfer and Storage: Ensures your data is protected during transfer and in storage.
- Compliancy: ISO 27001 certified solution. It provides all you need for your ISO, GDPR and SOC2 requirements
SimpleBackups provides a hassle-free solution for maintaining up-to-date backups of your entire GitHub presence, offering peace of mind and data security for developers and teams of all sizes.
Other Notable Tools
- GitHub Backup Utilities: An open-source tool that backs up repositories, wikis, issues, and other metadata.
- github-backup: A simple Python script for GitHub backup.
When choosing a backup solution, consider factors such as ease of use, storage flexibility, comprehensiveness of the backup, restoration process, and cost. We walk through those trade-offs in detail, including when a manual clone stops being enough, in our guide to choosing a GitHub backup approach.
GitHub Backup FAQs
How do you back up GitHub?
Back up each repository with a mirror clone (git clone --mirror) pushed to a second remote or to storage you control. A complete GitHub backup also captures the metadata GitHub stores outside Git: issues, pull requests, wikis, releases, and Actions workflows. For a whole account or organization, script the GitHub API or use a service that backs up repositories and metadata automatically on a schedule.
What is the best backup for GitHub?
The best GitHub backup automates copies of both repositories and their metadata (issues, pull requests, wikis, releases), keeps them in storage you control, and can restore a single repository or an entire organization quickly. For one repository, a scheduled git clone --mirror to external storage is enough. For teams, an automated service such as SimpleBackups handles scheduling, metadata, retention, and restores without custom scripts.
Does GitHub back up your repositories?
No. GitHub keeps repositories highly available with internal redundancy, but that is not a restorable backup. If you delete a repository, force-push over its history, or lose access to your account, GitHub does not guarantee recovery. Independent backups to storage you own are your responsibility under GitHub's shared-responsibility model.
How to backup GitHub repository?
To backup a GitHub repository, you can use the git clone command with the --mirror flag to create a local copy of the repository, including all branches and commit history.
git clone --mirror https://github.com/username/repository.git
This repository can then be backed up to another remote (GitLab, BitBucket, Gitea...).
git remote add backup-remote https://backup-server.com/username/repository.git
git push --mirror backup-remote
Or simply compress the Git repository and save it to another storage, using s3 or scp.
tar -czvf repository-backup.tar.gz repository.git
How to backup all branches in a GitHub repository?
You have 2 options when it comes to backing up all branches of your respository.
- Using
--mirrorflag when cloning the repository
git clone --mirror https://github.com/username/repository.git
- Using
fetch allafter a regular clone
git clone https://github.com/username/repository.git
git fetch --all
How to backup github organization?
Backing up an organization will invole backing up all the repositories in that organization. You can use the GitHub API to list all repositories in an organization and then backup each repository individually.
Using the GitHub CLI (gh), you can list all repositories in an organization:
Note that you'll have to install the GitHub CLI first: https://cli.github.com/ and get an access token from GitHub.
gh repo list organization_name --limit 1000 --json name,sshUrl > repos.json
Then, you can loop over the repositories and clone them:
cat repos.json | jq -r '.[].sshUrl' | xargs -n 1 git clone
Conclusion
Backing up your GitHub repositories and metadata is a crucial part of protecting your code and project history. By implementing a comprehensive backup strategy using the methods outlined in this guide, you can ensure that your valuable work is safe and recoverable in case of any unforeseen events.
Remember to regularly review and update your backup processes as your projects evolve and as GitHub introduces new features. With these practices in place, you can code with confidence, knowing that your GitHub data is securely backed up.
When you implement your own backup solutions using the methods outlined in this guide, you gain a deep understanding of the backup process and have full control over your data. However, this approach requires ongoing maintenance, monitoring, and troubleshooting to ensure your backups remain effective and up-to-date.
On the other hand, a service like SimpleBackups offers several key benefits that complement and enhance your backup strategy. If you would rather not maintain the scripts above, our GitHub backup service runs all of this on a schedule, across an entire organization, to storage you own.
Remember, the ultimate goal is to ensure your valuable GitHub data is safely and consistently backed up. Whether you choose to implement your own solutions, use a service like SimpleBackups, or employ a combination of both, regular backups are an essential practice for any developer or team relying on GitHub for their projects.