Open-source project
borgmatic-collective/borgmatic avatar
borgmatic-collective/borgmatic

borgmatic: one configuration file over Borg, and the checks most people skip

Mirror of borgmatic: Simple, configuration-driven backup software for servers and workstations

2,339 stars119 forksPythonGPL-3.0

At a glance

What is it?
borgmatic is a configuration layer over Borg Backup that describes sources, retention, database dumps, command hooks, validation checks and monitoring destinations in a single YAML file, and it brings only five runtime dependencies with it. The two features that decide whether it is more than a wrapper are the per-check frequency setting and the choice of where the repository passphrase comes from.
Who is it for?
borgmatic is worth adopting when one machine holds several databases and a growing set of retention rules, because the configuration file is the whole interface and there is nothing else to learn, and the retention keys plus labelled repositories are a clearer model than a pile of command line flags in a cron entry.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A configuration layer over Borg, with encryption on the client

borgmatic describes itself as configuration-driven backup software for servers and workstations, and the description of what it does is three sentences: protect your files with client-side encryption, back up your databases too, and monitor it all with integrated third-party services. The word that carries the design is powered by, applied to Borg Backup, and that relationship should be understood before anything else. This is not a new backup format and not a competitor to Borg. It is a way of describing a backup policy in a file and then invoking Borg to carry it out, which means every property of Borg, including its deduplication and its archive model, is inherited rather than reimplemented, and every limitation of Borg is inherited too. What the layer adds is the part people actually spend time on, which is the policy. Where to keep things and for how long, which databases to dump, what to run before and after each action, whether to verify, and who to tell when it does not run. Client-side encryption is the other thing the file makes explicit. The repository is encrypted on the machine that writes it, which is what allows a hosted repository to appear in the example configuration at all, since a provider holding the repository holds ciphertext. The example even shows a remote repository reached over SSH alongside a local one, each with a label, which is the shape of the whole tool: one description of what to back up, several destinations, no duplicated configuration.

The example configuration is the specification

The README does not describe features in prose so much as show a file, and that file is the best documentation in the project. It opens with a list of source directories, then repositories as a list of objects each carrying a path and a label, which is what lets one backup run to an off-site host and a local disk without duplicating anything. Retention is three integer keys for daily, weekly and monthly, which is a familiar shape and needs no explanation. Then come the parts that carry the design. A checks list names check types, and one of them carries a frequency, so the repository check and the archive check are separate entries with separate schedules. A commands list describes hooks keyed by an action and by when that action occurs, with a script to run. A database list names databases to dump, shown here for PostgreSQL. And a healthchecks entry carries a ping URL, which is the signal that a run happened at all. Two practical notes about the file as shipped. The repository path in the example includes a specific account identifier at a hosted provider, and the healthchecks entry contains a specific ping URL with a token in it, so both are public values that have to be replaced rather than copied, and an example that carries working-looking credentials is a trap worth naming. The other is that the file is meant to be hand-edited, which is visible later in the tooling rather than here, and it is the reason for two of the project's five dependencies.

yaml
source_directories:
    - /home
    - /etc

repositories:
    - path: ssh://[email protected]/./repo
      label: borgbase
    - path: /var/lib/backups/local.borg
      label: local

keep_daily: 7
keep_weekly: 4
keep_monthly: 6

checks with a frequency, and why the two check types are separated

The checks section is the most consequential thing in the configuration and the easiest to overlook, because backup software is usually judged on whether it wrote a file rather than on whether anyone can read that file back. The example distinguishes two check types. A repository check asks whether the repository itself is intact and reachable. An archive check verifies the archives themselves, and it carries its own frequency, set in the example to once every two weeks. That separation is correct, and it is the kind of correctness that comes from someone who has operated backups rather than from a feature list, because the two operations have wildly different costs. Reading metadata for every archive in a repository becomes expensive as the repository grows, so running it on every invocation turns a backup job into a long job, and running it never means you find out about a corrupted archive during the restore you needed. The configuration therefore lets you check the cheap thing often and the expensive thing on a schedule you choose. What the README does not say is what happens when a check fails, and that is the question to take to the documentation before you rely on it. Specifically, whether a failed archive check stops pruning, whether anything is reported beyond a log line, and whether the exit status of the run changes so that your scheduler notices. Those details determine whether a check is a safety net or a source of noise, and they are exactly the kind of thing that is documented on a reference page rather than in a readme.

yaml
checks:
    - name: repository
    - name: archives
      frequency: 2 weeks

