DEV Community

Cover image for Is GitHub a single point of failure?
Solve Computer Science
Solve Computer Science

Posted on

Is GitHub a single point of failure?

🧩 The problem

The title may seem provocative, but think about it for a moment: lots of open source projects host their Git repositories exclusively on GitHub. What happens when the website is down or experiences other issues? Here are just a few examples:

You can find older news articles such as the famous 2018 DDoS attack.

In this post I show you how to use multiple Git remotes to increase "Git hosting resilience".

The way you set up mirrors can change between services (web UI), while using remotes is platform-agnostic (Git CLI). For this reason we'll avoid Git mirrors in this discussion.

📓 Software and services

Notes:

🌐 Services

Service name Is open source Notes
bitbucket.org No -
codeberg.org Yes powered by Forgejo
framagit.org Yes powered by Gitlab
gitlab.com No (only the community edition is fully libre) powered by Gitlab, probably the most direct competitor of GitHub
sr.ht Yes powered by Sourcehut

🖥️ Software

These are all open source and self-hostable.

Software name Notes
Forgejo Fork of Gitea, maintained by Codeberg e.V. and contributors. Powers Codeberg
Gitea Fork of Gogs
GitLab Use the community edition
Gogs -
Sourcehut Powers sr.ht

🤔 So, how do you do it

Given the distributed nature of Git you can keep GitHub as main and then add one or more of these services as non-default remotes.

First of all check the existing remotes:

git remote -v
Enter fullscreen mode Exit fullscreen mode

By today's standards it's expected not to use HTTP basic auth with Git anymore, but something like SSH, possibly with unique encrypted keys for each service. For example, in my local md-toc project checkout I have these remotes:

codeberg    [email protected]:frnmst/md-toc.git (fetch)
codeberg    [email protected]:frnmst/md-toc.git (push)
framagit    [email protected]:frnmst/md-toc.git (fetch)
framagit    [email protected]:frnmst/md-toc.git (push)
origin  ssh://[email protected]/frnmst/md-toc.git (fetch)
origin  ssh://[email protected]/frnmst/md-toc.git (push)
forgejo ssh://[email protected]/frnmst/md-toc.git (fetch)
forgejo ssh://[email protected]/frnmst/md-toc.git (push)
Enter fullscreen mode Exit fullscreen mode

To add a new remote such as Codeberg, what I did at the time was simply:

git remote add codeberg [email protected]:frnmst/md-toc.git
Enter fullscreen mode Exit fullscreen mode

This method, however, has the disadvantage that you need to push changes one remote at a time. Alternatively, you can create a new all label and combine multiple push remotes into one. This way there is no need to manually git push each remote one by one. You can simply git push all.

Here's how you do it:

git remote add all ssh://[email protected]/frnmst/md-toc.git
git remote set-url --add --push all ssh://[email protected]/frnmst/md-toc.git
git remote set-url --add --push all ssh://[email protected]/frnmst/md-toc.git
git remote set-url --add --push all [email protected]:frnmst/md-toc.git
git remote set-url --add --push all [email protected]:frnmst/md-toc.git
Enter fullscreen mode Exit fullscreen mode

So we now have a new virtual all remote we can push to but a single pull remote from GitHub. Having multiple Git pull remotes could cause conflicts if all these don't have the same history. If there is a change in a single remote you can pull the changes directly from it and merge when necessary.

You can check the result of the previous commands like this:

git remote -v | grep all

all ssh://[email protected]/frnmst/md-toc.git (fetch)
all     ssh://[email protected]/frnmst/md-toc.git (push)
all ssh://[email protected]/frnmst/md-toc.git (push)
all [email protected]:frnmst/md-toc.git (push)
all [email protected]:frnmst/md-toc.git (push)
Enter fullscreen mode Exit fullscreen mode

Let's test the setup. In my case I didn't have any changes so I got the same message 4 times:

git push all dev --verbose

Pushing to ssh://github.com/frnmst/md-toc.git
To ssh://github.com/frnmst/md-toc.git
 = [up to date]      dev -> dev
updating local tracking ref 'refs/remotes/all/dev'
Everything up-to-date
Pushing to ssh://software.franco.net.eu.org/frnmst/md-toc.git
To ssh://software.franco.net.eu.org/frnmst/md-toc.git
 = [up to date]      dev -> dev
updating local tracking ref 'refs/remotes/all/dev'
Everything up-to-date
Pushing to codeberg.org:frnmst/md-toc.git
To codeberg.org:frnmst/md-toc.git
 = [up to date]      dev -> dev
updating local tracking ref 'refs/remotes/all/dev'
Everything up-to-date
Pushing to framagit.org:frnmst/md-toc.git
To framagit.org:frnmst/md-toc.git
 = [up to date]      dev -> dev
updating local tracking ref 'refs/remotes/all/dev'
Everything up-to-date
Enter fullscreen mode Exit fullscreen mode

🎉 Conclusion

Git has tons of options and there's an ecosystem of web services and libre software you can choose from to host your repository. Don't limit yourself to exclusively host on GitHub. The real challenge is how to sync stuff like issues, pull requests, projects, actions, etc... between all these platforms. Alternative systems like Fossil could help solve this.

Do you already use multiple remotes? If not, which ones do you intend to try? Are there easier ways to manage this use case (I'm not a Git expert)? Let me know in the comments.

Top comments (0)