garethgeorge/backrest
Backrest is a web UI and orchestrator for restic backup.
Mixed signals — read the receipts
weakest axiscopyleft license (GPL-3.0) — review compatibility
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2d ago
- ✓22+ active contributors
- ✓GPL-3.0 licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 75% of recent commits
- ⚠GPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
- →Use as dependency Concerns → Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/garethgeorge/backrest)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/garethgeorge/backrest on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: garethgeorge/backrest
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/garethgeorge/backrest shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Mixed signals — read the receipts
- Last commit 2d ago
- 22+ active contributors
- GPL-3.0 licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 75% of recent commits
- ⚠ GPL-3.0 is copyleft — check downstream compatibility
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live garethgeorge/backrest
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/garethgeorge/backrest.
What it runs against: a local clone of garethgeorge/backrest — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in garethgeorge/backrest | Confirms the artifact applies here, not a fork |
| 2 | License is still GPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 32 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of garethgeorge/backrest. If you don't
# have one yet, run these first:
#
# git clone https://github.com/garethgeorge/backrest.git
# cd backrest
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of garethgeorge/backrest and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "garethgeorge/backrest(\\.git)?\\b" \\
&& ok "origin remote is garethgeorge/backrest" \\
|| miss "origin remote is not garethgeorge/backrest (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is GPL-3.0" \\
|| miss "license drift — was GPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "cmd/backrest/backrest.go" \\
&& ok "cmd/backrest/backrest.go" \\
|| miss "missing critical file: cmd/backrest/backrest.go"
test -f "go.mod" \\
&& ok "go.mod" \\
|| miss "missing critical file: go.mod"
test -f "gen/go/v1/service.pb.go" \\
&& ok "gen/go/v1/service.pb.go" \\
|| miss "missing critical file: gen/go/v1/service.pb.go"
test -f ".goreleaser.yaml" \\
&& ok ".goreleaser.yaml" \\
|| miss "missing critical file: .goreleaser.yaml"
test -f "internal" \\
&& ok "internal" \\
|| miss "missing critical file: internal"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 32 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~2d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/garethgeorge/backrest"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Backrest is a web UI and orchestrator for restic backup, written in Go with a TypeScript frontend, that wraps the restic CLI to provide scheduling, repository management, snapshot browsing, and file restoration through an intuitive interface. It solves the problem of making restic—a powerful but CLI-only backup tool—accessible to non-technical users while adding automated scheduling, health checks, and multi-backend storage support (S3, B2, Azure, GCS, rclone remotes). Monorepo structure: cmd/backrest/ contains the main Go binary entry points with platform-specific code (main_linux.go, main_darwin.go, main_windows.go, tray.go for system tray); webui/ likely contains the TypeScript frontend (598KB codebase); docs/ is a separate Vitepress site. State/configuration is stored in restic repositories themselves rather than external databases, with Go backend handling orchestration and the TS frontend providing the web interface.
👥Who it's for
System administrators and NAS owners who need reliable encrypted backups but want to avoid learning restic CLI; DevOps engineers deploying backup infrastructure across multiple platforms (Linux, macOS, Windows, Docker, FreeBSD); users managing existing restic repositories who want a GUI and automation layer.
🌱Maturity & risk
Actively developed and production-ready. The project has significant GitHub activity (release pipelines, comprehensive test workflows in .github/workflows/), multi-platform support baked in (Dockerfile.alpine, Dockerfile.scratch, build/darwin/, build/windows/), and is packaged as a standalone binary with restic as the sole dependency. Clear semver versioning via release-please.yml suggests disciplined releases.
Low risk for most deployments. Single maintainer (garethgeorge) is a factor, but the project wraps a mature, battle-tested tool (restic) rather than reimplementing backup logic. Dependency risk is minimal since Go binaries are self-contained and restic is auto-downloaded. Main risk: tight coupling to restic CLI behavior means restic API changes require quick updates.
Active areas of work
Active release cycle via GitHub Actions (release.yml, release-preview.yml, release-please.yml), automated restic version tracking (update-restic.yml), and multi-platform build pipeline (.goreleaser.yaml). Development environment setup documented in .devcontainer/ suggests recent focus on contributor experience.
🚀Get running
Clone and run with: git clone https://github.com/garethgeorge/backrest && cd backrest && go build -o backrest ./cmd/backrest && ./backrest. Access the web UI at http://localhost:9898. For development with the devcontainer: code . --remote-code-server to use the .devcontainer/devcontainer.json setup.
Daily commands:
Development: go run ./cmd/backrest/backrest.go for the backend; frontend likely built separately. Production: download binary from releases or docker run garethgeorge/backrest. Access http://localhost:9898. The binary handles everything—restic is auto-downloaded on first run.
🗺️Map of the codebase
cmd/backrest/backrest.go— Main entry point for the Backrest application; sets up the CLI, initializes core systems, and orchestrates startup across all platforms.go.mod— Defines all Go dependencies and module version constraints; essential for understanding build environment and upstream library versions used by Backrest.gen/go/v1/service.pb.go— Auto-generated gRPC service definitions that define the core API contract between the backend and web frontend; changes here ripple through the entire system..goreleaser.yaml— Orchestrates multi-platform binary builds and release packaging; defines how artifacts are distributed across macOS, Windows, and Linux.internal— Root package containing core business logic, restic orchestration, backup scheduling, and configuration management; all major subsystems live here.docs/src/.vitepress/config.mts— Documentation site configuration and navigation structure; critical for onboarding contributors and end-users on features and API usage.Dockerfile.alpine— Production container image definition; shows runtime dependencies, layer optimization, and how Backrest is deployed in containerized environments.
🛠️How to make changes
Add a New gRPC API Endpoint
- Define the new RPC method and message types in the proto definition file (
gen/go/v1/service.pb.go (requires editing the .proto source, then regenerating)) - Implement the service handler in the internal package following existing patterns (
internal (create or update service handler matching generated interface)) - Wire up the handler in the main gRPC server setup in the entrypoint (
cmd/backrest/backrest.go (register handler in server initialization)) - Add integration tests validating the new endpoint (
internal (follow existing test patterns alongside service code)) - Document the new endpoint with examples and OpenAPI annotations (
docs/src/docs/api.md (add endpoint documentation and usage examples))
Add a New Backup Job Hook or Automation
- Add hook type definition to config messages (
gen/go/v1/config.pb.go (extend HookConfig message type)) - Implement hook handler in the internal orchestration package (
internal (create hook executor following existing hook patterns)) - Integrate hook invocation into the backup job execution pipeline (
internal (call hook executor at appropriate lifecycle points in job runner)) - Test hook execution with mock scenarios and real backup flows (
internal (add test cases covering hook success and failure paths)) - Document hook type, environment variables, and examples (
docs/src/docs/hooks.md and docs/src/cookbooks/slack-hook-build-kit-examples.md)
Deploy Backrest in a New Environment (e.g., Docker Compose, Kubernetes)
- Review existing container images and determine if Docker.alpine or Docker.scratch fits your needs (
Dockerfile.alpine or Dockerfile.scratch (base image, dependencies, entrypoint)) - Create environment-specific docker-compose or Kubernetes manifests following existing patterns (
Create manifests in docs/src/cookbooks/ or contribute to installation guide) - Configure reverse proxy or ingress for HTTPS/TLS termination (
docs/src/cookbooks/reverse-proxy-examples.md (add your deployment example)) - Set up persistent volume mounts for config and cache storage (
Reference cmd/docker-entrypoint/main.go for environment variable expectations) - Document your deployment pattern in the cookbooks for community reference (
docs/src/cookbooks/ (create new markdown file with your architecture diagram))
Add Support for a New Restic Backend Storage Type
- Extend config message to include new backend credentials and options (
gen/go/v1/config.pb.go (add RepoBackendConfig variant for new storage type)) - Implement backend initialization and validation logic (
internal (create backend provider following adapter pattern for existing backends)) - Add environment variable or config translation for restic CLI invocation (
internal (ensure restic CLI receives correct environment/flags for backend auth)) - Create integration tests using storage emulator or mock backend (
internal (test successful repo init, snapshot, and restore with new backend)) - Document credentials, connection settings, and gotchas (
docs/src/docs/operations.md (add backend setup section) and docs/src/introduction/getting-started.md)
🔧Why these technologies
- Go — Single static binary, cross-platform compilation (Linux, macOS, Windows), minimal runtime overhead, native concurrency support for job scheduling and orchestration.
- gRPC + Protocol Buffers — Type-safe, language-agnostic API contract between backend and web frontend; efficient serialization for frequent polling of backup status and snapshot metadata.
- Restic CLI — Mature, proven backup tool with strong encryption and deduplication; Backrest wraps rather than reimplements to avoid cryptographic mistakes and leverage Restic's stable feature set.
- Vue 3 + VitePress — Modern frontend framework for responsive web UI; VitePress used for documentation site to reduce build complexity and keep docs co-located with code.
- SQLite / embedded persistence — Zero external database dependencies; simplifies deployment on single machines and small servers; sufficient for backup job metadata and scheduling state.
⚖️Trade-offs already made
- Wrap Restic CLI instead of embedding Restic library
- Why: Reduces maintenance burden of staying in sync with
- Consequence: undefined
🪤Traps & gotchas
Restic must be installed or auto-downloaded (happens on first run, but network access required). The web UI port defaults to 9898—if you need a different port, check for environment variable or config file flags (not shown in file list, likely in cmd/backrest/backrest.go). Platform-specific code is split across main_*.go files—compiling for a target OS requires the right build flags. The system tray integration uses cgo on some platforms (tray.go), which has CGO_ENABLED requirements. Storage backends are entirely delegated to restic, so any backend issues are upstream restic problems, not Backrest bugs.
🏗️Architecture
💡Concepts to learn
- Cron scheduling and background orchestration — Backrest's core value over raw restic is automated scheduling of backups and maintenance tasks (prune, check, forget) without user intervention; this requires understanding how the Go backend manages and executes cron jobs
- CLI wrapping and subprocess management — Backrest operates by spawning restic as a subprocess and parsing its output; understanding Go's os/exec package and how to safely handle subprocess lifecycle is crucial for adding features or debugging restic interactions
- Multi-platform cross-compilation and platform-specific entry points — The codebase uses build tags and platform-specific files (main_linux.go, main_darwin.go, main_windows.go) to handle system tray, service installation, and UI launching differently per OS; critical for local development and platform-specific bugs
- System tray integration and background service patterns — Backrest runs both as a daemon and with system tray UI on desktop platforms; tray.go and tray_stub.go show how Go can integrate with native platform UIs without blocking the HTTP server
- Configuration management and hot-reload patterns — Backrest allows users to configure backup plans via the web UI without restarting; the backend must handle parsing, validation, and applying config changes while respecting running jobs
- Goreleaser multi-platform builds and artifact management — The .goreleaser.yaml file orchestrates building binaries for 6+ OS/arch combinations and packaging them into installers; understanding this reduces friction for releases and forks
- Devcontainer standardization — The .devcontainer/devcontainer.json ensures contributors have reproducible, isolated development environments; critical for cross-platform contribution friction reduction
🔗Related repos
restic/restic— The underlying backup engine that Backrest wraps; understanding restic's architecture and CLI is essential for Backrest developmentrclone/rclone— Provides storage backend abstraction that Backrest leverages via restic for cloud storage (S3, B2, Azure, GCS, SFTP, etc.)prometheus-community/helm-charts— Backrest users often deploy in Kubernetes/Helm environments; related monitoring and orchestration ecosystemborgbackup/borg— Alternative deduplicating backup solution that solves a similar problem but with different trade-offs and CLI-only interfaceDuplicacy/duplicacy— Another web UI–based backup tool for restic-like deduplication; direct competitor showing the problem space demand
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add E2E tests for restic integration workflows in cmd/backrest/
The repo has extensive Go code for restic orchestration (cmd/backrest/) but no visible test files. Given that Backrest's core value is wrapping restic CLI safely and reliably, adding integration tests would catch regressions in backup/restore workflows. This is high-value because backup software requires high reliability.
- [ ] Create cmd/backrest/*_test.go files for main entry points (backrest.go, tray.go)
- [ ] Add integration tests that verify restic CLI invocation with mock repositories
- [ ] Test cross-platform behavior (icon_unix.go, icon_windows.go, main_darwin.go, main_linux.go, main_windows.go)
- [ ] Integrate tests into .github/workflows/test.yml (currently exists but may not cover Go tests comprehensively)
Document CLI flags and configuration options in docs/src/docs/
The docs directory structure shows guides for getting-started, operations, hooks, and multihost, but there's no visible API/CLI reference for backrest binary flags. cmd/backrest/main_*.go likely contains flag definitions that aren't documented, creating friction for users trying to configure the tool programmatically or via Docker.
- [ ] Audit cmd/backrest/backrest.go and main_*.go for all flag definitions
- [ ] Create docs/src/docs/cli-reference.md documenting all available flags, environment variables, and their behavior
- [ ] Add examples for Docker usage with flags to docs/src/introduction/getting-started.md
- [ ] Cross-reference CLI options in docs/src/docs/operations.md for operational scenarios
Add GitHub Action to validate Dockerfile builds on PRs
The repo maintains multiple Dockerfiles (Dockerfile.alpine, Dockerfile.scratch) and a docker-entrypoint (cmd/docker-entrypoint/main.go), but .github/workflows/ doesn't show a Docker build validation step. This could catch breaking changes early, especially since Docker is a primary deployment method (README shows docker/pulls badge).
- [ ] Create .github/workflows/docker-build.yml to test both Dockerfile.alpine and Dockerfile.scratch on PRs
- [ ] Verify docker-entrypoint compiles and runs with mock configurations
- [ ] Add build caching to keep CI fast (docker/build-push-action with cache)
- [ ] Consider pushing images to GHCR for release builds (complement .github/workflows/release.yml)
🌿Good first issues
- Add missing integration tests for the backup scheduling logic by creating tests in internal/ that mock restic CLI calls and verify cron job execution matches configured plans—currently no test files visible in the file list.
- Enhance error handling in cmd/backrest/backrest.go by adding structured error messages and retry logic for restic operations that commonly fail transiently (e.g., network timeouts to S3 backends).
- Document the configuration schema for the YAML config file with examples for each storage backend (S3, B2, Azure, GCS, rclone) in docs/src/configuration.md, since the README mentions rclone support but provides no setup examples.
⭐Top contributors
Click to expand
Top contributors
- @garethgeorge — 75 commits
- @csm10495 — 2 commits
- @homandr — 2 commits
- @kyohenoki — 2 commits
- @ziotibia81 — 2 commits
📝Recent commits
Click to expand
Recent commits
680df5e— fix: sync errors due to buffer reuse when streaming large batches of operation data (garethgeorge)d641593— fix: allow multihost sync to use real h2 (rather than h2c) when using https (garethgeorge)2faa522— docs: update multihost docs (garethgeorge)a389bbc— ci: grant contents:write and packages:write to goreleaser job (garethgeorge)8cf3a32— ci: set up docker buildx for dockers_v2 sbom attestations (garethgeorge)cb49e14— chore(main): release 1.13.0 (#1169) (garethgeorge)900dd53— chore: fix README.md install instructions (garethgeorge)cd6c5ed— fix: improve install.sh on macOS and linux (garethgeorge)5053371— feat: apply multihost sync refinements found in validation pass (garethgeorge)b7e9448— fix: refine config validation policies (garethgeorge)
🔒Security observations
The Backrest repository shows a generally reasonable security posture with moderate-risk vulnerabilities identified primarily in dependency management and configuration. The codebase uses security-positive practices such as Alpine and scratch Docker images. Main concerns include: (1) dependency versions should be regularly updated and scanned for vulnerabilities, (2) security headers should be explicitly configured in the VitePress documentation site, (3) environment configuration files should be carefully managed to prevent credential exposure, and (4) the restic CLI wrapper should be thoroughly reviewed for injection vulnerabilities. Implementing automated dependency scanning, security header configuration, and comprehensive input validation would significantly improve the security posture.
- Medium · Outdated ESLint Dependency —
docs/package.json - devDependencies.eslint. ESLint version ^8.57.0 is used in the docs package. This version is from early 2024 and may contain known vulnerabilities. ESLint should be kept up-to-date to patch security issues in the linting framework. Fix: Update ESLint to the latest stable version (9.x or later). Run 'npm audit' to identify and remediate known vulnerabilities. - Medium · Outdated VitePress Dependency —
docs/package.json - devDependencies.vitepress. VitePress version ^1.0.0 is pinned to a specific major version. While this provides stability, it may miss critical security updates. The package should be regularly updated to receive security patches. Fix: Regularly update VitePress to the latest patch version. Implement automated dependency checking via Dependabot or similar tools in CI/CD pipeline. - Medium · Permissive Package Version Constraints —
docs/package.json - all devDependencies. Dependencies use caret (^) version constraints which allow minor and patch updates automatically. While this enables security patches, it may introduce breaking changes without explicit review. No lock file verification strategy is evident. Fix: Ensure package-lock.json is committed and verified in CI/CD. Consider using more restrictive version constraints for production dependencies. Implement automated security scanning with 'npm audit' in the CI pipeline. - Low · Missing Security Headers Configuration —
docs/src/.vitepress/config.mts. No explicit security headers configuration visible in the VitePress configuration (docs/src/.vitepress/config.mts). Standard security headers like CSP, X-Frame-Options, X-Content-Type-Options, and HSTS should be configured. Fix: Configure security headers in the VitePress config or web server. Set appropriate Content-Security-Policy, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and Strict-Transport-Security headers. - Low · Potential Unencrypted Configuration Files —
.envrc. The repository contains .envrc file which may contain environment variables. If this file contains sensitive credentials, they could be exposed in the repository history. Fix: Ensure .envrc is in .gitignore and never committed with sensitive data. Use secret management tools for credential handling. Document expected environment variables without storing actual values. - Low · Docker Image Base Security Not Explicitly Verified —
Dockerfile.alpine, Dockerfile.scratch. Dockerfile.alpine and Dockerfile.scratch are present but their content is not visible in the provided file structure. Alpine and scratch images are security-positive choices, but the base image version and build practices should be reviewed. Fix: Verify base images use specific version tags (not 'latest'). Regularly scan Docker images for vulnerabilities using tools like Trivy. Implement image signing and verification. - Low · No Visible Input Validation in Static Analysis —
cmd/backrest/ (Go source files). The file structure shows a backup management application (Backrest) that wraps restic CLI. Without visibility into the Go source code, potential injection risks in CLI argument handling cannot be fully assessed. Fix: Review all restic CLI invocations to ensure proper argument escaping and validation. Use parameterized commands or libraries that prevent shell injection. Implement input sanitization for user-provided paths and parameters.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.