Command hooks, database dumps, and the question the readme leaves open

Backing up a database that is being written to produces a file that may not be restorable, and every backup tool has to answer that. borgmatic's answer is visible in two places in the configuration, and the second is the interesting one. There is a list of databases to dump, shown for PostgreSQL, which tells the tool what to capture rather than leaving it to a hook you write. And there is a commands list, which lets you run your own script, keyed by an action and by when in the lifecycle that it should run, with the example showing a preparation script run before a create action. The generalisation is what matters. Anything that needs to happen around a backup can be expressed as a hook, which covers a database the tool does not know about, a filesystem snapshot before the read and a prune after it, a service that must be quiesced, or a mount that has to exist. It also covers the filesystems and volume managers in the integrations list, which sits alongside the databases and includes OpenZFS, Btrfs and LVM, so the sensible reading is that the hook mechanism is the general answer and the built-in database support is a convenience for the common cases. The README does not describe that mechanism for the filesystems, so the documentation is where to look. The unanswered question is the one that matters for trusting a schedule. If a script in a before hook exits non-zero, does the backup still run and produce an archive of a database that was not dumped cleanly, or does the run abort? A tool that aborts is safer, and a tool that continues is more convenient, and the difference decides whether a failed dump is a failed backup or a quiet corruption. Nothing in the file or the readme says, and that is the first thing to establish.

yaml
commands:
    - before: action
      when: [create]
      run: [prepare-for-backup.sh]

Where the repository passphrase comes from

Client-side encryption makes one secret the most important thing in the whole system, and borgmatic has a dedicated integration category for where that secret lives. The list covers systemd, Docker, Podman, KeePassXC and 1Password, and the grouping matters more than the individual entries. A systemd source means the passphrase can be provided by the init system's credential mechanism, so it is delivered to the process at run time rather than sitting in a configuration file or an environment variable in a shell history. The container runtimes give you the same property through whatever secret mechanism they use, which matters because a backup container is exactly the situation where an environment variable ends up in an inspect output. The password managers are the human answer, and they are the option most people will actually use, with the trade-off that the backup then needs the password manager unlocked on a schedule, which is a failure mode of its own and one that will surface as a failed run rather than as a wrong backup. Whichever you pick, the alternative in the example configuration is a passphrase in the file or on a command line, and neither belongs in a file under a backup directory. This is also the one part of the configuration where the tool cannot help you, because there is no correct answer, only the one your operational model can keep available at three in the morning. It is worth deciding deliberately and writing down where the secret lives, since the recovery procedure for a lost repository is a different document from the backup procedure, and the two are the same problem viewed from opposite ends.

One notification integration, a dozen monitoring destinations, and a ping URL

The monitoring list is long enough to look like a list of separate integrations and is in fact mostly one. Monitoring covers Healthchecks, Uptime Kuma, Cronitor, PagerDuty, Pushover, ntfy, Loki, Apprise, Zabbix, Sentry and Watchgoose, and Apprise appears in the same list as the notification fan-out mechanism, which explains the breadth: targets like Pushover and ntfy and PagerDuty are reached through a notification library rather than through bespoke code. The distinction between the two categories is worth keeping straight. A monitoring integration answers the question of whether the backup ran, and the configuration's healthchecks ping URL is the simplest version of that, a URL that gets pinged on success so an external service notices silence. The rest of the list covers richer cases: a service that tracks job history, a log aggregator, an error reporting service, a status page, a dead man's switch. The reason this category is the longest in a backup tool is that the most common real failure is not a corrupt archive, it is a backup that stopped running three months ago because a timer was disabled on a host that was rebuilt. A ping URL is the cheapest possible detector of that, and the example configuration shows one. Two cautions. The ping URL in the example is a real token belonging to somebody's project, so replace it rather than shipping it, and a ping that fires on success tells you nothing about the contents, which is what the checks are for. Notification configuration is also where a config-driven tool tends to grow quietly, so it is worth reading the notification section of the documentation rather than copying the example and assuming the rest defaults sensibly.

Five dependencies, and a coverage floor of one hundred percent

