Automated, encrypted backups for your Git repositories
soba backs up your Git repositories from GitHub, GitLab, Bitbucket, Azure DevOps, Gitea, and Sourcehut. Each repository is saved as a single-file git bundle, and only new bundles are stored when changes are detected. Bundles can optionally be encrypted with age for secure offsite storage.
| Multi-provider | GitHub, GitLab, Bitbucket, Azure DevOps, Gitea, Sourcehut |
| Efficient storage | Git bundles with change detection — unchanged repos are skipped |
| Encryption | Optional age encryption for bundles, manifests, and LFS archives |
| Built-in scheduler | Interval (24h, 45m) or cron (0 3 * * *) scheduling |
| Smart rotation | Keep only the n most recent backups per repo |
| Git LFS | Back up large file storage objects alongside repo bundles |
| Notifications | Slack, Telegram, webhooks, and ntfy alerts |
| Runs anywhere | Binary, Docker, Kubernetes, or Synology NAS |
Create git bundles of all repositories in your GitHub account:
mkdir soba-backups
docker run --rm \
-v ./soba-backups:/backups \
-e GIT_BACKUP_DIR=/backups \
-e GITHUB_TOKEN=<your-token> \
ghcr.io/jonhadfield/sobaDownload the latest release from the releases page, then:
install <soba binary> /usr/local/bin/sobaSet GIT_BACKUP_DIR and your provider credentials, then run:
sobadocker run --rm -t \
-v /path/to/backups:/backup \
-e GIT_BACKUP_DIR=/backup \
-e GITHUB_TOKEN=$GITHUB_TOKEN \
-e GITLAB_TOKEN=$GITLAB_TOKEN \
ghcr.io/jonhadfield/sobaDeploy soba as a CronJob. See the Kubernetes guide for manifests and instructions.
Run soba via the Docker GUI on your NAS. See the Synology guide for step-by-step instructions.
| Provider | Token Docs | Key Variables |
|---|---|---|
| GitHub | Create token | GITHUB_TOKEN |
| GitLab | Create token | GITLAB_TOKEN |
| Bitbucket | API tokens / OAuth2 | BITBUCKET_EMAIL + BITBUCKET_API_TOKEN |
| Azure DevOps | Create PAT | AZURE_DEVOPS_USERNAME + AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORGS |
| Gitea | Create token | GITEA_APIURL + GITEA_TOKEN |
| Sourcehut | Create PAT | SOURCEHUT_PAT |
For full provider configuration, organisation filtering, comparison modes, and self-hosted endpoints, see the provider documentation.
All configuration is via environment variables. Set GIT_BACKUP_DIR for the backup destination and add credentials for each provider you want to back up.
export GIT_BACKUP_DIR="/repo-backups/"
export GITHUB_TOKEN="ghp_..."Secrets can also be loaded from files using the _FILE suffix:
export GITHUB_TOKEN_FILE=/run/secrets/github_tokenIf both the variable and _FILE version are set, the variable takes precedence.
soba includes a built-in scheduler so it can run continuously. Set an interval or cron expression:
# Run every 24 hours
export GIT_BACKUP_INTERVAL=24h
# Run every 45 minutes
export GIT_BACKUP_INTERVAL=45m
# Run daily at 3am (cron syntax)
export GIT_BACKUP_CRON='0 3 * * *'soba can also be triggered by external schedulers like cron or systemd. See the logging and persistence guide for cron examples.
Keep only the n most recent backups per provider by setting the relevant variable:
export GITHUB_BACKUPS=7
export GITLAB_BACKUPS=7
export BITBUCKET_BACKUPS=7
export GITEA_BACKUPS=7
export AZURE_DEVOPS_BACKUPS=7
export SOURCEHUT_BACKUPS=7To include Git LFS objects in your backups, enable it per provider:
export GITHUB_BACKUP_LFS=yes
export GITLAB_BACKUP_LFS=yesLFS content is stored in a *.lfs.tar.gz file alongside the repository bundle. The Docker image includes git-lfs.
Encrypt bundles, manifests, and LFS archives with age encryption by setting a passphrase:
export BUNDLE_PASSPHRASE="your-secure-passphrase"When enabled:
- Bundles are saved as
.bundle.age - Manifests are saved as
.manifest.age - LFS archives are saved as
.lfs.tar.gz.age
Store the passphrase securely — without it, your backups cannot be decrypted.
Install the age CLI, then:
age -d -o repo.bundle repo.bundle.ageYou'll be prompted for the passphrase. For batch decryption:
#!/bin/bash
read -s -p "Enter passphrase: " PASSPHRASE
echo
for file in *.age; do
output="${file%.age}"
echo "Decrypting $file to $output..."
echo "$PASSPHRASE" | age -d -o "$output" "$file"
doneGet notified when backups complete or fail. To reduce noise on scheduled runs, send notifications only on failure:
export SOBA_NOTIFY_ON_FAILURE_ONLY=true| Channel | Variables |
|---|---|
| Slack | SLACK_CHANNEL_ID, SLACK_API_TOKEN |
| Telegram | SOBA_TELEGRAM_BOT_TOKEN, SOBA_TELEGRAM_CHAT_ID |
| Webhooks | SOBA_WEBHOOK_URL, SOBA_WEBHOOK_FORMAT (long or short) |
| ntfy | SOBA_NTFY_URL |
Webhook payload examples: long format, short format.
A git bundle is a portable archive of a repository. Clone it like any remote:
git clone soba.20180708153107.bundle my-repo
cd my-repo
git remote set-url origin <original-repo-url>If the bundle is encrypted, decrypt it first.
Tested on Windows 10, macOS, and Linux (amd64). Should also work on Linux (386, arm, arm64), FreeBSD, NetBSD, and OpenBSD.
See CHANGELOG.md for release history.
