BorgUI is a web-based management interface for Borg backup repositories.
Find a file
2025-03-16 16:38:46 +01:00
cmd/borgui add version command 2025-03-11 01:02:44 +01:00
docs update readme, quickstart, add screenshots 2025-03-16 16:25:41 +01:00
hack update readme and rename some files 2025-03-15 04:08:24 +01:00
internal remove unsecure defaults 2025-03-16 15:31:22 +01:00
.air.toml refactor to echo web framework 2025-02-26 04:44:11 +01:00
.cursorignore add ignore 2025-03-12 04:11:07 +01:00
.envrc move scripts 2025-02-23 21:14:04 +01:00
.gitignore live reloading 2025-02-25 00:49:36 +01:00
.golangci.yaml update 2025-02-27 03:31:30 +01:00
config.yaml remove unsecure defaults 2025-03-16 15:31:22 +01:00
config.yaml.example remove unsecure defaults 2025-03-16 15:31:22 +01:00
devkey fake ssh server and tooling 2025-02-23 19:39:28 +01:00
docker-compose.yaml update readme and rename some files 2025-03-15 04:08:24 +01:00
go.mod use go-cmd 2025-03-12 04:11:01 +01:00
go.sum use go-cmd 2025-03-12 04:11:01 +01:00
LICENSE add license 2025-03-16 16:30:26 +01:00
Makefile update readme and rename some files 2025-03-15 04:08:24 +01:00
README.md fix readme 2025-03-16 16:38:46 +01:00
release.sh update readme and rename some files 2025-03-15 04:08:24 +01:00

borgui

BorgUI is for anyone who wants to self-host Borg repositories but prefers a web interface over command-line tools for repository management and SSH key administration.

The primary deployment scenario that drove BorgUI's development consists of a Hetzner Storage Box with public SSH access hosting the Borg repositories, and BorgUI running on a Hetzner Cloud VM accessible only through a private Tailscale network.

While self-hosting requires more work than managed services, it offers greater control over backup infrastructure and typically costs less for large backup volumes. For users seeking a fully-managed alternative with less administrative overhead, BorgBase.com provides an excellent service.

Screenshots

Below are screenshots of BorgUI's interface to give you a better understanding of its features and user experience.

Repository List Add Repository
Repository List Add Repository
Repository Details Repository Settings Borgmatic Configuration
Repository Details Repository Settings Borgmatic Configuration
Admin Clients Task Execution Status Dashboard
Add Admin Key Task Execution Status Dashboard

Features

Features:

  • A web interface for creating, monitoring, and managing Borg repositories
  • Repository operations through SSH
  • Support for setting per-repository quotas
  • Management of per-repository SSH public keys for append-only backups
  • Additional admin client SSH public keys with unrestricted access to all repositories
  • Automated repository scanning via borg info
  • Scheduled background tasks including repository compaction, integrity checks, and data verification
  • Archive grouping functionality to organize of related backup archives
  • Prometheus metrics for borgui internals and repository information

Anti-Features:

  • Repository deletion - you can remove a repository from BorgUI, but the directory on disk won't be deleted

Repository Management

If repositories already exist and you provide the passphrase file, BorgUI will pick them up without re-initializing them. In this case, you need to manually scan them once or wait for the next daily scan.

If a repo gets renamed or removed in storage, BorgUI will detect this and mark the repo as orphaned. The web interface allows you to resolve this by either renaming the repo metadata in BorgUI's database to point to the renamed directory, or by creating a new repo with the old name.

SSH Key Management

It is expected that there is a 1:1 relation between backed up host and borg repository, thus every repository can only have one backup SSH key. Such a repository SSH key is restricted to that specific repository and can only append data to the repo. This allows us to run borg prune and borg check on the target machine, but not borg compact as the latter would be destrucive. BorgUI provides a way to compact the repo, its recommended to do this only every 90 days or more to leave some time for recovering old archives when there is a problem.

Admin clients can be added separately for manual administrative tasks like info, check, compact, repair, or manual restore. Admin client keys are not restricted to specific repositories.

Archive Groups

When backing up a NAS with several shared volumes, you might create one repository for the NAS with separate archives for each volume created daily. By default, Borg's CLI lists all these archives intermixed, making it difficult to filter and manage related backups. You can use --glob-archives to limit borg output to a single archive prefix, but this is rather cumbersome to use when you want to get an overview.