The manifest is short enough to be evidence in itself. The runtime dependencies are a JSON schema validator, a version comparison library, a process and system introspection library, an HTTP client, and a YAML library pinned to a minimum version. Five, for a tool that drives a backup engine, and each one is explainable. The schema validator is why there is a command whose only job is to validate a configuration, and that command is one of three console entry points alongside the main program and a configuration generator, so the intended workflow is generate a starter file, edit it, validate it, and only then run anything. The YAML library is the one worth understanding, because it is chosen for round-tripping rather than for parsing: the configuration is meant to be hand-maintained, with comments and ordering intact, and a parser that discards both would rewrite the operator's file into something they stop trusting. The same philosophy shows up in the formatter settings, where the quote style is set to be preserved rather than normalised, so a run of the formatter does not churn lines the operator wrote. The test configuration is the other thing to look at. It enables coverage reporting over the package, sets a hundred percent coverage floor, and suppresses the coverage report when tests fail so a failure is not buried under a table. The end-to-end tests are excluded from the default run, which is the correct call since they need Borg present and a real repository, and each test carries a two minute timeout. A hundred percent floor is a strong signal about how the project is maintained, with the honest caveat that it measures lines the tests execute, not the behaviour of a backup against a disk that is failing.

An archive browser in the optional extras, and a mirror rather than a source

Two things in the repository are not in the README at all and both are worth knowing. The first is an optional extra for browsing, which pulls in a terminal user interface toolkit and a binary file detector, with a separate development extra for the same. So the tool can list and inspect archives interactively, which is the answer to the most common complaint about command line backup tools, and it is not installed by default because it is not essential. The second is the sample directory, which holds sample units for both cron and the init system, so the question of how this runs on a schedule is answered by files you can read rather than by a paragraph. The credential integration with the init system, the systemd units in the samples and the option of sourcing credentials from it fit together, which makes the init system the better-supported scheduling story for a machine you administer. On the administrative side, the repository is described as a mirror, the canonical home is stated to be the project's own documentation site, and the tree contains a directory for a different forge's pipelines, a security policy, a Python security linter configuration, a static site generator configuration for the documentation, an agent instruction file, and separate pinned requirement files for the backup binary and for the test tooling. Pinning the Borg binary as a managed requirement is the decision there that has the most effect on your upgrades, since it ties the engine version to the wrapper version. The licence in the manifest is GPL with the or-later suffix, which is a slightly wider grant than the plain label suggests and worth reading before you redistribute anything. Versions move on a patch line, with three recent releases in the last six weeks, and the manifest on the main branch already carries the next development version.

Editorial conclusion

borgmatic is worth adopting when one machine holds several databases and a growing set of retention rules, because the configuration file is the whole interface and there is nothing else to learn, and the retention keys plus labelled repositories are a clearer model than a pile of command line flags in a cron entry. Do not adopt it as a replacement for understanding Borg, since the tool is described as powered by Borg Backup and its own value is the configuration, the hooks and the checks, so you still need to know what a repository and an archive are. Two things to verify in the documentation before you trust a schedule. What happens when a dump command in a before hook fails, because the README does not say whether the backup proceeds, and what happens when an archive check fails, since the repository check and the archive check are configured separately for good reason and the failure path is the part that matters. Then pick a passphrase source from the credential integrations rather than a file, and replace the example repository address and ping URL in the sample configuration, since those are published values.

Frequently asked questions

Is borgmatic a replacement for Borg Backup?

No. The README describes it as powered by Borg Backup, so the archives, the repositories and the deduplication are Borg's, and borgmatic is the configuration layer that describes the policy and invokes the engine. Understanding Borg's repository and archive model is still worth the hour it takes.

What is the difference between the repository check and the archives check?

The repository check validates the repository itself, and the archives check verifies the archives, which is more expensive as a repository grows. The example configuration gives the archives check its own frequency of two weeks, so the cheap check can run often while the expensive one runs on a schedule. The configuration does not say what happens when a check fails.

How do I get a consistent backup of a live database?

The configuration has a list of databases to dump, shown for PostgreSQL, and a commands list for your own scripts keyed by an action and by when in the lifecycle it runs, with the example running a preparation script before a create action. The README does not say what happens if such a script fails, so check the documentation before relying on it.

Where can the repository passphrase come from?

The credential integrations list systemd, Docker, Podman, KeePassXC and 1Password. An init system or container secret avoids a passphrase sitting in a file or in a shell history, and a password manager means the backup depends on that password manager being available when the job runs.

How do I know whether the backup actually ran?

The example configuration includes a healthchecks entry with a ping URL that is contacted on a successful run, so an external service notices the silence. The monitoring integrations go further and include a job history service, a log aggregator, an error reporting service and a dead man's switch. The ping in the example is a real token and has to be replaced.

Official sources

  1. borgmatic-collective/borgmatic on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/borgmatic-collective-borgmatic.svg)](https://hysenlabs.com/projects/borgmatic-collective-borgmatic)