# git-sync: bare clone backups of every repository you own

> git-sync is a Go command line tool that clones or updates all of your repositories into a local directory so that a lost account or a deleted repository is not the end of your code. It works with GitHub, GitLab, Bitbucket, Gitea, and Forgejo, does bare clones by default so the backup stays small, and can run on a schedule from a container or a systemd timer.

**AkashRajpurohit/git-sync** — 🔄 A simple tool to backup and sync your git repositories

- Repository: https://github.com/AkashRajpurohit/git-sync
- Website: https://github.com/AkashRajpurohit/git-sync/wiki
- Stars: 701 · Forks: 38
- Language: Go
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/akashrajpurohit-git-sync

## The failure this is written against

git-sync is a command line tool for backing up git repositories, and the reason it exists is stated in terms of a specific event: a GitHub account belonging to someone well known was banned, and the project points at that story and at a discussion of developers losing access to their repositories after a ban.

The questions behind that are the ones the tool answers. What do you do if you lose access to your repositories? What if the hosting service goes down? What if you delete a repository by accident? The answer the project gives is that you should have a local copy of your code, and git-sync makes that copy for you.

The mechanism is a bare clone of every repository into a directory you name. The bare part is the point, because a bare clone keeps the repository data without a working tree, which means the backup is small and the sync is fast. The project says that this keeps the file size of the backups minimal, which is what makes it practical to back up hundreds of repositories rather than a handful.

The project is MIT licensed, the documentation lives in the repository wiki, and there is a recorded terminal demo linked from the readme. The releases are frequent for a small tool, with v0.21.0 in February 2026, v0.20.0 in August 2025, and v0.19.0 in June 2025, and the last push to the repository was on 2026-10-13.

It is a small Go program with a small surface. The main file sits at the top of the tree, the commands are in a cmd directory, the implementation is in a pkg directory, and the distribution is handled by a release configuration, so the project is built with the usual tooling rather than with a bespoke pipeline.

## Bare, shallow, mirror, and full clones

The clone mode is configurable, and the default is the one the project was built around.

Bare clones are the default because they save space and speed up the synchronisation, since there is no working tree to write on every update. The other three modes are supported as well: shallow clones, which fetch a limited history, mirror clones, which carry the remote's refs across, and full clones, which fetch everything.

That choice matters more than it looks, because it decides what your recovery actually looks like. A bare clone is a repository without a checkout, so restoring from it means cloning from the backup rather than copying a directory, and a shallow clone cannot be used to check out an old commit that was never fetched. A tool that offers all four modes lets you match the backup to the history you actually need, and the documentation links to a separate page for setting up periodic backups.

The sync itself is incremental in the sense you would expect: repositories that are already present are updated rather than cloned again, and the tool decides which by looking at the local copy. The feature list describes that as cloning or updating all of your repositories to a local directory.

Concurrency is the other half of the speed story. Syncing several repositories at once shortens the wall clock time of a backup that touches hundreds of remotes, and it is a plain feature in the list rather than something you tune.

The hosts are equally plain. The project says it supports backing up repositories from all the major hosting services, and names GitHub, GitLab, Bitbucket, Gitea, and Forgejo. Each one has its own client library in the dependency list, which is how the tool reaches the API to list what you own before it starts cloning.

## Tokens, the API clients, and what the tool needs permission for

The module file is short enough to read as a description of what the program does, because every dependency corresponds to a service the tool talks to or a library it uses to do its own work.

The forge clients are one group. There is a Forgejo client, a GitHub client, a Bitbucket client, and a GitLab client, each at a recent version. Alongside them sits the OAuth2 library, which is how the tokens are obtained, and a UUID library that is presumably used to name or track the individual backup directories.

The rest of the list is the plumbing of a modern Go command line application. Cobra provides the command structure, Viper reads the configuration file, and the structured logger comes from the Uber logger package. The cron library is the scheduling one, and it is worth separating from the rest because it is a feature rather than a convenience: git-sync can run on a schedule by itself, and the repository has a systemd directory alongside the container recipe.

There is one dependency that deserves to be named plainly, which is the product analytics client. It is a direct requirement, not an indirect one, so the tool can send usage events somewhere, and the project also links a visitors counter on a personal domain in its badge row. Whether that matters to you is your call, but it is a thing to know before you point a tool with a repository token at your account.

The authentication model itself is not spelled out in the documentation. The project links a wiki as the documentation and the readme points at a page for setting up periodic backups, so the details of how a token is supplied live in the wiki rather than in the readme, and that page is the one to read before the first run.

## The configuration file and the backup directory

Settings are kept in a YAML file, and the container image shows the two that matter at startup: the path to the configuration and the path to the backup directory.

```dockerfile
CMD ["--config", "/git-sync/config.yaml", "--backup-dir", "/backups"]
```

Both are command line flags with sensible-looking defaults in the image, and the directory in the image is created empty, with the application data directory alongside it. The container runs the binary through an entrypoint script rather than executing it directly, which is the usual pattern for a container that needs to drop privileges or prepare a directory before the process starts, and the image installs the setuid helper alongside git for that purpose.

The backup directory is a first class setting rather than a fixed path, which is the right call for a tool that is likely to end up on a mounted volume. The readme lists a custom backup directory as its own feature, separate from the configuration file, so both the general settings and the storage location are things you decide rather than things the tool assumes.

