Skip to content

Automated, encrypted Git repository backups from GitHub, GitLab, Bitbucket, Azure DevOps, Gitea, and Sourcehut

License

Notifications You must be signed in to change notification settings

jonhadfield/soba

Repository files navigation

soba

Automated, encrypted backups for your Git repositories

logo

GitHub Release Codacy Badge CodeQL Go Report Card

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.

Features

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

Quick Start

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/soba

Installation

Binary

Download the latest release from the releases page, then:

install <soba binary> /usr/local/bin/soba

Set GIT_BACKUP_DIR and your provider credentials, then run:

soba

Docker

docker 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/soba

Kubernetes

Deploy soba as a CronJob. See the Kubernetes guide for manifests and instructions.

Synology NAS

Run soba via the Docker GUI on your NAS. See the Synology guide for step-by-step instructions.

Supported Providers

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.

Configuration

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_token

If both the variable and _FILE version are set, the variable takes precedence.

Scheduling

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.

Backup Rotation

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=7

Git LFS

To include Git LFS objects in your backups, enable it per provider:

export GITHUB_BACKUP_LFS=yes
export GITLAB_BACKUP_LFS=yes

LFS content is stored in a *.lfs.tar.gz file alongside the repository bundle. The Docker image includes git-lfs.

Encryption

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.

Decrypting backups

Install the age CLI, then:

age -d -o repo.bundle repo.bundle.age

You'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"
done

Notifications

Get 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.

Restoring Backups

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.

Supported Platforms

Tested on Windows 10, macOS, and Linux (amd64). Should also work on Linux (386, arm, arm64), FreeBSD, NetBSD, and OpenBSD.

Changelog

See CHANGELOG.md for release history.

Packages

 
 
 

Contributors 4

  •  
  •  
  •  
  •