Borg UI¶
Borg UI is a self-hosted web interface for Borg: browse archives, search for a file, restore it, run checks, and see what each repository holds. It can also create repositories and schedule backups, but I don't use it for that.
What it's for here¶
borgmatic does every scheduled backup, from config in git. Borg UI runs on one server as the window into all the repositories: every server's two LAN repos plus the offsite ones, imported as existing repositories with no Borg UI schedules (observe only).
- Restoring a file is a few clicks instead of
borgmatic list --findandextract --pathon the right host. - Checking that last night's archive exists in every repo, at a glance.
- It can't make things worse: its SSH key is append-only on every repo, like the backup clients, so it can read and restore but can't delete history. Files it restores land in one dedicated directory, never over live data.
Compose¶
services:
borg-ui:
image: ainullcode/borg-ui:2.3.10
container_name: borg-ui
restart: unless-stopped
ports:
- "8083:8081"
environment:
PORT: "8081"
TZ: ${TZ}
# No Redis: in-memory cache is enough for browsing and restores
REDIS_HOST: disabled
# Only read on the very first start (creates user `admin`), so the documented default is never live
INITIAL_ADMIN_PASSWORD: ${BORG_UI_ADMIN_PASSWORD}
LOCAL_MOUNT_POINTS: /local
volumes:
# App DB (repo list + passphrases), its SSH key + known hosts, generated secret key. Back this up.
- /path/to/appdata/borg-ui/data:/data
# Borg cache: regenerable, exclude it from backups
- /path/to/appdata/borg-ui/cache:/home/borg/.cache/borg
# Restore target: files restored in the UI land here (exclude it from backups)
- /path/to/appdata/borg-ui/restore:/local
healthcheck:
test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://localhost:8081/"]
interval: 60s
timeout: 10s
retries: 3
start_period: 60s
TZ=<TZ> # e.g. Etc/UTC
BORG_UI_ADMIN_PASSWORD=<YOUR_PASSWORD> # e.g. output of: openssl rand -base64 24
# data/ and restore/ must belong to the container user (1001:1001) BEFORE the first start;
# the entrypoint only chowns when PUID changes
sudo install -d -o 1001 -g 1001 -m 0700 /path/to/appdata/borg-ui/{data,cache,restore}
docker compose up -d
The data volume holds every repo's passphrase
Borg UI stores the repository passphrases in its database under /data. Treat that directory like a password
store: 0700, backed up (encrypted, by the host's own borgmatic), and the UI only reachable from the LAN or
behind your SSO/reverse proxy. Don't publish it to the internet.
SSH keys and host keys¶
Borg UI's own key¶
Borg UI uses one system SSH key for all remote repositories. Generate it in the UI (Remote Machines), as the
docs recommend. It's stored in the data volume (/data/ssh_keys, which the container links as ~/.ssh), so it
survives upgrades and is covered by the data volume's backup.
If you'd rather import an existing key, mount it read-only and import it from that path:
Use a key made for Borg UI only, never a host's or a person's personal key.
Pin it to append-only, on every repo host¶
Add Borg UI's public key to the borg user on every server that holds repos, and to the offsite account, with a
forced command that lists every repo it may read and --append-only:
command="borg serve --append-only --restrict-to-repository /srv/borg/server2 --restrict-to-repository /srv/borg/server3",restrict ssh-ed25519 AAAA... borg-ui
Borg 1.x has no true read-only borg serve. Append-only is the closest: Borg UI can list, extract and check, and
anything it "deletes" is only marked deleted and can be recovered until the server's weekly compaction. No
--storage-quota is needed since it never writes backups.
Because the key is restricted, Borg UI's automatic repository detection when browsing a remote path doesn't work.
Type the repository path (/srv/borg/server2) by hand when you import it.
Host keys¶
Verify each server's host key the first time Borg UI connects instead of blindly accepting it. On the target:
Compare that fingerprint with what Borg UI shows. Borg UI keeps the accepted keys next to its own key in the data volume. If a host key changes later (reinstall), find out why first, then update it in the UI.
Importing the repositories¶
For each repo: Repositories โ add/import an existing repository โ pick the remote machine (borg@<server>),
enter the path, the repo's passphrase, and don't add a schedule. Name them <client> @ <where> (for example
server2 @ server1, server2 @ offsite) so you always know which copy you're looking at.
Restoring with it¶
- Open the repository, pick an archive (newest first), browse or search for the file.
- Restore to
/local(the only local mount point it offers). On the host that's therestore/directory. - Check the file, then copy it into place yourself on the host it belongs to.
Database dumps show up in the archives under borgmatic/. Restoring them is a job for the command line; see
Restore & restore testing.