With BorgUI's archive groups, you can logically organize related archives into cohesive collections. This organization simplifies management, improves visualization of your backup sets, and enables automatic generation of borgmatic configuration files tailored to each group of archives. This approach combines the storage efficiency of a single repository with the organizational benefits of logically separated backups.

Task Execution

Borg locks the repository during most operations, allowing only one operation per repository at a time. Currently, the background queue executes one task at a time, so there are no parallel operations even across repositories. This could be optimized in the future with one parallel task per repository,

Scanning and Monitoring

Scanning is done via borg info, which may take a few minutes for larger repositories. In most cases, borg backups are executed daily so it doesnt make sense to scan more often. Thus, BorgUI scans all repositories once per day by default. Prometheus metrics are generated based on the internal state in the database, not by running borg info on every scrape. Long-running Borg task output can be viewed in the per-repository event log.

Comparison with BorgBase

BorgUI is not intended as a comprehensive replacement for BorgBase, but rather focuses on a small set of features for my specific use case.

Compared to BorgBase, BorgUI offers less flexible)SSH key management and no public REST API or API tokens found in BorgBase. It doesn't include built-in graphing, monitoring and alerting, but provides comprehensive Prometheus metrics that can integrate with existing monitoring infrastructure. For many deployments, BorgUI can effectively replace most Borg Prometheus exporters.

BorgUI is intended to be run by the same entity who owns the backup repositories and thus can be allowed access to repository contents. It regularly scans repositories via borg info and provides detailed statistics of repository contents including archive information and size breakdowns. It can also schedule regular maintenance tasks in the background for each repository, including compaction, integrity checks, and data verification. Note that those features need access to repository passphrases via passphrase files on disk.

Quick Start with Hetzner Storage Box

Before we can start, you'll need:

  • A Hetzner Storage Box (or similar service) with public SSH access enabled
  • A linux host (e.g. Hetzner Cloud VM) with ssh and borgbackup installed, to run BorgUI

When you use a Hetzner Storage Box, a Hetzner Cloud VM is the obvious choice because traffic between the Storage Box and the Cloud VM is free. This is negligible for borg init, borg info and borg compact as those run server-side, but borg check performs some operations on the client machine which requires to read all data from the server and is thus very traffic-intensive.

First, install BorgUI on the linux host (see below, section installation).

Create directories:

mkdir -p /etc/borgui
mkdir -p /var/lib/borgui
mkdir -p /var/lib/borgui/passwords
chown -R borgui:borgui /var/lib/borgui
chmod 600 /var/lib/borgui/passwords

Create BorgUI admin SSH key:

ssh-keygen -t ed25519 -C "borgui-admin" -f /etc/borgui/adminkey -N ""
chown borgui:borgui /etc/borgui/adminkey
chmod 600 /etc/borgui/adminkey

Create known_hosts file for storage box:

ssh-keyscan -p 23 u123456.your-storagebox.de > /etc/borgui/known_hosts

Create /etc/borgui/config.yaml with storage box connection details:

db: "/var/lib/borgui/borgui.db"
ssh-hostname: "u123456.your-storagebox.de"
ssh-port: "23"  # Hetzner Storage Box uses port 23 for SSH
ssh-username: "u123456"
ssh-admin-key-file: "/etc/borgui/adminkey"
ssh-known-hosts-file: "/etc/borgui/known_hosts"
borg-passwords-dir: "/var/lib/borgui/passwords"
server-session-key: "your-secure-session-key"

Inspect authorized_keys content that will be written to the storage box:

borgui storagebox authorized-keys

Obtain the storage box ssh password from the Hetzner Robot console and initialize your storage box for use with BorgUI:

borgui storagebox init -p "your-ssh-password"

This command:

  • Writes the borgui admin ssh key to /home/.ssh/authorized_keys (existing file will be overwritten)
  • Creates an empty /home/borg-repositories directory where borg repositories are stored (no-op if this directory already exists)

For each repository you plan to init, scan, or check via the web UI, add the passphrase to a file in /var/lib/borgui/passwords/<reponame>. Each file must be readable only by the borgui user and contain only the repository password, no newlines or other content.

Start the BorgUI web server with:

BORG_BASE_DIR=/var/lib/borgui borgui serve

