@open-xchange/relic
v0.1.2
Published
GitLab group backup CLI for disaster recovery
Maintainers
Keywords
Readme
Relic
GitLab group backup CLI for disaster recovery. Published as @open-xchange/relic. Backs up all git repositories (mirror clones) and project/group settings from a GitLab instance.
Features
- Mirror clones of all repositories (
--mirror) withgc.auto=0to preserve force-pushed history - Wiki repository backup (separate
.wiki.gitmirrors) - Empty repositories are detected (
empty_repo: true) and the git mirror step is skipped — settings are still backed up - Project settings: CI variables, protected branches/tags, webhooks, labels, milestones, deploy keys/tokens, pipeline schedules, approval rules, integrations, releases
- Optional issue + issue-note backup (per-issue notes saved to
issue_notes/<iid>.json); both default to off because they can be large - Group settings: CI variables, webhooks, labels, milestones, deploy tokens
- Incremental backups — hard-link copies previous mirror, then
git fetch --prune - Recursive group/project discovery with glob-based exclusion patterns
- Hierarchical rate limiting (global + per-endpoint token buckets)
- Configurable concurrency (separate pools for git and API operations)
- Backup verification (
git fsck+ JSON validation) - Retention-based pruning of old runs
- YAML config with environment variable substitution
.envfiles in the working directory are auto-loaded by the CLI
Requirements
- Node.js >= 22
- Git
- pnpm
Installation
pnpm install
pnpm buildThe CLI is available at dist/cli/index.js, or via pnpm dev during development:
# development
pnpm dev -- backup --help
# after build
node dist/cli/index.js backup --helpConfiguration
Copy the example config and edit it:
cp config/relic.example.yaml relic.yamlgitlab_url: "https://gitlab.com"
gitlab_token: "${RELIC_GITLAB_TOKEN}" # reads from environment variable
groups:
- "openxchange"
clone_method: "https" # "https" or "ssh"
backup_dir: "/data/backups/gitlab"
concurrency:
git_operations: 4 # parallel git clone/fetch
api_requests: 10 # parallel API calls
backup_scope:
repository: true
wiki: true
ci_variables: true
protected_branches: true
protected_tags: true
webhooks: true
labels: true
milestones: true
deploy_keys: true
deploy_tokens: true
pipeline_schedules: true
approval_rules: true # requires Premium/Ultimate
integrations: true
releases: true
issues: false # can be large; off by default
issue_notes: false # comments on issues; requires issues: true
group_backup_scope:
ci_variables: true
webhooks: true # requires Premium/Ultimate
labels: true
milestones: true
deploy_tokens: true
exclude: [] # e.g. ["openxchange/archived-*"]
retention_count: 0 # 0 = keep all runs
log_level: "info"Set your GitLab token:
export RELIC_GITLAB_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"The token needs read_api scope. For backing up CI variables, api scope is required.
Usage
List discovered groups and projects
relic list -c relic.yaml
relic list -c relic.yaml --format json
relic list -c relic.yaml --format csvRun a backup
# full backup
relic backup -c relic.yaml
# dry run (discover only, no writes)
relic backup -c relic.yaml --dry-run
# single project
relic backup -c relic.yaml --project openxchange/some-project
# repos only (skip API settings)
relic backup -c relic.yaml --repo-only
# settings only (skip git clones)
relic backup -c relic.yaml --settings-only
# verbose logging
relic backup -c relic.yaml -vVerify a backup
# verify latest run (git fsck + JSON validation)
relic verify -c relic.yaml
# verify a specific run
relic verify -c relic.yaml --run 2026-03-04T08-30-00Z
# only check repos or settings
relic verify -c relic.yaml --check-repos
relic verify -c relic.yaml --check-settingsBackup Directory Layout
/data/backups/gitlab/
├── runs/
│ └── 2026-03-04T08-30-00Z/
│ ├── manifest.json
│ ├── groups/
│ │ └── openxchange/
│ │ ├── _settings/
│ │ └── subgroup/
│ │ └── _settings/
│ └── projects/
│ └── openxchange/subgroup/project/
│ ├── repo.git/
│ ├── wiki.git/
│ ├── _settings/
│ │ ├── project.json
│ │ ├── … # other settings JSON files
│ │ └── issue_notes/<iid>.json # when issue_notes is enabled
│ └── _meta.json
├── latest -> runs/2026-03-04T08-30-00Z
└── state.jsonExit Codes
| Code | Meaning | |------|---------| | 0 | All projects backed up successfully | | 1 | Some projects failed (partial backup) | | 2 | Fatal error (e.g. disk full, config invalid) |
Development
pnpm dev -- list -c relic.yaml # run without building
pnpm test # run tests with coverage (output/coverage/)
pnpm test:watch # watch mode
pnpm build # compile TypeScript
pnpm typecheck # type-check without emittingLayout
src/— library + CLI sourcespec/— vitest test suites, mirroring thesrc/layout. Imports use the@/alias that maps tosrc/.config/— example config filesdist/— compiled output (gitignored)output/coverage/— coverage reports (gitignored)