The configuration file is read with Viper, which is the library that handles YAML files, environment variables, and flags in one place, and the project is a good example of the modern Go command line shape: a cobra command tree, Viper for configuration, a structured logger, and a configuration file that a human can read and edit.

The readme itself is deliberately short on instructions. It says there is a thorough guide in the documentation wiki, and links the bug report and feature request templates, which is the arrangement of a project whose documentation lives in the wiki rather than in the repository.

## Running on a schedule, and hearing about it afterwards

A backup that has to be run by hand is a backup that stops being run, and the project treats scheduling as a feature rather than as an afterthought.

There are two documented routes. The readme links a wiki page on setting up periodic backups, and the repository carries a systemd directory, which is where the timer or service unit for a Linux host would live. The cron library in the dependencies suggests the binary can also carry its own schedule rather than relying on an external timer.

The container image supports the same pattern, because the command in the image is the same command you would run by hand with a configuration and a backup directory. A container that runs the tool on a timer is a normal deployment, and the image is built for it: the binary is static, the entrypoint prepares the environment, and the directories it needs are created in the image rather than assumed to exist.

Notifications are the last item in the feature list, and they matter for a scheduled job. The tool tells you when a sync has finished, and when something went wrong, because a backup that fails silently for a month is indistinguishable from a backup that is working until the day you need it.

The project is also straightforward about how to get help. There is a bug report template and a feature request template, and the author says pull requests are welcome. The readme also asks for a star, offers a coffee fund, and points at a sponsor link, and it promotes a separate product of the author's at the end, which is a fair thing to know when you are reading what is documentation and what is promotion.

## The container build, and what it says about the deployment

The Dockerfile is a two stage build, and reading it tells you what a deployment of this tool looks like in practice.

The builder is the Alpine flavour of the Go image at the pinned Go version, and it installs the compressor, the certificate bundle, the time zone data, and git. The module files are copied first and the dependencies are downloaded and tidied in a separate step, so that a change to the source does not re-download the dependency tree.

The build itself disables the C cross compilation and targets Linux, and the binary is linked with the version and the build identifier stamped into a version package inside the code, using the linker flags that carry the values. The result is then compressed with the packer that was installed at the start of the build.

```dockerfile
RUN go build -a -installsuffix cgo -ldflags="-w -s -X github.com/AkashRajpurohit/git-sync/pkg/version.Version=${VERSION} -X github.com/AkashRajpurohit/git-sync/pkg/version.Build=${BUILD}" -o git-sync . \
  && upx -q git-sync
```

The runtime image is the plain Alpine base. Git is installed because the tool shells out to it for the clones, the setuid helper is installed for the entrypoint, and the two directories the tool needs are created in the image. The entrypoint script is copied in and made executable, and the container's entry point is that script with the binary as its argument, with the configuration and backup directory flags supplied as the default command.

One detail is worth copying into your own image: the time zone data is installed in the builder so that the final image has the zone database available, which matters for a tool that reports when a sync happened.

The remaining top level files are the ones you would expect for a released Go project: a goreleaser configuration for the binaries on each platform, a renovate configuration for the dependency updates, an all-contributors file, and a directory of scripts and assets used by the site and the demo.

## Conclusion

git-sync fits a developer with more repositories than they can keep in their head, who wants the backup to run on a schedule without thinking about it, and who is willing to give a tool a token with read access to their account. It does not fit a team that needs a restore tested in production, because a backup nobody has restored from is only a hypothesis, and it does not fit a GitLab or GitHub user whose repositories are already mirrored by their host. Verify first that the token you give it has the narrowest scope that still lists and clones your repositories, that the bare clone mode matches what your recovery procedure expects, since restoring a bare repository is a different set of commands from restoring a working copy, and that the scheduled run reports failures somewhere you will actually look.

## FAQ

### What does git-sync do?

It is a command line tool that clones or updates all of your repositories into a local directory, so that a lost account, a deleted repository, or an outage at the hosting service does not mean losing your code. The default is a bare clone, which keeps the backup small and the update fast.

### Which git hosting services does git-sync support?

GitHub, GitLab, Bitbucket, Gitea, and Forgejo, each with its own client library in the project dependencies. The tool lists the repositories you own through the service API before it starts cloning.

### What clone modes can git-sync use?

Bare clones are the default, because they save space and speed up syncing, and shallow, mirror, and full clones are supported as well. The mode is worth choosing deliberately, since a bare or shallow backup has to be restored with different commands than a working copy.

### How do I run git-sync on a schedule?

The project documents periodic backups in its wiki and the repository carries a systemd directory for a Linux host, and the cron library in the dependencies means the binary can also carry a schedule itself. The container image runs the same command with a configuration file and a backup directory, so a timer can drive the container.

### What does the git-sync container image look like?

A two stage Alpine build: a Go builder that installs git and the packer, downloads the dependencies, and links the binary with the version stamped in, and a plain Alpine runtime with git, the entrypoint script, and the two directories the tool needs. It runs as a non-root user and exposes the config and backup directory as flags on the default command.

## Sources

- [AkashRajpurohit/git-sync on GitHub](https://github.com/AkashRajpurohit/git-sync)
- [License: MIT](https://github.com/AkashRajpurohit/git-sync/blob/main/LICENSE)
- [Project website](https://github.com/AkashRajpurohit/git-sync/wiki)
- [README](https://github.com/AkashRajpurohit/git-sync/blob/main/README.md)
- [Releases](https://github.com/AkashRajpurohit/git-sync/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/akashrajpurohit-git-sync