Note: BORG_BASE_DIR configures where the local borg cache is written to.

Access the web interface on http://127.0.0.1:8080 through your browser (e.g. via ssh port forwarding) and manage repositories from there. Deploying a reverse proxy or tailscale is out of scope for a quickstart guide. If you dont know what you are doing, use borgbase instead.

Installation

Standalone

Clone the repository:

git clone https://forge.xchangeee.net/xchange/borgui.git
cd borgui

Build the project:

make build

Install the binary:

sudo install -m 755 bin/borgui /usr/local/bin/borgui

NixOS

tbd

Configuration

BorgUI can be configured through multiple methods (in order of precedence):

  1. Command line flags
  2. Environment variables (prefixed with BORGUI_)
  3. Configuration file
  4. Default values

BorgUI searches for a config.yaml file in the following locations:

  • ./config.yaml (current directory)
  • $HOME/.borgui/config.yaml
  • $HOME/.config/borgui/config.yaml
  • /etc/borgui/config.yaml

The following configuration options are required:

  • db: Path to the SQLite database file
  • ssh-hostname: Remote SSH server hostname
  • ssh-username: SSH username
  • ssh-admin-key-file: Path to SSH private key
  • ssh-known-hosts-file: Path to SSH known hosts file
  • borg-passwords-dir: Directory containing repository passwords

Usage

Initialize storage box with SSH password authentication:

borgui storagebox init -p "your-ssh-password"

Generate and print authorized_keys file content:

borgui storagebox authorized-keys

Start web server:

borgui serve

Monitoring

BorgUI exposes Prometheus metrics at /metrics. The following metrics are available:

Build Information

Metric Description Labels Type
borgui_build_info Build information constant (1.0) version - Version from git tag
commit - Git commit hash
branch - Git branch name
goversion - Go runtime version
builddate - Build timestamp
Gauge

Repository Metrics

Metric Description Labels Type
borgui_repositories Total number of repositories managed by BorgUI - Gauge
borgui_repository_size_bytes Repository size in bytes repository - Repository name
type - Size type (total, compressed, unique)
Gauge
borgui_repository_archives Number of archives in repository repository - Repository name Gauge
borgui_repository_last_scan_timestamp Unix timestamp of last successful scan repository - Repository name Gauge
borgui_repository_last_backup_timestamp Unix timestamp of newest archive repository - Repository name Gauge

Task Queue Metrics

Metric Description Labels Type
borgui_task_queue_size Number of tasks in the queue - Gauge
borgui_task_duration_seconds Task execution duration task_type - Type of task Histogram
borgui_task_errors_total Total number of task failures task_type - Type of task Counter

Example PromQL Queries

# Repositories without recent backups (>24h)
borgui_repository_last_backup_timestamp < (time() - 86400)

Security Considerations

BorgUI does not implement its own authentication or authorization mechanisms and is intended to be run behind a reverse proxy with http basicauth or other access controls. It should never be exposed directly to the public internet.

Configuration Reference

Here's a complete reference of all available configuration options:

# Application configuration
# Optional: Log level
log-level: "INFO"
# Required: Path to SQLite database file
db: "/var/lib/borgui/borgui.db"

# SSH configuration
# Required: Remote SSH server hostname
ssh-hostname: "backup.example.com"
# Optional: SSH port
ssh-port: 22
# Required: SSH username
ssh-username: "borgui"
# Required: Path to SSH private key for admin operations
ssh-admin-key-file: "/path/to/key"
# Required: Path to SSH known hosts file
ssh-known-hosts-file: "/path/to/hosts"

# Borg configuration
# Required: Directory containing repository passwords
borg-passwords-dir: "/path/to/passwords"

# Server configuration
# Optional: Server address to listen on
server-addr: "127.0.0.1:8080"
# Optional: Session key for securing sessions
server-session-key: "change-me-in-production"
# Optional: Server read timeout
server-read-timeout: 15s
# Optional: Server write timeout
server-write-timeout: 15s
# Optional: Server idle timeout
server-idle-timeout: 60s

# Job configuration
# Optional: Enable repository scanning
server-repo-scan-enabled: true
# Optional: Repository scan schedule
server-repo-scan-schedule: "0 0 14 * * *"
# Optional: Enable repository compaction
server-repo-compaction-enabled: true
# Optional: Repository compaction schedule
server-repo-compaction-schedule: "0 0 0 * * *"
# Optional: Enable repository simple check
server-repo-simple-check-enabled: true
# Optional: Repository simple check schedule
server-repo-simple-check-schedule: "0 0 1 * * *"
# Optional: Enable repository verify data check
server-repo-verify-data-check-enabled: true
# Optional: Repository verify data check schedule
server-repo-verify-data-check-schedule: "0 0 2 * * *"

All configuration options can be set via environment variables by prefixing with BORGUI_ and using uppercase, with hyphens replaced by underscores. For example:

# Required configuration
export BORGUI_DB="/path/to/db"
export BORGUI_SSH_HOSTNAME="backup.example.com"
export BORGUI_SSH_USERNAME="borgui"
export BORGUI_SSH_ADMIN_KEY_FILE="/path/to/key"
export BORGUI_SSH_KNOWN_HOSTS_FILE="/path/to/hosts"
export BORGUI_BORG_PASSWORDS_DIR="/path/to/passwords"

# Optional configuration
export BORGUI_LOG_LEVEL="INFO"
export BORGUI_SSH_PORT="22"
export BORGUI_SERVER_ADDR="127.0.0.1:8080"
export BORGUI_SERVER_SESSION_KEY="change-me-in-production"
export BORGUI_SERVER_READ_TIMEOUT="15s"
export BORGUI_SERVER_WRITE_TIMEOUT="15s"
export BORGUI_SERVER_IDLE_TIMEOUT="60s"

# Job configuration
export BORGUI_SERVER_REPO_SCAN_ENABLED="true"
export BORGUI_SERVER_REPO_SCAN_SCHEDULE="0 0 14 * * *"
export BORGUI_SERVER_REPO_COMPACTION_ENABLED="true"
export BORGUI_SERVER_REPO_COMPACTION_SCHEDULE="0 0 0 * * *"
export BORGUI_SERVER_REPO_SIMPLE_CHECK_ENABLED="true"
export BORGUI_SERVER_REPO_SIMPLE_CHECK_SCHEDULE="0 0 1 * * *"
export BORGUI_SERVER_REPO_VERIFY_DATA_CHECK_ENABLED="true"
export BORGUI_SERVER_REPO_VERIFY_DATA_CHECK_SCHEDULE="0 0 2 * * *"

All configuration options can also be set via command line flags by converting the YAML keys to flags. For example:

borgui serve \
  --log-level="INFO" \
  --db="/path/to/db" \
  --ssh-hostname="backup.example.com" \
  --ssh-username="borgui" \
  --ssh-admin-key-file="/path/to/key" \
  --ssh-known-hosts-file="/path/to/hosts" \
  --borg-passwords-dir="/path/to/passwords" \
  --server-addr="127.0.0.1:8080" \
  --server-session-key="change-me-in-production" \
  --server-repo-scan-enabled=true \
  --server-repo-scan-schedule="0 0 14 * * *"

Development

Prerequisites:

  • Go 1.24 or later
  • Borg Backup
  • SQLite3
  • Docker (for fake SSH server)
  • golangci-lint (for code quality checks)
  • air (for hot reload during development)
  • direnv (optional, for automatic environment setup)

The project includes a .envrc file for use with direnv, which automatically sets up your shell environment when you enter the project directory. This adds the bin and hack/scripts directories to your PATH.

The project uses Make for common development tasks. The binary is built with version information from git (version, commit, branch, build date).

Build:

  • make all - Complete build pipeline: format → deps → check → test → build
  • make build - Build binary with version info (git tag/commit/branch/date)

Development:

  • make format - Format code with go fmt
  • make deps - Install and verify Go dependencies
  • make check - Run static analysis (go vet + golangci-lint)
  • make test - Run all tests
  • make test-coverage - Run tests with HTML coverage report
  • make testdata - Set up test environment with local ssh server and test data
  • make devserver - Run with hot reload (requires air)
  • make clean - Remove build artifacts and clean test environment

Useful scripts:

  • inspect-sshserver.sh - Open an interactive shell in the test SSH server container

    Useful for:

    • Debugging SSH server configuration
    • Inspecting repository contents directly
    • Testing SSH connectivity
  • populate-test-repos.sh - Create test repositories with sample backup archives