mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-29 04:43:02 +02:00
Compare commits
75
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bddc82951e | ||
|
|
3d15c51039 | ||
|
|
f9206c15e7 | ||
|
|
0da4b64838 | ||
|
|
85f06a900a | ||
|
|
bae8f983e0 | ||
|
|
89c0cd0af6 | ||
|
|
292d9cb2c7 | ||
|
|
181030c5d1 | ||
|
|
2600598359 | ||
|
|
9e649af2a3 | ||
|
|
370bc7e7f0 | ||
|
|
7cca1358fb | ||
|
|
67406b60b3 | ||
|
|
72fde728a4 | ||
|
|
d4433eeec1 | ||
|
|
d0b5244888 | ||
|
|
1ed207c6b1 | ||
|
|
5ef257ebb6 | ||
|
|
eb2b0b35c1 | ||
|
|
0f7ebcb4b7 | ||
|
|
51d2ce7963 | ||
|
|
0c4222037d | ||
|
|
25e8aad517 | ||
|
|
11ffd76cc6 | ||
|
|
68864395b5 | ||
|
|
0777d40218 | ||
|
|
f680869d31 | ||
|
|
9b66004819 | ||
|
|
aa43906b28 | ||
|
|
134bf1ebdd | ||
|
|
dcd567f4f0 | ||
|
|
44d63cac07 | ||
|
|
4017897b1a | ||
|
|
d808d7424f | ||
|
|
a327496c99 | ||
|
|
46bb49f40b | ||
|
|
9cd607af9d | ||
|
|
b80d126424 | ||
|
|
dea1c0179b | ||
|
|
2deb27c985 | ||
|
|
6f9bf687d9 | ||
|
|
7e7b6e012c | ||
|
|
3ee1203435 | ||
|
|
2db5525f72 | ||
|
|
e3c369a579 | ||
|
|
9787c9fda0 | ||
|
|
c7053f8202 | ||
|
|
044b1722e3 | ||
|
|
b5f0dc7f1f | ||
|
|
efc4316a45 | ||
|
|
5e6ceea7e2 | ||
|
|
a1acf1e0dd | ||
|
|
8ea9a7cb55 | ||
|
|
f45258b6b7 | ||
|
|
85177b78a6 | ||
|
|
6d7dc378a9 | ||
|
|
2a69598e02 | ||
|
|
4c7435378c | ||
|
|
d15bed5d4f | ||
|
|
2b34c62e6f | ||
|
|
2a5e202daf | ||
|
|
7f2e3e0054 | ||
|
|
173ba00caa | ||
|
|
498d662b3d | ||
|
|
75405bed89 | ||
|
|
c989b73103 | ||
|
|
35feeb4615 | ||
|
|
5dca281439 | ||
|
|
13fbee858a | ||
|
|
e2cdcba4e7 | ||
|
|
595c639d25 | ||
|
|
b89f057be0 | ||
|
|
98ddb3e535 | ||
|
|
339186b579 |
+2
-2
@@ -21,7 +21,7 @@ module.exports = function(eleventyConfig) {
|
|||||||
if (process.env.NODE_ENV == "production") {
|
if (process.env.NODE_ENV == "production") {
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
return link.replace('https://torsion.org/borgmatic/', 'http://localhost:8080/');
|
return link.replace('https://torsion.org/', 'http://localhost:8080/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let markdownItAnchorOptions = {
|
let markdownItAnchorOptions = {
|
||||||
@@ -44,7 +44,7 @@ module.exports = function(eleventyConfig) {
|
|||||||
templateFormats: [
|
templateFormats: [
|
||||||
"md",
|
"md",
|
||||||
"txt"
|
"txt"
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,53 @@
|
|||||||
2.0.9.dev0
|
2.0.11
|
||||||
|
* #957: Document borgmatic's limitations around parallelism—both its own and Borg's. See the
|
||||||
|
documentation for more information:
|
||||||
|
https://torsion.org/borgmatic/how-to/make-per-application-backups/#limitations
|
||||||
|
* #1165: Fix for when the systemd service directories (RuntimeDirectory and StateDirectory) each
|
||||||
|
contain multiple paths.
|
||||||
|
* #1168: Fix for the "list", "info", and "delete" options in "extra_borg_options" being ignored
|
||||||
|
when "--archive" is omitted with Borg 1.x.
|
||||||
|
* #1169: Fix for a regression in the ZFS, LVM, and Btrfs hooks in which partial excludes of
|
||||||
|
snapshot paths were ignored.
|
||||||
|
* #1170: Fix for an inconsistent log level for Borg's last output line before exiting.
|
||||||
|
* #1172: Add an "environment" option to the Sentry monitoring hook.
|
||||||
|
* #1176: Fix the "--repository" flag not applying to command hooks.
|
||||||
|
* Add a "rename" option to "extra_borg_options" to support passing arbitrary flags to "borg
|
||||||
|
rename".
|
||||||
|
* Add documentation on patterns and excludes:
|
||||||
|
https://torsion.org/borgmatic/reference/configuration/patterns-and-excludes/
|
||||||
|
* Drop support for Python 3.9, which has been end-of-lifed.
|
||||||
|
|
||||||
|
2.0.10
|
||||||
|
* #427: Expand the "extra_borg_options" option to support passing arbitrary Borg flags to every
|
||||||
|
Borg sub-command that borgmatic uses. As part of this, deprecate the "init" option under
|
||||||
|
"borg_extra_options" in favor of "repo_create".
|
||||||
|
* #942: Factor reference material out of the documentation how-to guides. This means there's now a
|
||||||
|
whole reference section in the docs! Check it out: https://torsion.org/borgmatic/
|
||||||
|
* #973: For the MariaDB and MySQL database hooks, add a "skip_names" option to ignore particular
|
||||||
|
databases when dumping "all".
|
||||||
|
* #1150: Fix for a runtime directory error when the "create" action is used with the "--log-json"
|
||||||
|
flag.
|
||||||
|
* #1150: Fix for a runtime directory error when the configured patterns contain a global exclude.
|
||||||
|
* #1161: Fix a traceback (TypeError) in the "check" action with Python 3.14.
|
||||||
|
* #1166: Add a "borg_key_file" option for setting the Borg repository key file path.
|
||||||
|
* Add documentation search.
|
||||||
|
* Change the URL of the local documentation development server to be more like the production URL.
|
||||||
|
|
||||||
|
2.0.9
|
||||||
|
* #1105: More accurately collect Btrfs subvolumes to snapshot. As part of this, the Btrfs hook no
|
||||||
|
longer uses "findmnt" and the "findmnt_command" option is deprecated.
|
||||||
* #1123: Add loading of systemd credentials even when running borgmatic outside of a systemd
|
* #1123: Add loading of systemd credentials even when running borgmatic outside of a systemd
|
||||||
service.
|
service.
|
||||||
* #1149: Add support for Python 3.14.
|
* #1149: Add support for Python 3.14.
|
||||||
* #1149: Include automated tests in the source dist tarball uploaded to PyPI.
|
* #1149: Include automated tests in the source dist tarball uploaded to PyPI.
|
||||||
* #1151: Fix snapshotting in the ZFS, Btrfs, and LVM hooks to play nicely with the Borg 1.4+
|
* #1151: Fix snapshotting in the ZFS, Btrfs, and LVM hooks to play nicely with the Borg 1.4+
|
||||||
"slashdot" hack within source directory paths.
|
"slashdot" hack within source directory paths.
|
||||||
|
* #1152: Fix a regression in the Loki monitoring hook in which log messages weren't sending.
|
||||||
|
* #1156: Fix snapshotting in the ZFS, Btrfs, and LVM hooks to snapshot both parent and child
|
||||||
|
volumes/filesystems instead of just the parent. As part of this fix, borgmatic no longer
|
||||||
|
deduplicates patterns except for those containing the borgmatic runtime directory.
|
||||||
|
* Fix a traceback (TypeError) regression in the "spot" check when the "local_path" option isn't
|
||||||
|
set.
|
||||||
|
|
||||||
2.0.8
|
2.0.8
|
||||||
* #1114: Document systemd configuration changes for the ZFS filesystem hook.
|
* #1114: Document systemd configuration changes for the ZFS filesystem hook.
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
title: borgmatic
|
title: borgmatic
|
||||||
permalink: index.html
|
permalink: index.html
|
||||||
---
|
---
|
||||||
|
<img src="https://torsion.org/borgmatic/static/borgmatic.png" alt="borgmatic logo" width="150px" style="float: right; padding-left: 1em;">
|
||||||
## It's your data. Keep it that way.
|
|
||||||
|
|
||||||
<img src="docs/static/borgmatic.png" alt="borgmatic logo" width="150px" style="float: right; padding-left: 1em;">
|
|
||||||
|
|
||||||
borgmatic is simple, configuration-driven backup software for servers and
|
borgmatic is simple, configuration-driven backup software for servers and
|
||||||
workstations. Protect your files with client-side encryption. Backup your
|
workstations. Protect your files with client-side encryption. Backup your
|
||||||
@@ -60,45 +57,45 @@ borgmatic is powered by [Borg Backup](https://www.borgbackup.org/).
|
|||||||
|
|
||||||
### Data
|
### Data
|
||||||
|
|
||||||
<a href="https://www.postgresql.org/"><img src="docs/static/postgresql.png" alt="PostgreSQL" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.postgresql.org/"><img src="https://torsion.org/borgmatic/static/postgresql.png" alt="PostgreSQL" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.mysql.com/"><img src="docs/static/mysql.png" alt="MySQL" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.mysql.com/"><img src="https://torsion.org/borgmatic/static/mysql.png" alt="MySQL" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://mariadb.com/"><img src="docs/static/mariadb.png" alt="MariaDB" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://mariadb.com/"><img src="https://torsion.org/borgmatic/static/mariadb.png" alt="MariaDB" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.mongodb.com/"><img src="docs/static/mongodb.png" alt="MongoDB" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.mongodb.com/"><img src="https://torsion.org/borgmatic/static/mongodb.png" alt="MongoDB" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://sqlite.org/"><img src="docs/static/sqlite.png" alt="SQLite" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://sqlite.org/"><img src="https://torsion.org/borgmatic/static/sqlite.png" alt="SQLite" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://openzfs.org/"><img src="docs/static/openzfs.png" alt="OpenZFS" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://openzfs.org/"><img src="https://torsion.org/borgmatic/static/openzfs.png" alt="OpenZFS" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://btrfs.readthedocs.io/"><img src="docs/static/btrfs.png" alt="Btrfs" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://btrfs.readthedocs.io/"><img src="https://torsion.org/borgmatic/static/btrfs.png" alt="Btrfs" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://sourceware.org/lvm2/"><img src="docs/static/lvm.png" alt="LVM" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://sourceware.org/lvm2/"><img src="https://torsion.org/borgmatic/static/lvm.png" alt="LVM" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://rclone.org"><img src="docs/static/rclone.png" alt="rclone" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://rclone.org"><img src="https://torsion.org/borgmatic/static/rclone.png" alt="rclone" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.borgbase.com/?utm_source=borgmatic"><img src="docs/static/borgbase.png" alt="BorgBase" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.borgbase.com/?utm_source=borgmatic"><img src="https://torsion.org/borgmatic/static/borgbase.png" alt="BorgBase" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
|
|
||||||
|
|
||||||
### Monitoring
|
### Monitoring
|
||||||
|
|
||||||
<a href="https://healthchecks.io/"><img src="docs/static/healthchecks.png" alt="Healthchecks" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://healthchecks.io/"><img src="https://torsion.org/borgmatic/static/healthchecks.png" alt="Healthchecks" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://uptime.kuma.pet/"><img src="docs/static/uptimekuma.png" alt="Uptime Kuma" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://uptime.kuma.pet/"><img src="https://torsion.org/borgmatic/static/uptimekuma.png" alt="Uptime Kuma" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://cronitor.io/"><img src="docs/static/cronitor.png" alt="Cronitor" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://cronitor.io/"><img src="https://torsion.org/borgmatic/static/cronitor.png" alt="Cronitor" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://cronhub.io/"><img src="docs/static/cronhub.png" alt="Cronhub" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://cronhub.io/"><img src="https://torsion.org/borgmatic/static/cronhub.png" alt="Cronhub" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.pagerduty.com/"><img src="docs/static/pagerduty.png" alt="PagerDuty" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.pagerduty.com/"><img src="https://torsion.org/borgmatic/static/pagerduty.png" alt="PagerDuty" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.pushover.net/"><img src="docs/static/pushover.png" alt="Pushover" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.pushover.net/"><img src="https://torsion.org/borgmatic/static/pushover.png" alt="Pushover" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://ntfy.sh/"><img src="docs/static/ntfy.png" alt="ntfy" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://ntfy.sh/"><img src="https://torsion.org/borgmatic/static/ntfy.png" alt="ntfy" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://grafana.com/oss/loki/"><img src="docs/static/loki.png" alt="Loki" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://grafana.com/oss/loki/"><img src="https://torsion.org/borgmatic/static/loki.png" alt="Loki" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://github.com/caronc/apprise/wiki"><img src="docs/static/apprise.png" alt="Apprise" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://github.com/caronc/apprise/wiki"><img src="https://torsion.org/borgmatic/static/apprise.png" alt="Apprise" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.zabbix.com/"><img src="docs/static/zabbix.png" alt="Zabbix" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.zabbix.com/"><img src="https://torsion.org/borgmatic/static/zabbix.png" alt="Zabbix" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://sentry.io/"><img src="docs/static/sentry.png" alt="Sentry" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://sentry.io/"><img src="https://torsion.org/borgmatic/static/sentry.png" alt="Sentry" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
|
|
||||||
|
|
||||||
### Credentials
|
### Credentials
|
||||||
|
|
||||||
<a href="https://systemd.io/"><img src="docs/static/systemd.png" alt="Sentry" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://systemd.io/"><img src="https://torsion.org/borgmatic/static/systemd.png" alt="Sentry" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://www.docker.com/"><img src="docs/static/docker.png" alt="Docker" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://www.docker.com/"><img src="https://torsion.org/borgmatic/static/docker.png" alt="Docker" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://podman.io/"><img src="docs/static/podman.png" alt="Podman" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://podman.io/"><img src="https://torsion.org/borgmatic/static/podman.png" alt="Podman" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
<a href="https://keepassxc.org/"><img src="docs/static/keepassxc.png" alt="Podman" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
<a href="https://keepassxc.org/"><img src="https://torsion.org/borgmatic/static/keepassxc.png" alt="Podman" height="40px" style="margin-bottom:20px; margin-right:20px;"></a>
|
||||||
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Your first step is to [install and configure
|
Your first step is to [install and configure
|
||||||
borgmatic](https://torsion.org/borgmatic/docs/how-to/set-up-backups/).
|
borgmatic](https://torsion.org/borgmatic/how-to/set-up-backups/).
|
||||||
|
|
||||||
For additional documentation, check out the links above (left panel on wide screens)
|
For additional documentation, check out the links above (left panel on wide screens)
|
||||||
for <a href="https://torsion.org/borgmatic/#documentation">borgmatic how-to and
|
for <a href="https://torsion.org/borgmatic/#documentation">borgmatic how-to and
|
||||||
@@ -132,7 +129,7 @@ first. If you prefer to use an existing GitHub account, you can skip account
|
|||||||
creation and [login directly](https://projects.torsion.org/user/login).
|
creation and [login directly](https://projects.torsion.org/user/login).
|
||||||
|
|
||||||
Also see the [security
|
Also see the [security
|
||||||
policy](https://torsion.org/borgmatic/docs/security-policy/) for any security
|
policy](https://torsion.org/borgmatic/security-policy/) for any security
|
||||||
issues.
|
issues.
|
||||||
|
|
||||||
|
|
||||||
@@ -177,7 +174,7 @@ discuss your idea. Note that you'll need to
|
|||||||
first. In general, contributions are very welcome. We don't bite!
|
first. In general, contributions are very welcome. We don't bite!
|
||||||
|
|
||||||
Also, please check out the [borgmatic development
|
Also, please check out the [borgmatic development
|
||||||
how-to](https://torsion.org/borgmatic/docs/how-to/develop-on-borgmatic/) for
|
how-to](https://torsion.org/borgmatic/how-to/develop-on-borgmatic/) for
|
||||||
info on cloning source code, running tests, etc.
|
info on cloning source code, running tests, etc.
|
||||||
|
|
||||||
### Recent contributors
|
### Recent contributors
|
||||||
@@ -186,4 +183,4 @@ Thanks to all borgmatic contributors! There are multiple ways to contribute to
|
|||||||
this project, so the following includes those who have fixed bugs, contributed
|
this project, so the following includes those who have fixed bugs, contributed
|
||||||
features, *or* filed tickets.
|
features, *or* filed tickets.
|
||||||
|
|
||||||
{% include borgmatic/contributors.html %}
|
{% include borgmatic/contributors.html %}
|
||||||
+19
-24
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.borg.borg
|
import borgmatic.borg.borg
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -19,26 +18,22 @@ def run_borg(
|
|||||||
'''
|
'''
|
||||||
Run the "borg" action for the given repository.
|
Run the "borg" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if borg_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.info('Running arbitrary Borg command')
|
||||||
repository,
|
archive_name = borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
borg_arguments.repository,
|
repository['path'],
|
||||||
):
|
borg_arguments.archive,
|
||||||
logger.info('Running arbitrary Borg command')
|
config,
|
||||||
archive_name = borgmatic.borg.repo_list.resolve_archive_name(
|
local_borg_version,
|
||||||
repository['path'],
|
global_arguments,
|
||||||
borg_arguments.archive,
|
local_path,
|
||||||
config,
|
remote_path,
|
||||||
local_borg_version,
|
)
|
||||||
global_arguments,
|
borgmatic.borg.borg.run_arbitrary_borg(
|
||||||
local_path,
|
repository['path'],
|
||||||
remote_path,
|
config,
|
||||||
)
|
local_borg_version,
|
||||||
borgmatic.borg.borg.run_arbitrary_borg(
|
options=borg_arguments.options,
|
||||||
repository['path'],
|
archive=archive_name,
|
||||||
config,
|
local_path=local_path,
|
||||||
local_borg_version,
|
remote_path=remote_path,
|
||||||
options=borg_arguments.options,
|
)
|
||||||
archive=archive_name,
|
|
||||||
local_path=local_path,
|
|
||||||
remote_path=remote_path,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import borgmatic.borg.break_lock
|
import borgmatic.borg.break_lock
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,16 +17,12 @@ def run_break_lock(
|
|||||||
'''
|
'''
|
||||||
Run the "break-lock" action for the given repository.
|
Run the "break-lock" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if break_lock_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.info('Breaking repository and cache locks')
|
||||||
repository,
|
borgmatic.borg.break_lock.break_lock(
|
||||||
break_lock_arguments.repository,
|
repository['path'],
|
||||||
):
|
config,
|
||||||
logger.info('Breaking repository and cache locks')
|
local_borg_version,
|
||||||
borgmatic.borg.break_lock.break_lock(
|
global_arguments,
|
||||||
repository['path'],
|
local_path=local_path,
|
||||||
config,
|
remote_path=remote_path,
|
||||||
local_borg_version,
|
)
|
||||||
global_arguments,
|
|
||||||
local_path=local_path,
|
|
||||||
remote_path=remote_path,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import borgmatic.borg.change_passphrase
|
import borgmatic.borg.change_passphrase
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,20 +17,13 @@ def run_change_passphrase(
|
|||||||
'''
|
'''
|
||||||
Run the "key change-passphrase" action for the given repository.
|
Run the "key change-passphrase" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if (
|
logger.info('Changing repository passphrase')
|
||||||
change_passphrase_arguments.repository is None
|
borgmatic.borg.change_passphrase.change_passphrase(
|
||||||
or borgmatic.config.validate.repositories_match(
|
repository['path'],
|
||||||
repository,
|
config,
|
||||||
change_passphrase_arguments.repository,
|
local_borg_version,
|
||||||
)
|
change_passphrase_arguments,
|
||||||
):
|
global_arguments,
|
||||||
logger.info('Changing repository passphrase')
|
local_path=local_path,
|
||||||
borgmatic.borg.change_passphrase.change_passphrase(
|
remote_path=remote_path,
|
||||||
repository['path'],
|
)
|
||||||
config,
|
|
||||||
local_borg_version,
|
|
||||||
change_passphrase_arguments,
|
|
||||||
global_arguments,
|
|
||||||
local_path=local_path,
|
|
||||||
remote_path=remote_path,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import borgmatic.borg.pattern
|
|||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.borg.state
|
import borgmatic.borg.state
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.execute
|
import borgmatic.execute
|
||||||
import borgmatic.hooks.command
|
import borgmatic.hooks.command
|
||||||
|
|
||||||
@@ -246,7 +245,7 @@ def write_check_time(path): # pragma: no cover
|
|||||||
logger.debug(f'Writing check time at {path}')
|
logger.debug(f'Writing check time at {path}')
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(path), mode=0o700, exist_ok=True)
|
os.makedirs(os.path.dirname(path), mode=0o700, exist_ok=True)
|
||||||
pathlib.Path(path, mode=0o600).touch()
|
pathlib.Path(path).touch(mode=0o600)
|
||||||
|
|
||||||
|
|
||||||
def read_check_time(path):
|
def read_check_time(path):
|
||||||
@@ -750,12 +749,6 @@ def run_check(
|
|||||||
|
|
||||||
Raise ValueError if the Borg repository ID cannot be determined.
|
Raise ValueError if the Borg repository ID cannot be determined.
|
||||||
'''
|
'''
|
||||||
if check_arguments.repository and not borgmatic.config.validate.repositories_match(
|
|
||||||
repository,
|
|
||||||
check_arguments.repository,
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info('Running consistency checks')
|
logger.info('Running consistency checks')
|
||||||
|
|
||||||
repository_id = borgmatic.borg.check.get_repository_id(
|
repository_id = borgmatic.borg.check.get_repository_id(
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.borg.compact
|
import borgmatic.borg.compact
|
||||||
import borgmatic.borg.feature
|
import borgmatic.borg.feature
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.hooks.command
|
import borgmatic.hooks.command
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -22,12 +21,6 @@ def run_compact(
|
|||||||
'''
|
'''
|
||||||
Run the "compact" action for the given repository.
|
Run the "compact" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if compact_arguments.repository and not borgmatic.config.validate.repositories_match(
|
|
||||||
repository,
|
|
||||||
compact_arguments.repository,
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
if borgmatic.borg.feature.available(borgmatic.borg.feature.Feature.COMPACT, local_borg_version):
|
if borgmatic.borg.feature.available(borgmatic.borg.feature.Feature.COMPACT, local_borg_version):
|
||||||
logger.info(f'Compacting segments{dry_run_label}')
|
logger.info(f'Compacting segments{dry_run_label}')
|
||||||
borgmatic.borg.compact.compact_segments(
|
borgmatic.borg.compact.compact_segments(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import os
|
|||||||
import borgmatic.borg.extract
|
import borgmatic.borg.extract
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.hooks.command
|
import borgmatic.hooks.command
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -66,7 +65,7 @@ def load_config_paths_from_archive(
|
|||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path=config.get('local_path'),
|
local_path=config.get('local_path', 'borg'),
|
||||||
remote_path=config.get('remote_path'),
|
remote_path=config.get('remote_path'),
|
||||||
extract_to_stdout=True,
|
extract_to_stdout=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,26 +19,33 @@ def run_generate(generate_arguments, global_arguments):
|
|||||||
dry_run_label = ' (dry run; not actually writing anything)' if global_arguments.dry_run else ''
|
dry_run_label = ' (dry run; not actually writing anything)' if global_arguments.dry_run else ''
|
||||||
|
|
||||||
logger.answer(
|
logger.answer(
|
||||||
f'Generating a configuration file at: {generate_arguments.destination_filename}{dry_run_label}',
|
f'Generating configuration files within: {generate_arguments.destination_path}{dry_run_label}'
|
||||||
|
if generate_arguments.split
|
||||||
|
else f'Generating a configuration file at: {generate_arguments.destination_path}{dry_run_label}'
|
||||||
)
|
)
|
||||||
|
|
||||||
borgmatic.config.generate.generate_sample_configuration(
|
borgmatic.config.generate.generate_sample_configuration(
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
generate_arguments.source_filename,
|
generate_arguments.source_filename,
|
||||||
generate_arguments.destination_filename,
|
generate_arguments.destination_path,
|
||||||
borgmatic.config.validate.schema_filename(),
|
borgmatic.config.validate.schema_filename(),
|
||||||
overwrite=generate_arguments.overwrite,
|
overwrite=generate_arguments.overwrite,
|
||||||
|
split=generate_arguments.split,
|
||||||
)
|
)
|
||||||
|
|
||||||
if generate_arguments.source_filename:
|
if generate_arguments.source_filename:
|
||||||
logger.answer(
|
logger.answer(
|
||||||
f'''
|
f'''
|
||||||
Merged in the contents of configuration file at: {generate_arguments.source_filename}
|
Merged in the contents of configuration file at: {generate_arguments.source_filename}'''
|
||||||
To review the changes made, run:
|
|
||||||
|
|
||||||
diff --unified {generate_arguments.source_filename} {generate_arguments.destination_filename}''',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not generate_arguments.split:
|
||||||
|
logger.answer(
|
||||||
|
'''To review the changes made, run:
|
||||||
|
|
||||||
|
diff --unified {generate_arguments.source_filename} {generate_arguments.destination_path}''',
|
||||||
|
)
|
||||||
|
|
||||||
logger.answer(
|
logger.answer(
|
||||||
'''
|
'''
|
||||||
This includes all available configuration options with example values, the few
|
This includes all available configuration options with example values, the few
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import borgmatic.borg.feature
|
|||||||
import borgmatic.borg.rename
|
import borgmatic.borg.rename
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.hooks.dispatch
|
import borgmatic.hooks.dispatch
|
||||||
from borgmatic.actions import pattern
|
from borgmatic.actions import pattern
|
||||||
|
|
||||||
@@ -30,12 +29,6 @@ def run_create(
|
|||||||
|
|
||||||
If create_arguments.json is True, yield the JSON output from creating the archive.
|
If create_arguments.json is True, yield the JSON output from creating the archive.
|
||||||
'''
|
'''
|
||||||
if create_arguments.repository and not borgmatic.config.validate.repositories_match(
|
|
||||||
repository,
|
|
||||||
create_arguments.repository,
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
if config.get('list_details') and config.get('progress'):
|
if config.get('list_details') and config.get('progress'):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'With the create action, only one of --list/--files/list_details and --progress/progress can be used.',
|
'With the create action, only one of --list/--files/list_details and --progress/progress can be used.',
|
||||||
@@ -50,18 +43,20 @@ def run_create(
|
|||||||
working_directory = borgmatic.config.paths.get_working_directory(config)
|
working_directory = borgmatic.config.paths.get_working_directory(config)
|
||||||
|
|
||||||
with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory:
|
with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory:
|
||||||
|
patterns = pattern.process_patterns(
|
||||||
|
pattern.collect_patterns(config),
|
||||||
|
config,
|
||||||
|
working_directory,
|
||||||
|
borgmatic_runtime_directory,
|
||||||
|
)
|
||||||
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
|
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
|
||||||
'remove_data_source_dumps',
|
'remove_data_source_dumps',
|
||||||
config,
|
config,
|
||||||
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
)
|
)
|
||||||
patterns = pattern.process_patterns(
|
|
||||||
pattern.collect_patterns(config),
|
|
||||||
config,
|
|
||||||
working_directory,
|
|
||||||
)
|
|
||||||
active_dumps = borgmatic.hooks.dispatch.call_hooks(
|
active_dumps = borgmatic.hooks.dispatch.call_hooks(
|
||||||
'dump_data_sources',
|
'dump_data_sources',
|
||||||
config,
|
config,
|
||||||
@@ -79,6 +74,7 @@ def run_create(
|
|||||||
patterns,
|
patterns,
|
||||||
config,
|
config,
|
||||||
working_directory,
|
working_directory,
|
||||||
|
borgmatic_runtime_directory,
|
||||||
skip_expand_paths=config_paths,
|
skip_expand_paths=config_paths,
|
||||||
)
|
)
|
||||||
stream_processes = [process for processes in active_dumps.values() for process in processes]
|
stream_processes = [process for processes in active_dumps.values() for process in processes]
|
||||||
@@ -138,6 +134,7 @@ def run_create(
|
|||||||
config,
|
config,
|
||||||
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+18
-22
@@ -20,32 +20,28 @@ def run_delete(
|
|||||||
'''
|
'''
|
||||||
Run the "delete" action for the given repository and archive(s).
|
Run the "delete" action for the given repository and archive(s).
|
||||||
'''
|
'''
|
||||||
if delete_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.answer('Deleting archives')
|
||||||
repository,
|
|
||||||
delete_arguments.repository,
|
|
||||||
):
|
|
||||||
logger.answer('Deleting archives')
|
|
||||||
|
|
||||||
archive_name = (
|
archive_name = (
|
||||||
borgmatic.borg.repo_list.resolve_archive_name(
|
borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
delete_arguments.archive,
|
delete_arguments.archive,
|
||||||
config,
|
|
||||||
local_borg_version,
|
|
||||||
global_arguments,
|
|
||||||
local_path,
|
|
||||||
remote_path,
|
|
||||||
)
|
|
||||||
if delete_arguments.archive
|
|
||||||
else None
|
|
||||||
)
|
|
||||||
|
|
||||||
borgmatic.borg.delete.delete_archives(
|
|
||||||
repository,
|
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
borgmatic.actions.arguments.update_arguments(delete_arguments, archive=archive_name),
|
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
)
|
)
|
||||||
|
if delete_arguments.archive
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
borgmatic.borg.delete.delete_archives(
|
||||||
|
repository,
|
||||||
|
config,
|
||||||
|
local_borg_version,
|
||||||
|
borgmatic.actions.arguments.update_arguments(delete_arguments, archive=archive_name),
|
||||||
|
global_arguments,
|
||||||
|
local_path,
|
||||||
|
remote_path,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import borgmatic.borg.export_key
|
import borgmatic.borg.export_key
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,17 +17,13 @@ def run_export_key(
|
|||||||
'''
|
'''
|
||||||
Run the "key export" action for the given repository.
|
Run the "key export" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if export_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.info('Exporting repository key')
|
||||||
repository,
|
borgmatic.borg.export_key.export_key(
|
||||||
export_arguments.repository,
|
repository['path'],
|
||||||
):
|
config,
|
||||||
logger.info('Exporting repository key')
|
local_borg_version,
|
||||||
borgmatic.borg.export_key.export_key(
|
export_arguments,
|
||||||
repository['path'],
|
global_arguments,
|
||||||
config,
|
local_path=local_path,
|
||||||
local_borg_version,
|
remote_path=remote_path,
|
||||||
export_arguments,
|
)
|
||||||
global_arguments,
|
|
||||||
local_path=local_path,
|
|
||||||
remote_path=remote_path,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.borg.export_tar
|
import borgmatic.borg.export_tar
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -19,30 +18,26 @@ def run_export_tar(
|
|||||||
'''
|
'''
|
||||||
Run the "export-tar" action for the given repository.
|
Run the "export-tar" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if export_tar_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.info(f'Exporting archive {export_tar_arguments.archive} as tar file')
|
||||||
repository,
|
borgmatic.borg.export_tar.export_tar_archive(
|
||||||
export_tar_arguments.repository,
|
global_arguments.dry_run,
|
||||||
):
|
repository['path'],
|
||||||
logger.info(f'Exporting archive {export_tar_arguments.archive} as tar file')
|
borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
borgmatic.borg.export_tar.export_tar_archive(
|
|
||||||
global_arguments.dry_run,
|
|
||||||
repository['path'],
|
repository['path'],
|
||||||
borgmatic.borg.repo_list.resolve_archive_name(
|
export_tar_arguments.archive,
|
||||||
repository['path'],
|
|
||||||
export_tar_arguments.archive,
|
|
||||||
config,
|
|
||||||
local_borg_version,
|
|
||||||
global_arguments,
|
|
||||||
local_path,
|
|
||||||
remote_path,
|
|
||||||
),
|
|
||||||
export_tar_arguments.paths,
|
|
||||||
export_tar_arguments.destination,
|
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path=local_path,
|
local_path,
|
||||||
remote_path=remote_path,
|
remote_path,
|
||||||
tar_filter=export_tar_arguments.tar_filter,
|
),
|
||||||
strip_components=export_tar_arguments.strip_components,
|
export_tar_arguments.paths,
|
||||||
)
|
export_tar_arguments.destination,
|
||||||
|
config,
|
||||||
|
local_borg_version,
|
||||||
|
global_arguments,
|
||||||
|
local_path=local_path,
|
||||||
|
remote_path=remote_path,
|
||||||
|
tar_filter=export_tar_arguments.tar_filter,
|
||||||
|
strip_components=export_tar_arguments.strip_components,
|
||||||
|
)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.borg.extract
|
import borgmatic.borg.extract
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.hooks.command
|
import borgmatic.hooks.command
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -21,29 +20,25 @@ def run_extract(
|
|||||||
'''
|
'''
|
||||||
Run the "extract" action for the given repository.
|
Run the "extract" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if extract_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.info(f'Extracting archive {extract_arguments.archive}')
|
||||||
repository,
|
borgmatic.borg.extract.extract_archive(
|
||||||
extract_arguments.repository,
|
global_arguments.dry_run,
|
||||||
):
|
repository['path'],
|
||||||
logger.info(f'Extracting archive {extract_arguments.archive}')
|
borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
borgmatic.borg.extract.extract_archive(
|
|
||||||
global_arguments.dry_run,
|
|
||||||
repository['path'],
|
repository['path'],
|
||||||
borgmatic.borg.repo_list.resolve_archive_name(
|
extract_arguments.archive,
|
||||||
repository['path'],
|
|
||||||
extract_arguments.archive,
|
|
||||||
config,
|
|
||||||
local_borg_version,
|
|
||||||
global_arguments,
|
|
||||||
local_path,
|
|
||||||
remote_path,
|
|
||||||
),
|
|
||||||
extract_arguments.paths,
|
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path=local_path,
|
local_path,
|
||||||
remote_path=remote_path,
|
remote_path,
|
||||||
destination_path=extract_arguments.destination,
|
),
|
||||||
strip_components=extract_arguments.strip_components,
|
extract_arguments.paths,
|
||||||
)
|
config,
|
||||||
|
local_borg_version,
|
||||||
|
global_arguments,
|
||||||
|
local_path=local_path,
|
||||||
|
remote_path=remote_path,
|
||||||
|
destination_path=extract_arguments.destination,
|
||||||
|
strip_components=extract_arguments.strip_components,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import borgmatic.borg.import_key
|
import borgmatic.borg.import_key
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,17 +17,13 @@ def run_import_key(
|
|||||||
'''
|
'''
|
||||||
Run the "key import" action for the given repository.
|
Run the "key import" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if import_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.info('Importing repository key')
|
||||||
repository,
|
borgmatic.borg.import_key.import_key(
|
||||||
import_arguments.repository,
|
repository['path'],
|
||||||
):
|
config,
|
||||||
logger.info('Importing repository key')
|
local_borg_version,
|
||||||
borgmatic.borg.import_key.import_key(
|
import_arguments,
|
||||||
repository['path'],
|
global_arguments,
|
||||||
config,
|
local_path=local_path,
|
||||||
local_borg_version,
|
remote_path=remote_path,
|
||||||
import_arguments,
|
)
|
||||||
global_arguments,
|
|
||||||
local_path=local_path,
|
|
||||||
remote_path=remote_path,
|
|
||||||
)
|
|
||||||
|
|||||||
+22
-27
@@ -4,7 +4,6 @@ import borgmatic.actions.arguments
|
|||||||
import borgmatic.actions.json
|
import borgmatic.actions.json
|
||||||
import borgmatic.borg.info
|
import borgmatic.borg.info
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -23,30 +22,26 @@ def run_info(
|
|||||||
|
|
||||||
If info_arguments.json is True, yield the JSON output from the info for the archive.
|
If info_arguments.json is True, yield the JSON output from the info for the archive.
|
||||||
'''
|
'''
|
||||||
if info_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
if not info_arguments.json:
|
||||||
repository,
|
logger.answer('Displaying archive summary information')
|
||||||
info_arguments.repository,
|
|
||||||
):
|
|
||||||
if not info_arguments.json:
|
|
||||||
logger.answer('Displaying archive summary information')
|
|
||||||
|
|
||||||
archive_name = borgmatic.borg.repo_list.resolve_archive_name(
|
archive_name = borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
info_arguments.archive,
|
info_arguments.archive,
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
)
|
)
|
||||||
json_output = borgmatic.borg.info.display_archives_info(
|
json_output = borgmatic.borg.info.display_archives_info(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
borgmatic.actions.arguments.update_arguments(info_arguments, archive=archive_name),
|
borgmatic.actions.arguments.update_arguments(info_arguments, archive=archive_name),
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
)
|
)
|
||||||
if json_output:
|
if json_output:
|
||||||
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
||||||
|
|||||||
+25
-30
@@ -3,7 +3,6 @@ import logging
|
|||||||
import borgmatic.actions.arguments
|
import borgmatic.actions.arguments
|
||||||
import borgmatic.actions.json
|
import borgmatic.actions.json
|
||||||
import borgmatic.borg.list
|
import borgmatic.borg.list
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -22,33 +21,29 @@ def run_list(
|
|||||||
|
|
||||||
If list_arguments.json is True, yield the JSON output from listing the archive.
|
If list_arguments.json is True, yield the JSON output from listing the archive.
|
||||||
'''
|
'''
|
||||||
if list_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
if not list_arguments.json:
|
||||||
repository,
|
if list_arguments.find_paths: # pragma: no cover
|
||||||
list_arguments.repository,
|
logger.answer('Searching archives')
|
||||||
):
|
elif not list_arguments.archive: # pragma: no cover
|
||||||
if not list_arguments.json:
|
logger.answer('Listing archives')
|
||||||
if list_arguments.find_paths: # pragma: no cover
|
|
||||||
logger.answer('Searching archives')
|
|
||||||
elif not list_arguments.archive: # pragma: no cover
|
|
||||||
logger.answer('Listing archives')
|
|
||||||
|
|
||||||
archive_name = borgmatic.borg.repo_list.resolve_archive_name(
|
archive_name = borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
list_arguments.archive,
|
list_arguments.archive,
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
)
|
)
|
||||||
json_output = borgmatic.borg.list.list_archive(
|
json_output = borgmatic.borg.list.list_archive(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
borgmatic.actions.arguments.update_arguments(list_arguments, archive=archive_name),
|
borgmatic.actions.arguments.update_arguments(list_arguments, archive=archive_name),
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
)
|
)
|
||||||
if json_output:
|
if json_output:
|
||||||
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
||||||
|
|||||||
+18
-23
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.borg.mount
|
import borgmatic.borg.mount
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -19,30 +18,26 @@ def run_mount(
|
|||||||
'''
|
'''
|
||||||
Run the "mount" action for the given repository.
|
Run the "mount" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if mount_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
if mount_arguments.archive:
|
||||||
repository,
|
logger.info(f'Mounting archive {mount_arguments.archive}')
|
||||||
mount_arguments.repository,
|
else: # pragma: nocover
|
||||||
):
|
logger.info('Mounting repository')
|
||||||
if mount_arguments.archive:
|
|
||||||
logger.info(f'Mounting archive {mount_arguments.archive}')
|
|
||||||
else: # pragma: nocover
|
|
||||||
logger.info('Mounting repository')
|
|
||||||
|
|
||||||
borgmatic.borg.mount.mount_archive(
|
borgmatic.borg.mount.mount_archive(
|
||||||
|
repository['path'],
|
||||||
|
borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
borgmatic.borg.repo_list.resolve_archive_name(
|
mount_arguments.archive,
|
||||||
repository['path'],
|
|
||||||
mount_arguments.archive,
|
|
||||||
config,
|
|
||||||
local_borg_version,
|
|
||||||
global_arguments,
|
|
||||||
local_path,
|
|
||||||
remote_path,
|
|
||||||
),
|
|
||||||
mount_arguments,
|
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path=local_path,
|
local_path,
|
||||||
remote_path=remote_path,
|
remote_path,
|
||||||
)
|
),
|
||||||
|
mount_arguments,
|
||||||
|
config,
|
||||||
|
local_borg_version,
|
||||||
|
global_arguments,
|
||||||
|
local_path=local_path,
|
||||||
|
remote_path=remote_path,
|
||||||
|
)
|
||||||
|
|||||||
@@ -227,23 +227,37 @@ def device_map_patterns(patterns, working_directory=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def deduplicate_patterns(patterns, config):
|
def deduplicate_runtime_directory_patterns(patterns, config, borgmatic_runtime_directory=None):
|
||||||
'''
|
'''
|
||||||
Given a sequence of borgmatic.borg.pattern.Pattern instances and a configuration dict, return
|
Given a sequence of borgmatic.borg.pattern.Pattern instances, the borgmatic runtime directory,
|
||||||
them with all duplicate root child patterns removed. For instance, if two root patterns are
|
and a configuration dict, return them without any duplicate root child patterns that contain the
|
||||||
given with paths "/foo" and "/foo/bar", return just the one with "/foo". Non-root patterns are
|
runtime directory. For instance, if two root patterns are given with paths "/foo" and
|
||||||
passed through without modification.
|
"/foo/bar", and the runtime directory is "/foo/bar", return just the "/foo" pattern. Non-root
|
||||||
|
patterns and patterns not containing the runtime directory are passed through without
|
||||||
|
modification.
|
||||||
|
|
||||||
The one exception to deduplication is if two paths are on different filesystems (devices) and
|
One exception to deduplication is if two paths are on different filesystems (devices) and
|
||||||
"one_file_system" is True in the given configuration. In that case, the paths won't get
|
"one_file_system" is True in the given configuration. In that case, the paths won't get
|
||||||
deduplicated, because Borg won't cross filesystem boundaries when "one_file_system" is True.
|
deduplicated, because Borg won't cross filesystem boundaries when "one_file_system" is True.
|
||||||
|
|
||||||
The idea is that if Borg is given a root parent pattern, then it doesn't also need to be given
|
The idea is that if Borg is given a root parent pattern containing the borgmatic runtime
|
||||||
child patterns, because it will naturally spider the contents of the parent pattern's path. And
|
directory, then Borg doesn't also need to be given child patterns, because it will naturally
|
||||||
there are cases where Borg coming across the same file twice will result in duplicate reads and
|
spider the contents of the parent pattern's path. Additionally, there are cases where Borg
|
||||||
even hangs, e.g. when a database hook is using a named pipe for streaming database dumps to
|
coming across the same file twice will result in duplicate reads and even hangs, e.g. when a
|
||||||
Borg.
|
database hook in the borgmatic runtime directory is using a named pipe for streaming database
|
||||||
|
dumps to Borg.
|
||||||
|
|
||||||
|
This deduplication is limited to the borgmatic runtime directory (where borgmatic's named pipes
|
||||||
|
exist), because there are other legitimate use cases for parent and child patterns to both exist
|
||||||
|
in patterns. For instance, with some snapshotted filesystems, snapshots don't traverse from a
|
||||||
|
parent filesystem to a child and therefore both need to remain in patterns.
|
||||||
|
|
||||||
|
And for the case of named pipes outside of the borgmatic runtime directory, there is code
|
||||||
|
elsewhere (in the "create" action) that auto-excludes special files to prevent Borg hangs.
|
||||||
'''
|
'''
|
||||||
|
if borgmatic_runtime_directory is None:
|
||||||
|
return patterns
|
||||||
|
|
||||||
deduplicated = {} # Use just the keys as an ordered set.
|
deduplicated = {} # Use just the keys as an ordered set.
|
||||||
|
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
@@ -262,6 +276,8 @@ def deduplicate_patterns(patterns, config):
|
|||||||
|
|
||||||
if any(
|
if any(
|
||||||
pathlib.PurePath(other_pattern.path) == parent
|
pathlib.PurePath(other_pattern.path) == parent
|
||||||
|
and pathlib.PurePosixPath(other_pattern.path)
|
||||||
|
in pathlib.PurePath(borgmatic_runtime_directory).parents
|
||||||
and pattern.device is not None
|
and pattern.device is not None
|
||||||
and (
|
and (
|
||||||
other_pattern.device == pattern.device
|
other_pattern.device == pattern.device
|
||||||
@@ -276,16 +292,22 @@ def deduplicate_patterns(patterns, config):
|
|||||||
return tuple(deduplicated.keys())
|
return tuple(deduplicated.keys())
|
||||||
|
|
||||||
|
|
||||||
def process_patterns(patterns, config, working_directory, skip_expand_paths=None):
|
def process_patterns(
|
||||||
|
patterns, config, working_directory, borgmatic_runtime_directory=None, skip_expand_paths=None
|
||||||
|
):
|
||||||
'''
|
'''
|
||||||
Given a sequence of Borg patterns, a configuration dict, a configured working directory, and a
|
Given a sequence of Borg patterns, a configuration dict, a configured working directory, the
|
||||||
sequence of paths to skip path expansion for, expand and deduplicate any "root" patterns,
|
borgmatic runtime directory, and a sequence of paths to skip path expansion for, expand and
|
||||||
returning the resulting root and non-root patterns as a list.
|
deduplicate any "root" patterns, returning the resulting root and non-root patterns as a list.
|
||||||
|
|
||||||
|
If the borgmatic runtime directory is None, then don't deduplicate patterns. Deduplication is
|
||||||
|
really only necessary for the "create" action when the runtime directory might contain named
|
||||||
|
pipes for database dumps.
|
||||||
'''
|
'''
|
||||||
skip_paths = set(skip_expand_paths or ())
|
skip_paths = set(skip_expand_paths or ())
|
||||||
|
|
||||||
return list(
|
return list(
|
||||||
deduplicate_patterns(
|
deduplicate_runtime_directory_patterns(
|
||||||
device_map_patterns(
|
device_map_patterns(
|
||||||
expand_patterns(
|
expand_patterns(
|
||||||
patterns,
|
patterns,
|
||||||
@@ -294,5 +316,6 @@ def process_patterns(patterns, config, working_directory, skip_expand_paths=None
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
config,
|
config,
|
||||||
|
borgmatic_runtime_directory,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import borgmatic.borg.prune
|
import borgmatic.borg.prune
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.hooks.command
|
import borgmatic.hooks.command
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -21,12 +20,6 @@ def run_prune(
|
|||||||
'''
|
'''
|
||||||
Run the "prune" action for the given repository.
|
Run the "prune" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if prune_arguments.repository and not borgmatic.config.validate.repositories_match(
|
|
||||||
repository,
|
|
||||||
prune_arguments.repository,
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info(f'Pruning archives{dry_run_label}')
|
logger.info(f'Pruning archives{dry_run_label}')
|
||||||
borgmatic.borg.prune.prune_archives(
|
borgmatic.borg.prune.prune_archives(
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import subprocess
|
|||||||
import borgmatic.borg.info
|
import borgmatic.borg.info
|
||||||
import borgmatic.borg.recreate
|
import borgmatic.borg.recreate
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
from borgmatic.actions.pattern import collect_patterns, process_patterns
|
from borgmatic.actions.pattern import collect_patterns, process_patterns
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -25,64 +24,60 @@ def run_recreate(
|
|||||||
'''
|
'''
|
||||||
Run the "recreate" action for the given repository.
|
Run the "recreate" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if recreate_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
if recreate_arguments.archive:
|
||||||
repository,
|
logger.answer(f'Recreating archive {recreate_arguments.archive}')
|
||||||
recreate_arguments.repository,
|
else:
|
||||||
):
|
logger.answer('Recreating repository')
|
||||||
if recreate_arguments.archive:
|
|
||||||
logger.answer(f'Recreating archive {recreate_arguments.archive}')
|
|
||||||
else:
|
|
||||||
logger.answer('Recreating repository')
|
|
||||||
|
|
||||||
# Collect and process patterns.
|
# Collect and process patterns.
|
||||||
processed_patterns = process_patterns(
|
processed_patterns = process_patterns(
|
||||||
collect_patterns(config),
|
collect_patterns(config),
|
||||||
config,
|
config,
|
||||||
borgmatic.config.paths.get_working_directory(config),
|
borgmatic.config.paths.get_working_directory(config),
|
||||||
|
)
|
||||||
|
|
||||||
|
archive = borgmatic.borg.repo_list.resolve_archive_name(
|
||||||
|
repository['path'],
|
||||||
|
recreate_arguments.archive,
|
||||||
|
config,
|
||||||
|
local_borg_version,
|
||||||
|
global_arguments,
|
||||||
|
local_path,
|
||||||
|
remote_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
if archive and archive.endswith('.recreate'):
|
||||||
|
if recreate_arguments.archive == 'latest':
|
||||||
|
raise ValueError(
|
||||||
|
f'The latest archive "{archive}" is leftover from a prior recreate. Delete it first or select a different archive.',
|
||||||
|
)
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
f'The archive "{recreate_arguments.archive}" is leftover from a prior recreate. Select a different archive.',
|
||||||
)
|
)
|
||||||
|
|
||||||
archive = borgmatic.borg.repo_list.resolve_archive_name(
|
try:
|
||||||
|
borgmatic.borg.recreate.recreate_archive(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
recreate_arguments.archive,
|
archive,
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
|
recreate_arguments,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path=local_path,
|
||||||
remote_path,
|
remote_path=remote_path,
|
||||||
|
patterns=processed_patterns,
|
||||||
)
|
)
|
||||||
|
except subprocess.CalledProcessError as error:
|
||||||
if archive and archive.endswith('.recreate'):
|
if error.returncode == BORG_EXIT_CODE_ARCHIVE_ALREADY_EXISTS:
|
||||||
if recreate_arguments.archive == 'latest':
|
if recreate_arguments.target:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f'The latest archive "{archive}" is leftover from a prior recreate. Delete it first or select a different archive.',
|
f'The archive "{recreate_arguments.target}" already exists. Delete it first or set a different target archive name.',
|
||||||
)
|
)
|
||||||
|
|
||||||
raise ValueError(
|
if archive:
|
||||||
f'The archive "{recreate_arguments.archive}" is leftover from a prior recreate. Select a different archive.',
|
raise ValueError(
|
||||||
)
|
f'The archive "{archive}.recreate" is leftover from a prior recreate. Delete it first or select a different archive.',
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
raise
|
||||||
borgmatic.borg.recreate.recreate_archive(
|
|
||||||
repository['path'],
|
|
||||||
archive,
|
|
||||||
config,
|
|
||||||
local_borg_version,
|
|
||||||
recreate_arguments,
|
|
||||||
global_arguments,
|
|
||||||
local_path=local_path,
|
|
||||||
remote_path=remote_path,
|
|
||||||
patterns=processed_patterns,
|
|
||||||
)
|
|
||||||
except subprocess.CalledProcessError as error:
|
|
||||||
if error.returncode == BORG_EXIT_CODE_ARCHIVE_ALREADY_EXISTS:
|
|
||||||
if recreate_arguments.target:
|
|
||||||
raise ValueError(
|
|
||||||
f'The archive "{recreate_arguments.target}" already exists. Delete it first or set a different target archive name.',
|
|
||||||
)
|
|
||||||
|
|
||||||
if archive:
|
|
||||||
raise ValueError(
|
|
||||||
f'The archive "{archive}.recreate" is leftover from a prior recreate. Delete it first or select a different archive.',
|
|
||||||
)
|
|
||||||
|
|
||||||
raise
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import borgmatic.borg.repo_create
|
import borgmatic.borg.repo_create
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,12 +17,6 @@ def run_repo_create(
|
|||||||
'''
|
'''
|
||||||
Run the "repo-create" action for the given repository.
|
Run the "repo-create" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if repo_create_arguments.repository and not borgmatic.config.validate.repositories_match(
|
|
||||||
repository,
|
|
||||||
repo_create_arguments.repository,
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info('Creating repository')
|
logger.info('Creating repository')
|
||||||
|
|
||||||
encryption_mode = repo_create_arguments.encryption_mode or repository.get('encryption')
|
encryption_mode = repo_create_arguments.encryption_mode or repository.get('encryption')
|
||||||
|
|||||||
@@ -17,20 +17,16 @@ def run_repo_delete(
|
|||||||
'''
|
'''
|
||||||
Run the "repo-delete" action for the given repository.
|
Run the "repo-delete" action for the given repository.
|
||||||
'''
|
'''
|
||||||
if repo_delete_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
logger.answer(
|
||||||
repository,
|
'Deleting repository' + (' cache' if repo_delete_arguments.cache_only else ''),
|
||||||
repo_delete_arguments.repository,
|
)
|
||||||
):
|
|
||||||
logger.answer(
|
|
||||||
'Deleting repository' + (' cache' if repo_delete_arguments.cache_only else ''),
|
|
||||||
)
|
|
||||||
|
|
||||||
borgmatic.borg.repo_delete.delete_repository(
|
borgmatic.borg.repo_delete.delete_repository(
|
||||||
repository,
|
repository,
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
repo_delete_arguments,
|
repo_delete_arguments,
|
||||||
global_arguments,
|
global_arguments,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.actions.json
|
import borgmatic.actions.json
|
||||||
import borgmatic.borg.repo_info
|
import borgmatic.borg.repo_info
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -21,21 +20,17 @@ def run_repo_info(
|
|||||||
|
|
||||||
If repo_info_arguments.json is True, yield the JSON output from the info for the repository.
|
If repo_info_arguments.json is True, yield the JSON output from the info for the repository.
|
||||||
'''
|
'''
|
||||||
if repo_info_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
if not repo_info_arguments.json:
|
||||||
repository,
|
logger.answer('Displaying repository summary information')
|
||||||
repo_info_arguments.repository,
|
|
||||||
):
|
|
||||||
if not repo_info_arguments.json:
|
|
||||||
logger.answer('Displaying repository summary information')
|
|
||||||
|
|
||||||
json_output = borgmatic.borg.repo_info.display_repository_info(
|
json_output = borgmatic.borg.repo_info.display_repository_info(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
repo_info_arguments=repo_info_arguments,
|
repo_info_arguments=repo_info_arguments,
|
||||||
global_arguments=global_arguments,
|
global_arguments=global_arguments,
|
||||||
local_path=local_path,
|
local_path=local_path,
|
||||||
remote_path=remote_path,
|
remote_path=remote_path,
|
||||||
)
|
)
|
||||||
if json_output:
|
if json_output:
|
||||||
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
import borgmatic.actions.json
|
import borgmatic.actions.json
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.validate
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -21,21 +20,17 @@ def run_repo_list(
|
|||||||
|
|
||||||
If repo_list_arguments.json is True, yield the JSON output from listing the repository.
|
If repo_list_arguments.json is True, yield the JSON output from listing the repository.
|
||||||
'''
|
'''
|
||||||
if repo_list_arguments.repository is None or borgmatic.config.validate.repositories_match(
|
if not repo_list_arguments.json:
|
||||||
repository,
|
logger.answer('Listing repository')
|
||||||
repo_list_arguments.repository,
|
|
||||||
):
|
|
||||||
if not repo_list_arguments.json:
|
|
||||||
logger.answer('Listing repository')
|
|
||||||
|
|
||||||
json_output = borgmatic.borg.repo_list.list_repository(
|
json_output = borgmatic.borg.repo_list.list_repository(
|
||||||
repository['path'],
|
repository['path'],
|
||||||
config,
|
config,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
repo_list_arguments=repo_list_arguments,
|
repo_list_arguments=repo_list_arguments,
|
||||||
global_arguments=global_arguments,
|
global_arguments=global_arguments,
|
||||||
local_path=local_path,
|
local_path=local_path,
|
||||||
remote_path=remote_path,
|
remote_path=remote_path,
|
||||||
)
|
)
|
||||||
if json_output:
|
if json_output:
|
||||||
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
yield borgmatic.actions.json.parse_json(json_output, repository.get('label'))
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import pathlib
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
import borgmatic.actions.pattern
|
||||||
import borgmatic.borg.extract
|
import borgmatic.borg.extract
|
||||||
import borgmatic.borg.list
|
import borgmatic.borg.list
|
||||||
import borgmatic.borg.mount
|
import borgmatic.borg.mount
|
||||||
import borgmatic.borg.repo_list
|
import borgmatic.borg.repo_list
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.config.validate
|
|
||||||
import borgmatic.hooks.data_source.dump
|
import borgmatic.hooks.data_source.dump
|
||||||
import borgmatic.hooks.dispatch
|
import borgmatic.hooks.dispatch
|
||||||
|
|
||||||
@@ -523,26 +523,26 @@ def run_restore(
|
|||||||
remote_path,
|
remote_path,
|
||||||
):
|
):
|
||||||
'''
|
'''
|
||||||
Run the "restore" action for the given repository, but only if the repository matches the
|
Run the "restore" action for the given repository.
|
||||||
requested repository in restore arguments.
|
|
||||||
|
|
||||||
Raise ValueError if a configured data source could not be found to restore or there's no
|
Raise ValueError if a configured data source could not be found to restore or there's no
|
||||||
matching dump in the archive.
|
matching dump in the archive.
|
||||||
'''
|
'''
|
||||||
if restore_arguments.repository and not borgmatic.config.validate.repositories_match(
|
|
||||||
repository,
|
|
||||||
restore_arguments.repository,
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info(f'Restoring data sources from archive {restore_arguments.archive}')
|
logger.info(f'Restoring data sources from archive {restore_arguments.archive}')
|
||||||
|
working_directory = borgmatic.config.paths.get_working_directory(config)
|
||||||
|
|
||||||
with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory:
|
with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory:
|
||||||
|
patterns = borgmatic.actions.pattern.process_patterns(
|
||||||
|
borgmatic.actions.pattern.collect_patterns(config),
|
||||||
|
config,
|
||||||
|
working_directory,
|
||||||
|
)
|
||||||
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
|
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
|
||||||
'remove_data_source_dumps',
|
'remove_data_source_dumps',
|
||||||
config,
|
config,
|
||||||
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -625,6 +625,7 @@ def run_restore(
|
|||||||
config,
|
config,
|
||||||
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
from borgmatic.borg import environment, flags
|
from borgmatic.borg import environment, flags
|
||||||
@@ -22,6 +23,7 @@ def break_lock(
|
|||||||
'''
|
'''
|
||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('break_lock', '')
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'break-lock')
|
(local_path, 'break-lock')
|
||||||
@@ -31,6 +33,7 @@ def break_lock(
|
|||||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.execute
|
import borgmatic.execute
|
||||||
@@ -25,6 +26,7 @@ def change_passphrase(
|
|||||||
borgmatic.logger.add_custom_log_levels()
|
borgmatic.logger.add_custom_log_levels()
|
||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('key_change_passphrase', '')
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'key', 'change-passphrase')
|
(local_path, 'key', 'change-passphrase')
|
||||||
@@ -34,6 +36,7 @@ def change_passphrase(
|
|||||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(
|
+ flags.make_repository_flags(
|
||||||
repository_path,
|
repository_path,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def validate_planned_backup_paths(
|
|||||||
excluded.
|
excluded.
|
||||||
|
|
||||||
Raise ValueError if the runtime directory has been excluded via "exclude_patterns" or similar,
|
Raise ValueError if the runtime directory has been excluded via "exclude_patterns" or similar,
|
||||||
because any features that rely on the runtime directory getting backed up will break. For
|
because any features that rely on the runtime directory getting backed up will break. For
|
||||||
instance, without the runtime directory, Borg can't consume any database dumps and borgmatic may
|
instance, without the runtime directory, Borg can't consume any database dumps and borgmatic may
|
||||||
hang waiting for them to be consumed.
|
hang waiting for them to be consumed.
|
||||||
'''
|
'''
|
||||||
@@ -72,7 +72,11 @@ def validate_planned_backup_paths(
|
|||||||
paths_output = execute_command_and_capture_output(
|
paths_output = execute_command_and_capture_output(
|
||||||
(
|
(
|
||||||
*flags.omit_flag_and_value(
|
*flags.omit_flag_and_value(
|
||||||
flags.omit_flag(create_command, '--exclude-nodump'), '--filter'
|
flags.omit_flag(
|
||||||
|
flags.omit_flag(create_command, '--exclude-nodump'),
|
||||||
|
'--log-json',
|
||||||
|
),
|
||||||
|
'--filter',
|
||||||
),
|
),
|
||||||
'--dry-run',
|
'--dry-run',
|
||||||
'--list',
|
'--list',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.borg.environment
|
import borgmatic.borg.environment
|
||||||
import borgmatic.borg.feature
|
import borgmatic.borg.feature
|
||||||
@@ -28,6 +29,8 @@ def make_delete_command(
|
|||||||
arguments to the delete action as an argparse.Namespace, and global arguments, return a command
|
arguments to the delete action as an argparse.Namespace, and global arguments, return a command
|
||||||
as a tuple to delete archives from the repository.
|
as a tuple to delete archives from the repository.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('delete', '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(local_path, 'delete')
|
(local_path, 'delete')
|
||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
@@ -66,6 +69,7 @@ def make_delete_command(
|
|||||||
'repository',
|
'repository',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ borgmatic.borg.flags.make_repository_flags(repository['path'], local_borg_version)
|
+ borgmatic.borg.flags.make_repository_flags(repository['path'], local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ OPTION_TO_ENVIRONMENT_VARIABLE = {
|
|||||||
'borg_files_cache_ttl': 'BORG_FILES_CACHE_TTL',
|
'borg_files_cache_ttl': 'BORG_FILES_CACHE_TTL',
|
||||||
'borg_security_directory': 'BORG_SECURITY_DIR',
|
'borg_security_directory': 'BORG_SECURITY_DIR',
|
||||||
'borg_keys_directory': 'BORG_KEYS_DIR',
|
'borg_keys_directory': 'BORG_KEYS_DIR',
|
||||||
|
'borg_key_file': 'BORG_KEY_FILE',
|
||||||
'ssh_command': 'BORG_RSH',
|
'ssh_command': 'BORG_RSH',
|
||||||
'temporary_directory': 'TMPDIR',
|
'temporary_directory': 'TMPDIR',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -31,6 +32,7 @@ def export_key(
|
|||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
working_directory = borgmatic.config.paths.get_working_directory(config)
|
working_directory = borgmatic.config.paths.get_working_directory(config)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('key_export', '')
|
||||||
|
|
||||||
if export_arguments.path and export_arguments.path != '-':
|
if export_arguments.path and export_arguments.path != '-':
|
||||||
if os.path.exists(os.path.join(working_directory or '', export_arguments.path)):
|
if os.path.exists(os.path.join(working_directory or '', export_arguments.path)):
|
||||||
@@ -52,6 +54,7 @@ def export_key(
|
|||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
+ flags.make_flags('paper', export_arguments.paper)
|
+ flags.make_flags('paper', export_arguments.paper)
|
||||||
+ flags.make_flags('qr-html', export_arguments.qr_html)
|
+ flags.make_flags('qr-html', export_arguments.qr_html)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(
|
+ flags.make_repository_flags(
|
||||||
repository_path,
|
repository_path,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -34,6 +35,7 @@ def export_tar_archive(
|
|||||||
borgmatic.logger.add_custom_log_levels()
|
borgmatic.logger.add_custom_log_levels()
|
||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('export_tar', '')
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'export-tar')
|
(local_path, 'export-tar')
|
||||||
@@ -47,6 +49,7 @@ def export_tar_archive(
|
|||||||
+ (('--dry-run',) if dry_run else ())
|
+ (('--dry-run',) if dry_run else ())
|
||||||
+ (('--tar-filter', tar_filter) if tar_filter else ())
|
+ (('--tar-filter', tar_filter) if tar_filter else ())
|
||||||
+ (('--strip-components', str(strip_components)) if strip_components else ())
|
+ (('--strip-components', str(strip_components)) if strip_components else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_archive_flags(
|
+ flags.make_repository_archive_flags(
|
||||||
repository_path,
|
repository_path,
|
||||||
archive,
|
archive,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
@@ -23,6 +24,7 @@ def extract_last_archive_dry_run(
|
|||||||
Perform an extraction dry-run of the most recent archive. If there are no archives, skip the
|
Perform an extraction dry-run of the most recent archive. If there are no archives, skip the
|
||||||
dry-run.
|
dry-run.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('extract', '')
|
||||||
verbosity_flags = ()
|
verbosity_flags = ()
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
verbosity_flags = ('--debug', '--show-rc')
|
verbosity_flags = ('--debug', '--show-rc')
|
||||||
@@ -51,6 +53,7 @@ def extract_last_archive_dry_run(
|
|||||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||||
+ verbosity_flags
|
+ verbosity_flags
|
||||||
+ list_flag
|
+ list_flag
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_archive_flags(
|
+ flags.make_repository_archive_flags(
|
||||||
repository_path,
|
repository_path,
|
||||||
last_archive_name,
|
last_archive_name,
|
||||||
@@ -92,6 +95,7 @@ def extract_archive(
|
|||||||
'''
|
'''
|
||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('extract', '')
|
||||||
|
|
||||||
if config.get('progress') and extract_to_stdout:
|
if config.get('progress') and extract_to_stdout:
|
||||||
raise ValueError('progress and extract to stdout cannot both be set')
|
raise ValueError('progress and extract to stdout cannot both be set')
|
||||||
@@ -131,6 +135,7 @@ def extract_archive(
|
|||||||
+ (('--strip-components', str(strip_components)) if strip_components else ())
|
+ (('--strip-components', str(strip_components)) if strip_components else ())
|
||||||
+ (('--progress',) if config.get('progress') else ())
|
+ (('--progress',) if config.get('progress') else ())
|
||||||
+ (('--stdout',) if extract_to_stdout else ())
|
+ (('--stdout',) if extract_to_stdout else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_archive_flags(
|
+ flags.make_repository_archive_flags(
|
||||||
# Make the repository path absolute so the destination directory used below via changing
|
# Make the repository path absolute so the destination directory used below via changing
|
||||||
# the working directory doesn't prevent Borg from finding the repo. But also apply the
|
# the working directory doesn't prevent Borg from finding the repo. But also apply the
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -30,6 +31,7 @@ def import_key(
|
|||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
working_directory = borgmatic.config.paths.get_working_directory(config)
|
working_directory = borgmatic.config.paths.get_working_directory(config)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('key_import', '')
|
||||||
|
|
||||||
if import_arguments.path and import_arguments.path != '-':
|
if import_arguments.path and import_arguments.path != '-':
|
||||||
if not os.path.exists(os.path.join(working_directory or '', import_arguments.path)):
|
if not os.path.exists(os.path.join(working_directory or '', import_arguments.path)):
|
||||||
@@ -48,6 +50,7 @@ def import_key(
|
|||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
+ flags.make_flags('paper', import_arguments.paper)
|
+ flags.make_flags('paper', import_arguments.paper)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(
|
+ flags.make_repository_flags(
|
||||||
repository_path,
|
repository_path,
|
||||||
local_borg_version,
|
local_borg_version,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -23,6 +24,8 @@ def make_info_command(
|
|||||||
arguments to the info action as an argparse.Namespace, and global arguments, return a command
|
arguments to the info action as an argparse.Namespace, and global arguments, return a command
|
||||||
as a tuple to display summary information for archives in the repository.
|
as a tuple to display summary information for archives in the repository.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('info', '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(local_path, 'info')
|
(local_path, 'info')
|
||||||
+ (
|
+ (
|
||||||
@@ -58,6 +61,7 @@ def make_info_command(
|
|||||||
info_arguments,
|
info_arguments,
|
||||||
excludes=('repository', 'archive', 'prefix', 'match_archives'),
|
excludes=('repository', 'archive', 'prefix', 'match_archives'),
|
||||||
)
|
)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import argparse
|
|||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -35,6 +36,8 @@ def make_list_command(
|
|||||||
and local and remote Borg paths, return a command as a tuple to list archives or paths within an
|
and local and remote Borg paths, return a command as a tuple to list archives or paths within an
|
||||||
archive.
|
archive.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('list', '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(local_path, 'list')
|
(local_path, 'list')
|
||||||
+ (
|
+ (
|
||||||
@@ -52,6 +55,7 @@ def make_list_command(
|
|||||||
+ flags.make_flags('log-json', config.get('log_json'))
|
+ flags.make_flags('log-json', config.get('log_json'))
|
||||||
+ flags.make_flags('lock-wait', config.get('lock_wait'))
|
+ flags.make_flags('lock-wait', config.get('lock_wait'))
|
||||||
+ flags.make_flags_from_arguments(list_arguments, excludes=MAKE_FLAGS_EXCLUDES)
|
+ flags.make_flags_from_arguments(list_arguments, excludes=MAKE_FLAGS_EXCLUDES)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ (
|
+ (
|
||||||
flags.make_repository_archive_flags(
|
flags.make_repository_archive_flags(
|
||||||
repository_path,
|
repository_path,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
from borgmatic.borg import environment, feature, flags
|
from borgmatic.borg import environment, feature, flags
|
||||||
@@ -25,6 +26,7 @@ def mount_archive(
|
|||||||
'''
|
'''
|
||||||
umask = config.get('umask', None)
|
umask = config.get('umask', None)
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('mount', '')
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'mount')
|
(local_path, 'mount')
|
||||||
@@ -39,6 +41,7 @@ def mount_archive(
|
|||||||
excludes=('repository', 'archive', 'mount_point', 'paths', 'options'),
|
excludes=('repository', 'archive', 'mount_point', 'paths', 'options'),
|
||||||
)
|
)
|
||||||
+ (('-o', mount_arguments.options) if mount_arguments.options else ())
|
+ (('-o', mount_arguments.options) if mount_arguments.options else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ (
|
+ (
|
||||||
(
|
(
|
||||||
flags.make_repository_flags(repository_path, local_borg_version)
|
flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ def recreate_archive(
|
|||||||
exclude_flags = flags.make_exclude_flags(config)
|
exclude_flags = flags.make_exclude_flags(config)
|
||||||
compression = config.get('compression', None)
|
compression = config.get('compression', None)
|
||||||
chunker_params = config.get('chunker_params', None)
|
chunker_params = config.get('chunker_params', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('recreate', '')
|
||||||
|
|
||||||
# Available recompress MODES: "if-different", "always", "never" (default)
|
# Available recompress MODES: "if-different", "always", "never" (default)
|
||||||
recompress = config.get('recompress', None)
|
recompress = config.get('recompress', None)
|
||||||
@@ -71,6 +72,7 @@ def recreate_archive(
|
|||||||
+ (('--chunker-params', chunker_params) if chunker_params else ())
|
+ (('--chunker-params', chunker_params) if chunker_params else ())
|
||||||
+ (('--recompress', recompress) if recompress else ())
|
+ (('--recompress', recompress) if recompress else ())
|
||||||
+ exclude_flags
|
+ exclude_flags
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ (
|
+ (
|
||||||
(
|
(
|
||||||
flags.make_repository_flags(repository, local_borg_version)
|
flags.make_repository_flags(repository, local_borg_version)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.borg.flags
|
import borgmatic.borg.flags
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ def make_rename_command(
|
|||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
):
|
):
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('rename', '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(local_path, 'rename')
|
(local_path, 'rename')
|
||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
@@ -24,6 +27,7 @@ def make_rename_command(
|
|||||||
+ borgmatic.borg.flags.make_flags('umask', config.get('umask'))
|
+ borgmatic.borg.flags.make_flags('umask', config.get('umask'))
|
||||||
+ borgmatic.borg.flags.make_flags('log-json', config.get('log_json'))
|
+ borgmatic.borg.flags.make_flags('log-json', config.get('log_json'))
|
||||||
+ borgmatic.borg.flags.make_flags('lock-wait', config.get('lock_wait'))
|
+ borgmatic.borg.flags.make_flags('lock-wait', config.get('lock_wait'))
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ borgmatic.borg.flags.make_repository_archive_flags(
|
+ borgmatic.borg.flags.make_repository_archive_flags(
|
||||||
repository_name,
|
repository_name,
|
||||||
old_archive_name,
|
old_archive_name,
|
||||||
|
|||||||
@@ -66,8 +66,14 @@ def create_repository(
|
|||||||
|
|
||||||
lock_wait = config.get('lock_wait')
|
lock_wait = config.get('lock_wait')
|
||||||
umask = config.get('umask')
|
umask = config.get('umask')
|
||||||
|
extra_borg_options_from_init = config.get('extra_borg_options', {}).get('init', '')
|
||||||
extra_borg_options = config.get('extra_borg_options', {}).get('repo-create', '')
|
extra_borg_options = config.get('extra_borg_options', {}).get('repo-create', '')
|
||||||
|
|
||||||
|
if extra_borg_options_from_init:
|
||||||
|
logger.warning(
|
||||||
|
'The "init" option in "extra_borg_options" is deprecated and will be removed from a future release; use "repo_create" instead.'
|
||||||
|
)
|
||||||
|
|
||||||
repo_create_command = (
|
repo_create_command = (
|
||||||
(local_path,)
|
(local_path,)
|
||||||
+ (
|
+ (
|
||||||
@@ -88,6 +94,7 @@ def create_repository(
|
|||||||
+ (('--remote-path', remote_path) if remote_path else ())
|
+ (('--remote-path', remote_path) if remote_path else ())
|
||||||
+ (('--umask', str(umask)) if umask else ())
|
+ (('--umask', str(umask)) if umask else ())
|
||||||
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options_from_init)) if extra_borg_options_from_init else ())
|
||||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.borg.environment
|
import borgmatic.borg.environment
|
||||||
import borgmatic.borg.feature
|
import borgmatic.borg.feature
|
||||||
@@ -26,6 +27,15 @@ def make_repo_delete_command(
|
|||||||
arguments to the repo_delete action as an argparse.Namespace, and global arguments, return a command
|
arguments to the repo_delete action as an argparse.Namespace, and global arguments, return a command
|
||||||
as a tuple to repo_delete the entire repository.
|
as a tuple to repo_delete the entire repository.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get(
|
||||||
|
'repo_delete'
|
||||||
|
if borgmatic.borg.feature.available(
|
||||||
|
borgmatic.borg.feature.Feature.REPO_DELETE, local_borg_version
|
||||||
|
)
|
||||||
|
else 'delete',
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(local_path,)
|
(local_path,)
|
||||||
+ (
|
+ (
|
||||||
@@ -56,6 +66,7 @@ def make_repo_delete_command(
|
|||||||
repo_delete_arguments,
|
repo_delete_arguments,
|
||||||
excludes=('list_details', 'force', 'repository'),
|
excludes=('list_details', 'force', 'repository'),
|
||||||
)
|
)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ borgmatic.borg.flags.make_repository_flags(repository['path'], local_borg_version)
|
+ borgmatic.borg.flags.make_repository_flags(repository['path'], local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -24,6 +25,10 @@ def display_repository_info(
|
|||||||
'''
|
'''
|
||||||
borgmatic.logger.add_custom_log_levels()
|
borgmatic.logger.add_custom_log_levels()
|
||||||
lock_wait = config.get('lock_wait', None)
|
lock_wait = config.get('lock_wait', None)
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get(
|
||||||
|
'repo_info' if feature.available(feature.Feature.REPO_INFO, local_borg_version) else 'info',
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path,)
|
(local_path,)
|
||||||
@@ -47,6 +52,7 @@ def display_repository_info(
|
|||||||
+ flags.make_flags('log-json', config.get('log_json'))
|
+ flags.make_flags('log-json', config.get('log_json'))
|
||||||
+ flags.make_flags('lock-wait', lock_wait)
|
+ flags.make_flags('lock-wait', lock_wait)
|
||||||
+ (('--json',) if repo_info_arguments.json else ())
|
+ (('--json',) if repo_info_arguments.json else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -62,6 +63,10 @@ def get_latest_archive(
|
|||||||
|
|
||||||
Raises ValueError if there are no archives in the repository.
|
Raises ValueError if there are no archives in the repository.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get(
|
||||||
|
'repo_list' if feature.available(feature.Feature.REPO_LIST, local_borg_version) else 'list',
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
local_path,
|
local_path,
|
||||||
@@ -81,6 +86,7 @@ def get_latest_archive(
|
|||||||
),
|
),
|
||||||
*flags.make_flags('last', 1),
|
*flags.make_flags('last', 1),
|
||||||
'--json',
|
'--json',
|
||||||
|
*(tuple(shlex.split(extra_borg_options)) if extra_borg_options else ()),
|
||||||
*flags.make_repository_flags(repository_path, local_borg_version),
|
*flags.make_repository_flags(repository_path, local_borg_version),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -121,6 +127,11 @@ def make_repo_list_command(
|
|||||||
arguments to the repo_list action, global arguments as an argparse.Namespace instance, and local and
|
arguments to the repo_list action, global arguments as an argparse.Namespace instance, and local and
|
||||||
remote Borg paths, return a command as a tuple to list archives with a repository.
|
remote Borg paths, return a command as a tuple to list archives with a repository.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get(
|
||||||
|
'repo_list' if feature.available(feature.Feature.REPO_LIST, local_borg_version) else 'list',
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
local_path,
|
local_path,
|
||||||
@@ -160,6 +171,7 @@ def make_repo_list_command(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
+ flags.make_flags_from_arguments(repo_list_arguments, excludes=MAKE_FLAGS_EXCLUDES)
|
+ flags.make_flags_from_arguments(repo_list_arguments, excludes=MAKE_FLAGS_EXCLUDES)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.logger
|
import borgmatic.logger
|
||||||
@@ -24,6 +25,7 @@ def transfer_archives(
|
|||||||
instance, transfer archives to the given repository.
|
instance, transfer archives to the given repository.
|
||||||
'''
|
'''
|
||||||
borgmatic.logger.add_custom_log_levels()
|
borgmatic.logger.add_custom_log_levels()
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('transfer', '')
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'transfer')
|
(local_path, 'transfer')
|
||||||
@@ -53,6 +55,7 @@ def transfer_archives(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||||
+ flags.make_flags('other-repo', transfer_arguments.source_repository)
|
+ flags.make_flags('other-repo', transfer_arguments.source_repository)
|
||||||
+ flags.make_flags('dry-run', dry_run)
|
+ flags.make_flags('dry-run', dry_run)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
from borgmatic.execute import execute_command
|
from borgmatic.execute import execute_command
|
||||||
@@ -11,10 +12,12 @@ def unmount_archive(config, mount_point, local_path='borg'):
|
|||||||
Given a mounted filesystem mount point, and an optional local Borg paths, umount the filesystem
|
Given a mounted filesystem mount point, and an optional local Borg paths, umount the filesystem
|
||||||
from the mount point.
|
from the mount point.
|
||||||
'''
|
'''
|
||||||
|
extra_borg_options = config.get('extra_borg_options', {}).get('umount', '')
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'umount')
|
(local_path, 'umount')
|
||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
|
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||||
+ (mount_point,)
|
+ (mount_point,)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ def local_borg_version(config, local_path='borg'):
|
|||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
)
|
)
|
||||||
|
|
||||||
output = execute_command_and_capture_output(
|
output = execute_command_and_capture_output(
|
||||||
full_command,
|
full_command,
|
||||||
environment=environment.make_environment(config),
|
environment=environment.make_environment(config),
|
||||||
|
|||||||
@@ -641,8 +641,8 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
|||||||
repo_create_parser = action_parsers.add_parser(
|
repo_create_parser = action_parsers.add_parser(
|
||||||
'repo-create',
|
'repo-create',
|
||||||
aliases=ACTION_ALIASES['repo-create'],
|
aliases=ACTION_ALIASES['repo-create'],
|
||||||
help='Create a new, empty Borg repository',
|
help='Create a new, empty Borg repository (also known as "init")',
|
||||||
description='Create a new, empty Borg repository',
|
description='Create a new, empty Borg repository (also known as "init")',
|
||||||
add_help=False,
|
add_help=False,
|
||||||
)
|
)
|
||||||
repo_create_group = repo_create_parser.add_argument_group('repo-create arguments')
|
repo_create_group = repo_create_parser.add_argument_group('repo-create arguments')
|
||||||
@@ -1213,9 +1213,9 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
|||||||
config_generate_group.add_argument(
|
config_generate_group.add_argument(
|
||||||
'-d',
|
'-d',
|
||||||
'--destination',
|
'--destination',
|
||||||
dest='destination_filename',
|
dest='destination_path',
|
||||||
default=config_paths[0],
|
default=config_paths[0],
|
||||||
help=f'Destination configuration file, default: {unexpanded_config_paths[0]}',
|
help=f'Destination configuration file (or directory if using --split), default: {unexpanded_config_paths[0]}',
|
||||||
)
|
)
|
||||||
config_generate_group.add_argument(
|
config_generate_group.add_argument(
|
||||||
'--overwrite',
|
'--overwrite',
|
||||||
@@ -1223,6 +1223,11 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help='Whether to overwrite any existing destination file, defaults to false',
|
help='Whether to overwrite any existing destination file, defaults to false',
|
||||||
)
|
)
|
||||||
|
config_generate_group.add_argument(
|
||||||
|
'--split',
|
||||||
|
action='store_true',
|
||||||
|
help='Assuming the destination is a directory instead of a file, split the configuration into separate files within it, one per option, useful for documentation',
|
||||||
|
)
|
||||||
config_generate_group.add_argument(
|
config_generate_group.add_argument(
|
||||||
'-h',
|
'-h',
|
||||||
'--help',
|
'--help',
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ def run_actions( # noqa: PLR0912, PLR0915
|
|||||||
'''
|
'''
|
||||||
Given parsed command-line arguments as an argparse.ArgumentParser instance, the configuration
|
Given parsed command-line arguments as an argparse.ArgumentParser instance, the configuration
|
||||||
filename, a configuration dict, a sequence of loaded configuration paths, local and remote paths
|
filename, a configuration dict, a sequence of loaded configuration paths, local and remote paths
|
||||||
to Borg, a local Borg version string, and a repository name, run all actions from the
|
to Borg, a local Borg version string, and a repository dict, run all actions from the
|
||||||
command-line arguments on the given repository.
|
command-line arguments on the given repository.
|
||||||
|
|
||||||
Yield JSON output strings from executing any actions that produce JSON.
|
Yield JSON output strings from executing any actions that produce JSON.
|
||||||
@@ -362,6 +362,22 @@ def run_actions( # noqa: PLR0912, PLR0915
|
|||||||
'repository': repository_path,
|
'repository': repository_path,
|
||||||
}
|
}
|
||||||
skip_actions = set(get_skip_actions(config, arguments))
|
skip_actions = set(get_skip_actions(config, arguments))
|
||||||
|
requested_repository = next(
|
||||||
|
(
|
||||||
|
repository
|
||||||
|
for action_arguments in arguments.values()
|
||||||
|
for repository in (getattr(action_arguments, 'repository', None),)
|
||||||
|
if repository is not None
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
if requested_repository and not borgmatic.config.validate.repositories_match(
|
||||||
|
repository,
|
||||||
|
requested_repository,
|
||||||
|
):
|
||||||
|
logger.debug('Skipping actions because the requested --repository does not match')
|
||||||
|
return
|
||||||
|
|
||||||
with borgmatic.hooks.command.Before_after_hooks(
|
with borgmatic.hooks.command.Before_after_hooks(
|
||||||
command_hooks=config.get('commands'),
|
command_hooks=config.get('commands'),
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ def comment_out_line(line):
|
|||||||
return '# '.join((indent_spaces, line[count_indent_spaces:]))
|
return '# '.join((indent_spaces, line[count_indent_spaces:]))
|
||||||
|
|
||||||
|
|
||||||
def comment_out_optional_configuration(rendered_config):
|
def transform_optional_configuration(rendered_config, comment_out=True):
|
||||||
'''
|
'''
|
||||||
Post-process a rendered configuration string to comment out optional key/values, as determined
|
Post-process a rendered configuration string to comment out optional key/values, as determined
|
||||||
by a sentinel in the comment before each key.
|
by a sentinel in the comment before each key.
|
||||||
@@ -117,6 +117,9 @@ def comment_out_optional_configuration(rendered_config):
|
|||||||
|
|
||||||
Ideally ruamel.yaml would support commenting out keys during configuration generation, but it's
|
Ideally ruamel.yaml would support commenting out keys during configuration generation, but it's
|
||||||
not terribly easy to accomplish that way.
|
not terribly easy to accomplish that way.
|
||||||
|
|
||||||
|
If comment_out is False, then just strip the comment sentinel without actually commenting
|
||||||
|
anything out.
|
||||||
'''
|
'''
|
||||||
lines = []
|
lines = []
|
||||||
optional = False
|
optional = False
|
||||||
@@ -129,6 +132,9 @@ def comment_out_optional_configuration(rendered_config):
|
|||||||
# Upon encountering an optional configuration option, comment out lines until the next blank
|
# Upon encountering an optional configuration option, comment out lines until the next blank
|
||||||
# line.
|
# line.
|
||||||
if line.strip().startswith(f'# {COMMENTED_OUT_SENTINEL}'):
|
if line.strip().startswith(f'# {COMMENTED_OUT_SENTINEL}'):
|
||||||
|
if comment_out is False:
|
||||||
|
continue
|
||||||
|
|
||||||
optional = True
|
optional = True
|
||||||
indent_characters_at_sentinel = indent_characters
|
indent_characters_at_sentinel = indent_characters
|
||||||
continue
|
continue
|
||||||
@@ -313,16 +319,18 @@ def merge_source_configuration_into_destination(destination_config, source_confi
|
|||||||
def generate_sample_configuration(
|
def generate_sample_configuration(
|
||||||
dry_run,
|
dry_run,
|
||||||
source_filename,
|
source_filename,
|
||||||
destination_filename,
|
destination_path,
|
||||||
schema_filename,
|
schema_filename,
|
||||||
overwrite=False,
|
overwrite=False,
|
||||||
|
split=False,
|
||||||
):
|
):
|
||||||
'''
|
'''
|
||||||
Given an optional source configuration filename, and a required destination configuration
|
Given an optional source configuration filename, a required destination configuration path, the
|
||||||
filename, the path to a schema filename in a YAML rendition of the JSON Schema format, and
|
path to a schema filename in a YAML rendition of the JSON Schema format, whether to overwrite a
|
||||||
whether to overwrite a destination file, write out a sample configuration file based on that
|
destination file, and whether to split the configuration into multiple files (one per option) in
|
||||||
schema. If a source filename is provided, merge the parsed contents of that configuration into
|
the assumed destination directory, write out sample configuration file(s) based on that schema.
|
||||||
the generated configuration.
|
If a source filename is provided, merge the parsed contents of that configuration into the
|
||||||
|
generated configuration.
|
||||||
'''
|
'''
|
||||||
schema = ruamel.yaml.YAML(typ='safe').load(open(schema_filename, encoding='utf-8'))
|
schema = ruamel.yaml.YAML(typ='safe').load(open(schema_filename, encoding='utf-8'))
|
||||||
source_config = None
|
source_config = None
|
||||||
@@ -345,8 +353,31 @@ def generate_sample_configuration(
|
|||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if split:
|
||||||
|
if os.path.exists(destination_path) and not os.path.isdir(destination_path):
|
||||||
|
raise ValueError('With the --split flag, the destination path must be a directory')
|
||||||
|
|
||||||
|
os.makedirs(destination_path, exist_ok=True)
|
||||||
|
|
||||||
|
for option_name, option_config in destination_config.items():
|
||||||
|
write_configuration(
|
||||||
|
os.path.join(destination_path, f'{option_name}.yaml'),
|
||||||
|
transform_optional_configuration(
|
||||||
|
render_configuration({option_name: option_config}),
|
||||||
|
comment_out=False,
|
||||||
|
),
|
||||||
|
overwrite=overwrite,
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
if os.path.exists(destination_path) and not os.path.isfile(destination_path):
|
||||||
|
raise ValueError('Without the --split flag, the destination path must be a file')
|
||||||
|
|
||||||
write_configuration(
|
write_configuration(
|
||||||
destination_filename,
|
destination_path,
|
||||||
comment_out_optional_configuration(render_configuration(destination_config)),
|
transform_optional_configuration(
|
||||||
|
render_configuration(destination_config), comment_out=True
|
||||||
|
),
|
||||||
overwrite=overwrite,
|
overwrite=overwrite,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,10 +2,19 @@ import contextlib
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Systemd_directories(Enum):
|
||||||
|
RUNTIME_DIRECTORY = 0
|
||||||
|
STATE_DIRECTORY = 1
|
||||||
|
CACHE_DIRECTORY = 2
|
||||||
|
LOGS_DIRECTORY = 3
|
||||||
|
CONFIGURATION_DIRECTORY = 4
|
||||||
|
|
||||||
|
|
||||||
def expand_user_in_path(path):
|
def expand_user_in_path(path):
|
||||||
'''
|
'''
|
||||||
Given a directory path, expand any tildes in it.
|
Given a directory path, expand any tildes in it.
|
||||||
@@ -16,6 +25,17 @@ def expand_user_in_path(path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_systemd_directory(directory):
|
||||||
|
'''
|
||||||
|
Given a systemd directory environment variable enum, read the value if set and return the first
|
||||||
|
configured directory.
|
||||||
|
'''
|
||||||
|
separator = ':'
|
||||||
|
|
||||||
|
paths = os.environ.get(directory.name)
|
||||||
|
return paths.split(separator)[0] if paths else None
|
||||||
|
|
||||||
|
|
||||||
def get_working_directory(config): # pragma: no cover
|
def get_working_directory(config): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
Given a configuration dict, get the working directory from it, expanding any tildes.
|
Given a configuration dict, get the working directory from it, expanding any tildes.
|
||||||
@@ -96,7 +116,9 @@ class Runtime_directory:
|
|||||||
runtime_directory = (
|
runtime_directory = (
|
||||||
config.get('user_runtime_directory')
|
config.get('user_runtime_directory')
|
||||||
or os.environ.get('XDG_RUNTIME_DIR') # Set by PAM on Linux.
|
or os.environ.get('XDG_RUNTIME_DIR') # Set by PAM on Linux.
|
||||||
or os.environ.get('RUNTIME_DIRECTORY') # Set by systemd if configured.
|
or resolve_systemd_directory(
|
||||||
|
Systemd_directories.RUNTIME_DIRECTORY
|
||||||
|
) # Set by systemd if configured.
|
||||||
)
|
)
|
||||||
|
|
||||||
if runtime_directory:
|
if runtime_directory:
|
||||||
@@ -174,7 +196,9 @@ def get_borgmatic_state_directory(config):
|
|||||||
os.path.join(
|
os.path.join(
|
||||||
config.get('user_state_directory')
|
config.get('user_state_directory')
|
||||||
or os.environ.get('XDG_STATE_HOME')
|
or os.environ.get('XDG_STATE_HOME')
|
||||||
or os.environ.get('STATE_DIRECTORY') # Set by systemd if configured.
|
or resolve_systemd_directory(
|
||||||
|
Systemd_directories.STATE_DIRECTORY
|
||||||
|
) # Set by systemd if configured.
|
||||||
or '~/.local/state',
|
or '~/.local/state',
|
||||||
'borgmatic',
|
'borgmatic',
|
||||||
),
|
),
|
||||||
|
|||||||
+167
-36
@@ -21,12 +21,22 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
List of source directories and files to back up. Globs and tildes
|
List of source directories and files to back up. Globs and tildes
|
||||||
are expanded. Do not backslash spaces in path names.
|
are expanded. Do not backslash spaces in path names. Be aware that
|
||||||
|
by default, Borg treats missing source directories as warnings
|
||||||
|
rather than errors. If you'd like to change that behavior, see
|
||||||
|
https://torsion.org/borgmatic/how-to/customize-warnings-and-errors/
|
||||||
|
or the "source_directories_must_exist" option.
|
||||||
example:
|
example:
|
||||||
- /home
|
- /home
|
||||||
- /etc
|
- /etc
|
||||||
- /var/log/syslog*
|
- /var/log/syslog*
|
||||||
- /home/user/path with spaces
|
- /home/user/path with spaces
|
||||||
|
source_directories_must_exist:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
If true, then source directories (and root pattern paths) must
|
||||||
|
exist. If they don't, an error is raised. Defaults to false.
|
||||||
|
example: true
|
||||||
repositories:
|
repositories:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -73,7 +83,7 @@ properties:
|
|||||||
Whether any missing parent directories of the repository
|
Whether any missing parent directories of the repository
|
||||||
path should be created, only used for the repo-create
|
path should be created, only used for the repo-create
|
||||||
action. Defaults to false. (This option is supported
|
action. Defaults to false. (This option is supported
|
||||||
for Borg 1.x only)
|
for Borg 1.x only.)
|
||||||
example: true
|
example: true
|
||||||
description: |
|
description: |
|
||||||
A required list of local or remote repositories with paths and
|
A required list of local or remote repositories with paths and
|
||||||
@@ -163,9 +173,11 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Any paths matching these patterns are included/excluded from
|
Any paths matching these patterns are included/excluded from
|
||||||
backups. Globs are expanded. (Tildes are not.) See the output of
|
backups. Recursion root patterns ("R ...") are effectively the same
|
||||||
"borg help patterns" for more details. Quote any value if it
|
as "source_directories"; they tell Borg which paths to backup
|
||||||
contains leading punctuation, so it parses correctly.
|
(modulo any excludes). Globs are expanded. (Tildes are not.) See
|
||||||
|
the output of "borg help patterns" for more details. Quote any value
|
||||||
|
if it contains leading punctuation, so it parses correctly.
|
||||||
example:
|
example:
|
||||||
- 'R /'
|
- 'R /'
|
||||||
- '- /home/*/.cache'
|
- '- /home/*/.cache'
|
||||||
@@ -232,7 +244,8 @@ properties:
|
|||||||
exclude_nodump:
|
exclude_nodump:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: |
|
description: |
|
||||||
Exclude files with the NODUMP flag. Defaults to false.
|
Exclude files with the NODUMP flag. Defaults to false. (This option
|
||||||
|
is supported for Borg 1.x only.)
|
||||||
example: true
|
example: true
|
||||||
borgmatic_source_directory:
|
borgmatic_source_directory:
|
||||||
type: string
|
type: string
|
||||||
@@ -248,7 +261,7 @@ properties:
|
|||||||
Path for storing temporary runtime data like streaming database
|
Path for storing temporary runtime data like streaming database
|
||||||
dumps and bootstrap metadata. borgmatic automatically creates and
|
dumps and bootstrap metadata. borgmatic automatically creates and
|
||||||
uses a "borgmatic" subdirectory here. Defaults to $XDG_RUNTIME_DIR
|
uses a "borgmatic" subdirectory here. Defaults to $XDG_RUNTIME_DIR
|
||||||
or or $TMPDIR or $TEMP or /run/user/$UID.
|
or $TMPDIR or $TEMP or /run/user/$UID.
|
||||||
example: /run/user/1001
|
example: /run/user/1001
|
||||||
user_state_directory:
|
user_state_directory:
|
||||||
type: string
|
type: string
|
||||||
@@ -259,12 +272,6 @@ properties:
|
|||||||
create the check records again (and therefore re-run checks).
|
create the check records again (and therefore re-run checks).
|
||||||
Defaults to $XDG_STATE_HOME or ~/.local/state.
|
Defaults to $XDG_STATE_HOME or ~/.local/state.
|
||||||
example: /var/lib/borgmatic
|
example: /var/lib/borgmatic
|
||||||
source_directories_must_exist:
|
|
||||||
type: boolean
|
|
||||||
description: |
|
|
||||||
If true, then source directories (and root pattern paths) must
|
|
||||||
exist. If they don't, an error is raised. Defaults to false.
|
|
||||||
example: true
|
|
||||||
encryption_passcommand:
|
encryption_passcommand:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
@@ -275,7 +282,7 @@ properties:
|
|||||||
encryption_passphrase takes precedence. This can also be used to
|
encryption_passphrase takes precedence. This can also be used to
|
||||||
access encrypted systemd service credentials. Defaults to not set.
|
access encrypted systemd service credentials. Defaults to not set.
|
||||||
For more details, see:
|
For more details, see:
|
||||||
https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/
|
https://torsion.org/borgmatic/how-to/provide-your-passwords/
|
||||||
example: "secret-tool lookup borg-repository repo-name"
|
example: "secret-tool lookup borg-repository repo-name"
|
||||||
encryption_passphrase:
|
encryption_passphrase:
|
||||||
type: string
|
type: string
|
||||||
@@ -406,14 +413,20 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Path for Borg security and encryption nonce files. Defaults to
|
Path for Borg security and encryption nonce files. Defaults to
|
||||||
$borg_base_directory/.config/borg/security
|
$borg_config_directory/security
|
||||||
example: /path/to/base/config/security
|
example: /path/to/base/config/security
|
||||||
borg_keys_directory:
|
borg_keys_directory:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Path for Borg encryption key files. Defaults to
|
Path for Borg encryption key files. Defaults to
|
||||||
$borg_base_directory/.config/borg/keys
|
$borg_config_directory/keys
|
||||||
example: /path/to/base/config/keys
|
example: /path/to/base/config/keys
|
||||||
|
borg_key_file:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Path for the Borg repository key file, for use with a repository
|
||||||
|
created with "keyfile" encryption.
|
||||||
|
example: /path/to/base/config/keyfile
|
||||||
borg_exit_codes:
|
borg_exit_codes:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -514,30 +527,123 @@ properties:
|
|||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
init:
|
break_lock:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Extra command-line options to pass to "borg init".
|
Extra command-line options to pass to "borg break-lock".
|
||||||
example: "--extra-option"
|
example: "--extra-option"
|
||||||
create:
|
check:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Extra command-line options to pass to "borg create".
|
Extra command-line options to pass to "borg check".
|
||||||
example: "--extra-option"
|
|
||||||
prune:
|
|
||||||
type: string
|
|
||||||
description: |
|
|
||||||
Extra command-line options to pass to "borg prune".
|
|
||||||
example: "--extra-option"
|
example: "--extra-option"
|
||||||
compact:
|
compact:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Extra command-line options to pass to "borg compact".
|
Extra command-line options to pass to "borg compact".
|
||||||
example: "--extra-option"
|
example: "--extra-option"
|
||||||
check:
|
create:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Extra command-line options to pass to "borg check".
|
Extra command-line options to pass to "borg create".
|
||||||
|
example: "--extra-option"
|
||||||
|
delete:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg delete".
|
||||||
|
example: "--extra-option"
|
||||||
|
export_tar:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg export-tar".
|
||||||
|
example: "--extra-option"
|
||||||
|
extract:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg extract".
|
||||||
|
example: "--extra-option"
|
||||||
|
key_export:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg key export".
|
||||||
|
example: "--extra-option"
|
||||||
|
key_import:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg key import".
|
||||||
|
example: "--extra-option"
|
||||||
|
key_change_passphrase:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg key
|
||||||
|
change-passphrase".
|
||||||
|
example: "--extra-option"
|
||||||
|
info:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg info".
|
||||||
|
example: "--extra-option"
|
||||||
|
init:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Deprecated. Use "repo_create" instead. Extra command-line
|
||||||
|
options to pass to "borg init" / "borg repo-create".
|
||||||
|
example: "--extra-option"
|
||||||
|
list:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg list".
|
||||||
|
example: "--extra-option"
|
||||||
|
mount:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg mount".
|
||||||
|
example: "--extra-option"
|
||||||
|
prune:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg prune".
|
||||||
|
example: "--extra-option"
|
||||||
|
recreate:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg recreate".
|
||||||
|
example: "--extra-option"
|
||||||
|
rename:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg rename".
|
||||||
|
example: "--extra-option"
|
||||||
|
repo_create:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg init" / "borg
|
||||||
|
repo-create".
|
||||||
|
example: "--extra-option"
|
||||||
|
repo_delete:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg repo-delete".
|
||||||
|
example: "--extra-option"
|
||||||
|
repo_info:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg repo-info".
|
||||||
|
example: "--extra-option"
|
||||||
|
repo_list:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg repo-list".
|
||||||
|
example: "--extra-option"
|
||||||
|
transfer:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg transfer".
|
||||||
|
example: "--extra-option"
|
||||||
|
umount:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Extra command-line options to pass to "borg umount".
|
||||||
example: "--extra-option"
|
example: "--extra-option"
|
||||||
description: |
|
description: |
|
||||||
Additional options to pass directly to particular Borg commands,
|
Additional options to pass directly to particular Borg commands,
|
||||||
@@ -1541,6 +1647,15 @@ properties:
|
|||||||
database hook implicitly enables read_special (see
|
database hook implicitly enables read_special (see
|
||||||
above) to support dump and restore streaming.
|
above) to support dump and restore streaming.
|
||||||
example: users
|
example: users
|
||||||
|
skip_names:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Database names to skip when dumping "all" databases.
|
||||||
|
Ignored when the database name is not "all".
|
||||||
|
example:
|
||||||
|
- cache
|
||||||
label:
|
label:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
@@ -1723,6 +1838,15 @@ properties:
|
|||||||
database hook implicitly enables read_special (see
|
database hook implicitly enables read_special (see
|
||||||
above) to support dump and restore streaming.
|
above) to support dump and restore streaming.
|
||||||
example: users
|
example: users
|
||||||
|
skip_names:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Database names to skip when dumping "all" databases.
|
||||||
|
Ignored when the database name is not "all".
|
||||||
|
example:
|
||||||
|
- cache
|
||||||
label:
|
label:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
@@ -2139,17 +2263,17 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
The message body to publish.
|
The message body to publish.
|
||||||
example: Your backups have failed.
|
example: Your backups have started.
|
||||||
priority:
|
priority:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
The priority to set.
|
The priority to set.
|
||||||
example: urgent
|
example: min
|
||||||
tags:
|
tags:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Tags to attach to the message.
|
Tags to attach to the message.
|
||||||
example: incoming_envelope
|
example: borgmatic
|
||||||
finish:
|
finish:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
@@ -2163,17 +2287,17 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
The message body to publish.
|
The message body to publish.
|
||||||
example: Your backups have failed.
|
example: Your backups have finished.
|
||||||
priority:
|
priority:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
The priority to set.
|
The priority to set.
|
||||||
example: urgent
|
example: min
|
||||||
tags:
|
tags:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Tags to attach to the message.
|
Tags to attach to the message.
|
||||||
example: incoming_envelope
|
example: borgmatic,+1
|
||||||
fail:
|
fail:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
@@ -2192,12 +2316,12 @@ properties:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
The priority to set.
|
The priority to set.
|
||||||
example: urgent
|
example: max
|
||||||
tags:
|
tags:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Tags to attach to the message.
|
Tags to attach to the message.
|
||||||
example: incoming_envelope
|
example: borgmatic,-1,skull
|
||||||
states:
|
states:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -2898,6 +3022,12 @@ properties:
|
|||||||
project monitor. Used along with the data source name URL to
|
project monitor. Used along with the data source name URL to
|
||||||
construct a cron URL.
|
construct a cron URL.
|
||||||
example: mymonitor
|
example: mymonitor
|
||||||
|
environment:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Sentry monitor environment used in the call to Sentry. If
|
||||||
|
not set, the Sentry default is used.
|
||||||
|
example: production
|
||||||
states:
|
states:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -2951,7 +3081,8 @@ properties:
|
|||||||
findmnt_command:
|
findmnt_command:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Command to use instead of "findmnt".
|
Deprecated and unused. Was the command to use instead of
|
||||||
|
"findmnt".
|
||||||
example: /usr/local/bin/findmnt
|
example: /usr/local/bin/findmnt
|
||||||
description: |
|
description: |
|
||||||
Configuration for integration with the Btrfs filesystem.
|
Configuration for integration with the Btrfs filesystem.
|
||||||
|
|||||||
@@ -158,8 +158,8 @@ def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path, b
|
|||||||
if not line or not ready_process:
|
if not line or not ready_process:
|
||||||
break
|
break
|
||||||
|
|
||||||
# Keep the last few lines of output in case the process errors, and we need the output for
|
# Keep the last few lines of output in case the process errors, and we need the
|
||||||
# the exception below.
|
# output for the exception below.
|
||||||
append_last_lines(
|
append_last_lines(
|
||||||
buffer_last_lines[ready_buffer],
|
buffer_last_lines[ready_buffer],
|
||||||
captured_outputs[ready_process],
|
captured_outputs[ready_process],
|
||||||
@@ -199,7 +199,7 @@ def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path, b
|
|||||||
last_lines,
|
last_lines,
|
||||||
captured_outputs[process],
|
captured_outputs[process],
|
||||||
line,
|
line,
|
||||||
output_log_level=logging.ERROR,
|
output_log_level,
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT:
|
if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import os
|
|||||||
|
|
||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -58,28 +59,31 @@ def dump_data_sources(
|
|||||||
manifest_file,
|
manifest_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
patterns.extend(
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
borgmatic.borg.pattern.Pattern(
|
patterns,
|
||||||
config_path,
|
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
|
||||||
)
|
|
||||||
for config_path in config_paths
|
|
||||||
)
|
|
||||||
patterns.append(
|
|
||||||
borgmatic.borg.pattern.Pattern(
|
borgmatic.borg.pattern.Pattern(
|
||||||
os.path.join(borgmatic_runtime_directory, 'bootstrap'),
|
os.path.join(borgmatic_runtime_directory, 'bootstrap'),
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for config_path in config_paths:
|
||||||
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
|
patterns,
|
||||||
|
borgmatic.borg.pattern.Pattern(
|
||||||
|
config_path,
|
||||||
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, dry_run):
|
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, patterns, dry_run):
|
||||||
'''
|
'''
|
||||||
Given a bootstrap configuration dict, a configuration dict, the borgmatic runtime directory, and
|
Given a bootstrap configuration dict, a configuration dict, the borgmatic runtime directory, the
|
||||||
whether this is a dry run, then remove the manifest file created above. If this is a dry run,
|
configured patterns, and whether this is a dry run, then remove the manifest file created above.
|
||||||
then don't actually remove anything.
|
If this is a dry run, then don't actually remove anything.
|
||||||
'''
|
'''
|
||||||
dry_run_label = ' (dry run; not actually removing anything)' if dry_run else ''
|
dry_run_label = ' (dry run; not actually removing anything)' if dry_run else ''
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import collections
|
import collections
|
||||||
|
import functools
|
||||||
import glob
|
import glob
|
||||||
import itertools
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.execute
|
import borgmatic.execute
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
import borgmatic.hooks.data_source.snapshot
|
import borgmatic.hooks.data_source.snapshot
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -22,93 +23,44 @@ def use_streaming(hook_config, config): # pragma: no cover
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_contained_subvolume_paths(btrfs_command, subvolume_path):
|
@functools.cache
|
||||||
|
def path_is_a_subvolume(btrfs_command, path):
|
||||||
'''
|
'''
|
||||||
Given the path of a Btrfs subvolume, return it in a sequence along with the paths of its
|
Given a btrfs command and a path, return whether the path is a Btrfs subvolume. Return False if
|
||||||
contained subvolumes.
|
the btrfs command errors, which probably indicates there isn't a containing Btrfs subvolume for
|
||||||
|
the given path.
|
||||||
|
|
||||||
If the btrfs command errors, log that error and return an empty sequence.
|
As a performance optimization, multiple calls to this function with the same arguments are
|
||||||
|
cached.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
btrfs_output = borgmatic.execute.execute_command_and_capture_output(
|
borgmatic.execute.execute_command(
|
||||||
(
|
(
|
||||||
*btrfs_command.split(' '),
|
*btrfs_command.split(' '),
|
||||||
'subvolume',
|
'subvolume',
|
||||||
'list',
|
'show',
|
||||||
subvolume_path,
|
path,
|
||||||
),
|
),
|
||||||
|
output_log_level=None,
|
||||||
close_fds=True,
|
close_fds=True,
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError as error:
|
# An error from the command (probably) indicates that the path is not actually a subvolume.
|
||||||
logger.debug(
|
except subprocess.CalledProcessError:
|
||||||
f'Ignoring Btrfs subvolume {subvolume_path} because of error listing its subvolumes: {error}',
|
return False
|
||||||
)
|
|
||||||
|
|
||||||
return ()
|
return True
|
||||||
|
|
||||||
return (
|
|
||||||
subvolume_path,
|
|
||||||
*tuple(
|
|
||||||
os.path.join(subvolume_path, line.split(' ')[-1])
|
|
||||||
for line in btrfs_output.splitlines()
|
|
||||||
if line.strip()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
FINDMNT_BTRFS_ROOT_SUBVOLUME_OPTION = 'subvolid=5'
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_subvolume_paths(btrfs_command, findmnt_command):
|
|
||||||
'''
|
|
||||||
Given btrfs and findmnt commands to run, get the sorted paths for all Btrfs subvolumes on the
|
|
||||||
system.
|
|
||||||
'''
|
|
||||||
findmnt_output = borgmatic.execute.execute_command_and_capture_output(
|
|
||||||
(
|
|
||||||
*findmnt_command.split(' '),
|
|
||||||
'-t', # Filesystem type.
|
|
||||||
'btrfs',
|
|
||||||
'--json',
|
|
||||||
'--list', # Request a flat list instead of a nested subvolume hierarchy.
|
|
||||||
),
|
|
||||||
close_fds=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return tuple(
|
|
||||||
sorted(
|
|
||||||
itertools.chain.from_iterable(
|
|
||||||
# If findmnt gave us a Btrfs root filesystem, list the subvolumes within it.
|
|
||||||
# This is necessary because findmnt only returns a subvolume's mount point
|
|
||||||
# rather than its original subvolume path (which can differ). For instance,
|
|
||||||
# a subvolume might exist at /mnt/subvolume but be mounted at /home/myuser.
|
|
||||||
# findmnt is still useful though because it's a global way to discover all
|
|
||||||
# Btrfs subvolumes—even if we have to do some additional legwork ourselves.
|
|
||||||
(
|
|
||||||
get_contained_subvolume_paths(btrfs_command, filesystem['target'])
|
|
||||||
if FINDMNT_BTRFS_ROOT_SUBVOLUME_OPTION in filesystem['options'].split(',')
|
|
||||||
else (filesystem['target'],)
|
|
||||||
)
|
|
||||||
for filesystem in json.loads(findmnt_output)['filesystems']
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
except json.JSONDecodeError as error:
|
|
||||||
raise ValueError(f'Invalid {findmnt_command} JSON output: {error}')
|
|
||||||
except KeyError as error:
|
|
||||||
raise ValueError(f'Invalid {findmnt_command} output: Missing key "{error}"')
|
|
||||||
|
|
||||||
|
|
||||||
Subvolume = collections.namedtuple('Subvolume', ('path', 'contained_patterns'), defaults=((),))
|
|
||||||
|
|
||||||
|
|
||||||
|
@functools.cache
|
||||||
def get_subvolume_property(btrfs_command, subvolume_path, property_name):
|
def get_subvolume_property(btrfs_command, subvolume_path, property_name):
|
||||||
'''
|
'''
|
||||||
Given a btrfs command, a subvolume path, and a property name to lookup, return the value of the
|
Given a btrfs command, a subvolume path, and a property name to lookup, return the value of the
|
||||||
corresponding property.
|
corresponding property.
|
||||||
|
|
||||||
Raise subprocess.CalledProcessError if the btrfs command errors.
|
Raise subprocess.CalledProcessError if the btrfs command errors.
|
||||||
|
|
||||||
|
As a performance optimization, multiple calls to this function with the same arguments are
|
||||||
|
cached.
|
||||||
'''
|
'''
|
||||||
output = borgmatic.execute.execute_command_and_capture_output(
|
output = borgmatic.execute.execute_command_and_capture_output(
|
||||||
(
|
(
|
||||||
@@ -134,37 +86,71 @@ def get_subvolume_property(btrfs_command, subvolume_path, property_name):
|
|||||||
}.get(value, value)
|
}.get(value, value)
|
||||||
|
|
||||||
|
|
||||||
def omit_read_only_subvolume_paths(btrfs_command, subvolume_paths):
|
def get_containing_subvolume_path(btrfs_command, path):
|
||||||
'''
|
'''
|
||||||
Given a Btrfs command to run and a sequence of Btrfs subvolume paths, filter them down to just
|
Given a btrfs command and a path, return the subvolume path that contains the given path (or is
|
||||||
those that are read-write. The idea is that Btrfs can't actually snapshot a read-only subvolume,
|
the same as the path).
|
||||||
so we should just ignore them.
|
|
||||||
|
If there is no such subvolume path or the containing subvolume is read-only, return None.
|
||||||
'''
|
'''
|
||||||
retained_subvolume_paths = []
|
# Probe the given pattern's path and all of its parents, grandparents, etc. to try to find a
|
||||||
|
# Btrfs subvolume.
|
||||||
|
for candidate_path in (
|
||||||
|
path,
|
||||||
|
*tuple(str(ancestor) for ancestor in pathlib.PurePath(path).parents),
|
||||||
|
):
|
||||||
|
if not path_is_a_subvolume(btrfs_command, candidate_path):
|
||||||
|
continue
|
||||||
|
|
||||||
for subvolume_path in subvolume_paths:
|
|
||||||
try:
|
try:
|
||||||
if get_subvolume_property(btrfs_command, subvolume_path, 'ro'):
|
if get_subvolume_property(btrfs_command, candidate_path, 'ro'):
|
||||||
logger.debug(f'Ignoring Btrfs subvolume {subvolume_path} because it is read-only')
|
logger.debug(f'Ignoring Btrfs subvolume {candidate_path} because it is read-only')
|
||||||
else:
|
|
||||||
retained_subvolume_paths.append(subvolume_path)
|
return None
|
||||||
except subprocess.CalledProcessError as error: # noqa: PERF203
|
|
||||||
|
logger.debug(f'Path {candidate_path} is a Btrfs subvolume')
|
||||||
|
|
||||||
|
return candidate_path
|
||||||
|
except subprocess.CalledProcessError as error:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'Error determining read-only status of Btrfs subvolume {subvolume_path}: {error}',
|
f'Error determining read-only status of Btrfs subvolume {candidate_path}: {error}',
|
||||||
)
|
)
|
||||||
|
|
||||||
return tuple(retained_subvolume_paths)
|
return None
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_subvolumes(btrfs_command, findmnt_command, patterns=None):
|
def get_all_subvolume_paths(btrfs_command, patterns):
|
||||||
|
'''
|
||||||
|
Given a btrfs command and a sequence of patterns, get the sorted paths for all Btrfs subvolumes
|
||||||
|
containing those patterns.
|
||||||
|
'''
|
||||||
|
return tuple(
|
||||||
|
sorted(
|
||||||
|
{
|
||||||
|
subvolume_path
|
||||||
|
for pattern in patterns
|
||||||
|
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||||
|
if pattern.source == borgmatic.borg.pattern.Pattern_source.CONFIG
|
||||||
|
for subvolume_path in (get_containing_subvolume_path(btrfs_command, pattern.path),)
|
||||||
|
if subvolume_path
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Subvolume = collections.namedtuple('Subvolume', ('path', 'contained_patterns'), defaults=((),))
|
||||||
|
|
||||||
|
|
||||||
|
def get_subvolumes(btrfs_command, patterns):
|
||||||
'''
|
'''
|
||||||
Given a Btrfs command to run and a sequence of configured patterns, find the intersection
|
Given a Btrfs command to run and a sequence of configured patterns, find the intersection
|
||||||
between the current Btrfs filesystem and subvolume paths and the paths of any patterns. The
|
between the current Btrfs filesystem and subvolume paths and the paths of any patterns. The
|
||||||
idea is that these pattern paths represent the requested subvolumes to snapshot.
|
idea is that these pattern paths represent the requested subvolumes to snapshot.
|
||||||
|
|
||||||
Only include subvolumes that contain at least one root pattern sourced from borgmatic
|
Only include subvolumes that contain at least one root pattern sourced from borgmatic
|
||||||
configuration (as opposed to generated elsewhere in borgmatic). But if patterns is None, then
|
configuration (as opposed to generated elsewhere in borgmatic).
|
||||||
return all subvolumes instead, sorted by path.
|
|
||||||
|
|
||||||
Return the result as a sequence of matching Subvolume instances.
|
Return the result as a sequence of matching Subvolume instances.
|
||||||
'''
|
'''
|
||||||
@@ -172,15 +158,10 @@ def get_subvolumes(btrfs_command, findmnt_command, patterns=None):
|
|||||||
subvolumes = []
|
subvolumes = []
|
||||||
|
|
||||||
# For each subvolume path, match it against the given patterns to find the subvolumes to
|
# For each subvolume path, match it against the given patterns to find the subvolumes to
|
||||||
# backup. Sort the subvolumes from longest to shortest mount points, so longer mount points get
|
# backup. Sort the subvolumes from longest to shortest mount points, so longer subvolumes get
|
||||||
# a whack at the candidate pattern piñata before their parents do. (Patterns are consumed during
|
# a whack at the candidate pattern piñata before their parents do. (Patterns are consumed during
|
||||||
# this process, so no two subvolumes end up with the same contained patterns.)
|
# this process, so no two subvolumes end up with the same contained patterns.)
|
||||||
for subvolume_path in reversed(
|
for subvolume_path in reversed(get_all_subvolume_paths(btrfs_command, patterns)):
|
||||||
omit_read_only_subvolume_paths(
|
|
||||||
btrfs_command,
|
|
||||||
get_all_subvolume_paths(btrfs_command, findmnt_command),
|
|
||||||
),
|
|
||||||
):
|
|
||||||
subvolumes.extend(
|
subvolumes.extend(
|
||||||
Subvolume(subvolume_path, contained_patterns)
|
Subvolume(subvolume_path, contained_patterns)
|
||||||
for contained_patterns in (
|
for contained_patterns in (
|
||||||
@@ -189,8 +170,7 @@ def get_subvolumes(btrfs_command, findmnt_command, patterns=None):
|
|||||||
candidate_patterns,
|
candidate_patterns,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if patterns is None
|
if any(
|
||||||
or any(
|
|
||||||
pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||||
and pattern.source == borgmatic.borg.pattern.Pattern_source.CONFIG
|
and pattern.source == borgmatic.borg.pattern.Pattern_source.CONFIG
|
||||||
for pattern in contained_patterns
|
for pattern in contained_patterns
|
||||||
@@ -325,11 +305,15 @@ def dump_data_sources(
|
|||||||
dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
|
dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
|
||||||
logger.info(f'Snapshotting Btrfs subvolumes{dry_run_label}')
|
logger.info(f'Snapshotting Btrfs subvolumes{dry_run_label}')
|
||||||
|
|
||||||
|
if 'findmnt_command' in hook_config:
|
||||||
|
logger.warning(
|
||||||
|
'The Btrfs "findmnt_command" option is deprecated and will be removed from a future release; findmnt is no longer used',
|
||||||
|
)
|
||||||
|
|
||||||
# Based on the configured patterns, determine Btrfs subvolumes to backup. Only consider those
|
# Based on the configured patterns, determine Btrfs subvolumes to backup. Only consider those
|
||||||
# patterns that came from actual user configuration (as opposed to, say, other hooks).
|
# patterns that came from actual user configuration (as opposed to, say, other hooks).
|
||||||
btrfs_command = hook_config.get('btrfs_command', 'btrfs')
|
btrfs_command = hook_config.get('btrfs_command', 'btrfs')
|
||||||
findmnt_command = hook_config.get('findmnt_command', 'findmnt')
|
subvolumes = get_subvolumes(btrfs_command, patterns)
|
||||||
subvolumes = get_subvolumes(btrfs_command, findmnt_command, patterns)
|
|
||||||
|
|
||||||
if not subvolumes:
|
if not subvolumes:
|
||||||
logger.warning(f'No Btrfs subvolumes found to snapshot{dry_run_label}')
|
logger.warning(f'No Btrfs subvolumes found to snapshot{dry_run_label}')
|
||||||
@@ -345,16 +329,19 @@ def dump_data_sources(
|
|||||||
|
|
||||||
snapshot_subvolume(btrfs_command, subvolume.path, snapshot_path)
|
snapshot_subvolume(btrfs_command, subvolume.path, snapshot_path)
|
||||||
|
|
||||||
|
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
|
||||||
|
patterns, subvolume.contained_patterns
|
||||||
|
)
|
||||||
|
|
||||||
for pattern in subvolume.contained_patterns:
|
for pattern in subvolume.contained_patterns:
|
||||||
snapshot_pattern = make_borg_snapshot_pattern(subvolume.path, pattern)
|
snapshot_pattern = make_borg_snapshot_pattern(subvolume.path, pattern)
|
||||||
|
borgmatic.hooks.data_source.config.replace_pattern(
|
||||||
|
patterns, pattern, snapshot_pattern, last_contained_pattern_index
|
||||||
|
)
|
||||||
|
|
||||||
# Attempt to update the pattern in place, since pattern order matters to Borg.
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
try:
|
patterns, make_snapshot_exclude_pattern(subvolume.path)
|
||||||
patterns[patterns.index(pattern)] = snapshot_pattern
|
)
|
||||||
except ValueError:
|
|
||||||
patterns.append(snapshot_pattern)
|
|
||||||
|
|
||||||
patterns.append(make_snapshot_exclude_pattern(subvolume.path))
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -375,11 +362,12 @@ def delete_snapshot(btrfs_command, snapshot_path): # pragma: no cover
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, dry_run):
|
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, patterns, dry_run):
|
||||||
'''
|
'''
|
||||||
Given a Btrfs configuration dict, a configuration dict, the borgmatic runtime directory, and
|
Given a Btrfs configuration dict, a configuration dict, the borgmatic runtime directory, the
|
||||||
whether this is a dry run, delete any Btrfs snapshots created by borgmatic. If this is a dry run
|
configured patterns, and whether this is a dry run, delete any Btrfs snapshots created by
|
||||||
or Btrfs isn't configured in borgmatic's configuration, then don't actually remove anything.
|
borgmatic. If this is a dry run or Btrfs isn't configured in borgmatic's configuration, then
|
||||||
|
don't actually remove anything.
|
||||||
'''
|
'''
|
||||||
if hook_config is None:
|
if hook_config is None:
|
||||||
return
|
return
|
||||||
@@ -387,10 +375,9 @@ def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, d
|
|||||||
dry_run_label = ' (dry run; not actually removing anything)' if dry_run else ''
|
dry_run_label = ' (dry run; not actually removing anything)' if dry_run else ''
|
||||||
|
|
||||||
btrfs_command = hook_config.get('btrfs_command', 'btrfs')
|
btrfs_command = hook_config.get('btrfs_command', 'btrfs')
|
||||||
findmnt_command = hook_config.get('findmnt_command', 'findmnt')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
all_subvolumes = get_subvolumes(btrfs_command, findmnt_command)
|
all_subvolumes = get_subvolumes(btrfs_command, patterns)
|
||||||
except FileNotFoundError as error:
|
except FileNotFoundError as error:
|
||||||
logger.debug(f'Could not find "{error.filename}" command')
|
logger.debug(f'Could not find "{error.filename}" command')
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import contextlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
import borgmatic.borg.pattern
|
||||||
from borgmatic.execute import execute_command_and_capture_output
|
from borgmatic.execute import execute_command_and_capture_output
|
||||||
|
|
||||||
IS_A_HOOK = False
|
IS_A_HOOK = False
|
||||||
@@ -102,3 +104,105 @@ def get_ip_from_container(container):
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Could not determine ip address for container '{container}'; running in host mode or userspace networking?"
|
f"Could not determine ip address for container '{container}'; running in host mode or userspace networking?"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def inject_pattern(patterns, data_source_pattern):
|
||||||
|
'''
|
||||||
|
Given a list of borgmatic.borg.pattern.Pattern instances representing the configured patterns,
|
||||||
|
insert the given data source pattern at the start of the list. The idea is that borgmatic is
|
||||||
|
injecting its own custom pattern specific to a data source hook into the user's configured
|
||||||
|
patterns so that the hook's data gets included in the backup.
|
||||||
|
|
||||||
|
As part of this injection, if the data source pattern is a root pattern, also insert an
|
||||||
|
"include" version of the given root pattern, in an attempt to preempt any of the user's
|
||||||
|
configured exclude patterns that may follow.
|
||||||
|
'''
|
||||||
|
if data_source_pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||||
|
patterns.insert(
|
||||||
|
0,
|
||||||
|
borgmatic.borg.pattern.Pattern(
|
||||||
|
path=data_source_pattern.path,
|
||||||
|
type=borgmatic.borg.pattern.Pattern_type.INCLUDE,
|
||||||
|
style=data_source_pattern.style,
|
||||||
|
device=data_source_pattern.device,
|
||||||
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
patterns.insert(0, data_source_pattern)
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_pattern_index(patterns, patterns_subset):
|
||||||
|
'''
|
||||||
|
Given a sequence of all patterns and a subset of those patterns, find each subset pattern in the
|
||||||
|
all patterns sequence and return the highest (last) index.
|
||||||
|
'''
|
||||||
|
last_pattern_index = 0
|
||||||
|
|
||||||
|
for pattern in patterns_subset:
|
||||||
|
with contextlib.suppress(ValueError):
|
||||||
|
last_pattern_index = max(patterns.index(pattern), last_pattern_index)
|
||||||
|
|
||||||
|
return last_pattern_index
|
||||||
|
|
||||||
|
|
||||||
|
def replace_pattern(
|
||||||
|
patterns, pattern_to_replace, data_source_pattern, last_contained_pattern_index
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
Given a list of borgmatic.borg.pattern.Pattern instances representing the configured patterns,
|
||||||
|
replace the given pattern with the given data source pattern. The idea is that borgmatic is
|
||||||
|
replacing a configured pattern with its own modified pattern specific to a data source hook so
|
||||||
|
that the hook's data gets included in the backup.
|
||||||
|
|
||||||
|
As part of this replacement, if the data source pattern is a root pattern, also insert an
|
||||||
|
"include" version of the given root pattern right after the given last contained pattern index
|
||||||
|
in an attempt to preempt any of the user's configured global exclude patterns that may follow.
|
||||||
|
But we don't want to preempt any intentional partial excludes of the data source pattern itself,
|
||||||
|
which is why the include goes after the last contained pattern index.
|
||||||
|
|
||||||
|
For instance, let's say that the patterns are effectively:
|
||||||
|
|
||||||
|
R /foo
|
||||||
|
R /bar
|
||||||
|
- /bar/.cache
|
||||||
|
R /baz
|
||||||
|
- **
|
||||||
|
|
||||||
|
... and "R /bar" is the pattern to replace, data source pattern is "R /bar/snapshot", and the
|
||||||
|
last contained pattern index is 2 (corresponding to "- /bar/.cache"). The resulting patterns
|
||||||
|
after calling this function would be:
|
||||||
|
|
||||||
|
R /foo
|
||||||
|
R /bar/snapshot
|
||||||
|
- /bar/snapshot/.cache
|
||||||
|
+ /bar/snapshot
|
||||||
|
R /baz
|
||||||
|
- **
|
||||||
|
|
||||||
|
Note that the positioning of "+ /bar/snapshot" means that it overrides the "- **" global exclude
|
||||||
|
but not the "- /bar/snapshot/.cache" contained pattern exclude.
|
||||||
|
|
||||||
|
If the pattern to replace can't be found in the given patterns, then just inject the data source
|
||||||
|
pattern at the start of the list.
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
index = patterns.index(pattern_to_replace)
|
||||||
|
except ValueError:
|
||||||
|
inject_pattern(patterns, data_source_pattern)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
patterns[index] = data_source_pattern
|
||||||
|
|
||||||
|
if data_source_pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||||
|
patterns.insert(
|
||||||
|
last_contained_pattern_index + 1,
|
||||||
|
borgmatic.borg.pattern.Pattern(
|
||||||
|
path=data_source_pattern.path,
|
||||||
|
type=borgmatic.borg.pattern.Pattern_type.INCLUDE,
|
||||||
|
style=data_source_pattern.style,
|
||||||
|
device=data_source_pattern.device,
|
||||||
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import subprocess
|
|||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.execute
|
import borgmatic.execute
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
import borgmatic.hooks.data_source.snapshot
|
import borgmatic.hooks.data_source.snapshot
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -268,6 +269,10 @@ def dump_data_sources(
|
|||||||
snapshot_mount_path,
|
snapshot_mount_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
|
||||||
|
patterns, logical_volume.contained_patterns
|
||||||
|
)
|
||||||
|
|
||||||
for pattern in logical_volume.contained_patterns:
|
for pattern in logical_volume.contained_patterns:
|
||||||
snapshot_pattern = make_borg_snapshot_pattern(
|
snapshot_pattern = make_borg_snapshot_pattern(
|
||||||
pattern,
|
pattern,
|
||||||
@@ -275,11 +280,9 @@ def dump_data_sources(
|
|||||||
normalized_runtime_directory,
|
normalized_runtime_directory,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Attempt to update the pattern in place, since pattern order matters to Borg.
|
borgmatic.hooks.data_source.config.replace_pattern(
|
||||||
try:
|
patterns, pattern, snapshot_pattern, last_contained_pattern_index
|
||||||
patterns[patterns.index(pattern)] = snapshot_pattern
|
)
|
||||||
except ValueError:
|
|
||||||
patterns.append(snapshot_pattern)
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -353,12 +356,12 @@ def get_snapshots(lvs_command, snapshot_name=None):
|
|||||||
raise ValueError(f'Invalid {lvs_command} output: Missing key "{error}"')
|
raise ValueError(f'Invalid {lvs_command} output: Missing key "{error}"')
|
||||||
|
|
||||||
|
|
||||||
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, dry_run): # noqa: PLR0912
|
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, patterns, dry_run): # noqa: PLR0912
|
||||||
'''
|
'''
|
||||||
Given an LVM configuration dict, a configuration dict, the borgmatic runtime directory, and
|
Given an LVM configuration dict, a configuration dict, the borgmatic runtime directory, the
|
||||||
whether this is a dry run, unmount and delete any LVM snapshots created by borgmatic. If this is
|
configured patterns, and whether this is a dry run, unmount and delete any LVM snapshots created
|
||||||
a dry run or LVM isn't configured in borgmatic's configuration, then don't actually remove
|
by borgmatic. If this is a dry run or LVM isn't configured in borgmatic's configuration, then
|
||||||
anything.
|
don't actually remove anything.
|
||||||
'''
|
'''
|
||||||
if hook_config is None:
|
if hook_config is None:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import shlex
|
|||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.hooks.credential.parse
|
import borgmatic.hooks.credential.parse
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
from borgmatic.execute import (
|
from borgmatic.execute import (
|
||||||
execute_command,
|
execute_command,
|
||||||
execute_command_and_capture_output,
|
execute_command_and_capture_output,
|
||||||
@@ -112,7 +113,14 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
|||||||
names to dump. In the case of "all", query for the names of databases on the configured host and
|
names to dump. In the case of "all", query for the names of databases on the configured host and
|
||||||
return them, excluding any system databases that will cause problems during restore.
|
return them, excluding any system databases that will cause problems during restore.
|
||||||
'''
|
'''
|
||||||
|
skip_names = database.get('skip_names')
|
||||||
|
|
||||||
if database['name'] != 'all':
|
if database['name'] != 'all':
|
||||||
|
if skip_names:
|
||||||
|
logger.warning(
|
||||||
|
f'For MariaDB database {database["name"]}, ignoring the "skip_names" option, which is only supported for database "all"'
|
||||||
|
)
|
||||||
|
|
||||||
return (database['name'],)
|
return (database['name'],)
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
@@ -144,12 +152,16 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
|||||||
|
|
||||||
logger.debug('Querying for "all" MariaDB databases to dump')
|
logger.debug('Querying for "all" MariaDB databases to dump')
|
||||||
|
|
||||||
|
if skip_names:
|
||||||
|
logger.debug(f'Skipping database names: {", ".join(skip_names)}')
|
||||||
|
|
||||||
show_output = execute_command_and_capture_output(show_command, environment=environment)
|
show_output = execute_command_and_capture_output(show_command, environment=environment)
|
||||||
|
|
||||||
return tuple(
|
return tuple(
|
||||||
show_name
|
show_name
|
||||||
for show_name in show_output.strip().splitlines()
|
for show_name in show_output.strip().splitlines()
|
||||||
if show_name not in SYSTEM_DATABASE_NAMES
|
if show_name not in SYSTEM_DATABASE_NAMES
|
||||||
|
if not skip_names or show_name not in skip_names
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -355,7 +367,8 @@ def dump_data_sources(
|
|||||||
dump.write_data_source_dumps_metadata(
|
dump.write_data_source_dumps_metadata(
|
||||||
borgmatic_runtime_directory, 'mariadb_databases', dumps_metadata
|
borgmatic_runtime_directory, 'mariadb_databases', dumps_metadata
|
||||||
)
|
)
|
||||||
patterns.append(
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
|
patterns,
|
||||||
borgmatic.borg.pattern.Pattern(
|
borgmatic.borg.pattern.Pattern(
|
||||||
os.path.join(borgmatic_runtime_directory, 'mariadb_databases'),
|
os.path.join(borgmatic_runtime_directory, 'mariadb_databases'),
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
@@ -369,6 +382,7 @@ def remove_data_source_dumps(
|
|||||||
databases,
|
databases,
|
||||||
config,
|
config,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
dry_run,
|
dry_run,
|
||||||
): # pragma: no cover
|
): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import shlex
|
|||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.hooks.credential.parse
|
import borgmatic.hooks.credential.parse
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
from borgmatic.execute import execute_command, execute_command_with_processes
|
from borgmatic.execute import execute_command, execute_command_with_processes
|
||||||
from borgmatic.hooks.data_source import config as database_config
|
from borgmatic.hooks.data_source import config as database_config
|
||||||
from borgmatic.hooks.data_source import dump
|
from borgmatic.hooks.data_source import dump
|
||||||
@@ -100,7 +101,8 @@ def dump_data_sources(
|
|||||||
dump.write_data_source_dumps_metadata(
|
dump.write_data_source_dumps_metadata(
|
||||||
borgmatic_runtime_directory, 'mongodb_databases', dumps_metadata
|
borgmatic_runtime_directory, 'mongodb_databases', dumps_metadata
|
||||||
)
|
)
|
||||||
patterns.append(
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
|
patterns,
|
||||||
borgmatic.borg.pattern.Pattern(
|
borgmatic.borg.pattern.Pattern(
|
||||||
os.path.join(borgmatic_runtime_directory, 'mongodb_databases'),
|
os.path.join(borgmatic_runtime_directory, 'mongodb_databases'),
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
@@ -185,6 +187,7 @@ def remove_data_source_dumps(
|
|||||||
databases,
|
databases,
|
||||||
config,
|
config,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
dry_run,
|
dry_run,
|
||||||
): # pragma: no cover
|
): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import shlex
|
|||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.hooks.credential.parse
|
import borgmatic.hooks.credential.parse
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
import borgmatic.hooks.data_source.mariadb
|
import borgmatic.hooks.data_source.mariadb
|
||||||
from borgmatic.execute import (
|
from borgmatic.execute import (
|
||||||
execute_command,
|
execute_command,
|
||||||
@@ -35,7 +36,14 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
|||||||
names to dump. In the case of "all", query for the names of databases on the configured host and
|
names to dump. In the case of "all", query for the names of databases on the configured host and
|
||||||
return them, excluding any system databases that will cause problems during restore.
|
return them, excluding any system databases that will cause problems during restore.
|
||||||
'''
|
'''
|
||||||
|
skip_names = database.get('skip_names')
|
||||||
|
|
||||||
if database['name'] != 'all':
|
if database['name'] != 'all':
|
||||||
|
if skip_names:
|
||||||
|
logger.warning(
|
||||||
|
f'For MySQL database {database["name"]}, ignoring the "skip_names" option, which is only supported for database "all"'
|
||||||
|
)
|
||||||
|
|
||||||
return (database['name'],)
|
return (database['name'],)
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
@@ -73,12 +81,16 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
|||||||
|
|
||||||
logger.debug('Querying for "all" MySQL databases to dump')
|
logger.debug('Querying for "all" MySQL databases to dump')
|
||||||
|
|
||||||
|
if skip_names:
|
||||||
|
logger.debug(f'Skipping database names: {", ".join(skip_names)}')
|
||||||
|
|
||||||
show_output = execute_command_and_capture_output(show_command, environment=environment)
|
show_output = execute_command_and_capture_output(show_command, environment=environment)
|
||||||
|
|
||||||
return tuple(
|
return tuple(
|
||||||
show_name
|
show_name
|
||||||
for show_name in show_output.strip().splitlines()
|
for show_name in show_output.strip().splitlines()
|
||||||
if show_name not in SYSTEM_DATABASE_NAMES
|
if show_name not in SYSTEM_DATABASE_NAMES
|
||||||
|
if not skip_names or show_name not in skip_names
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -286,7 +298,8 @@ def dump_data_sources(
|
|||||||
dump.write_data_source_dumps_metadata(
|
dump.write_data_source_dumps_metadata(
|
||||||
borgmatic_runtime_directory, 'mysql_databases', dumps_metadata
|
borgmatic_runtime_directory, 'mysql_databases', dumps_metadata
|
||||||
)
|
)
|
||||||
patterns.append(
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
|
patterns,
|
||||||
borgmatic.borg.pattern.Pattern(
|
borgmatic.borg.pattern.Pattern(
|
||||||
os.path.join(borgmatic_runtime_directory, 'mysql_databases'),
|
os.path.join(borgmatic_runtime_directory, 'mysql_databases'),
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
@@ -300,6 +313,7 @@ def remove_data_source_dumps(
|
|||||||
databases,
|
databases,
|
||||||
config,
|
config,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
dry_run,
|
dry_run,
|
||||||
): # pragma: no cover
|
): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import shlex
|
|||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.hooks.credential.parse
|
import borgmatic.hooks.credential.parse
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
from borgmatic.execute import (
|
from borgmatic.execute import (
|
||||||
execute_command,
|
execute_command,
|
||||||
execute_command_and_capture_output,
|
execute_command_and_capture_output,
|
||||||
@@ -260,7 +261,8 @@ def dump_data_sources(
|
|||||||
dump.write_data_source_dumps_metadata(
|
dump.write_data_source_dumps_metadata(
|
||||||
borgmatic_runtime_directory, 'postgresql_databases', dumps_metadata
|
borgmatic_runtime_directory, 'postgresql_databases', dumps_metadata
|
||||||
)
|
)
|
||||||
patterns.append(
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
|
patterns,
|
||||||
borgmatic.borg.pattern.Pattern(
|
borgmatic.borg.pattern.Pattern(
|
||||||
os.path.join(borgmatic_runtime_directory, 'postgresql_databases'),
|
os.path.join(borgmatic_runtime_directory, 'postgresql_databases'),
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
@@ -274,6 +276,7 @@ def remove_data_source_dumps(
|
|||||||
databases,
|
databases,
|
||||||
config,
|
config,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
dry_run,
|
dry_run,
|
||||||
): # pragma: no cover
|
): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import shlex
|
|||||||
|
|
||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
from borgmatic.execute import execute_command, execute_command_with_processes
|
from borgmatic.execute import execute_command, execute_command_with_processes
|
||||||
from borgmatic.hooks.data_source import dump
|
from borgmatic.hooks.data_source import dump
|
||||||
|
|
||||||
@@ -106,7 +107,8 @@ def dump_data_sources(
|
|||||||
dump.write_data_source_dumps_metadata(
|
dump.write_data_source_dumps_metadata(
|
||||||
borgmatic_runtime_directory, 'sqlite_databases', dumps_metadata
|
borgmatic_runtime_directory, 'sqlite_databases', dumps_metadata
|
||||||
)
|
)
|
||||||
patterns.append(
|
borgmatic.hooks.data_source.config.inject_pattern(
|
||||||
|
patterns,
|
||||||
borgmatic.borg.pattern.Pattern(
|
borgmatic.borg.pattern.Pattern(
|
||||||
os.path.join(borgmatic_runtime_directory, 'sqlite_databases'),
|
os.path.join(borgmatic_runtime_directory, 'sqlite_databases'),
|
||||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||||
@@ -120,6 +122,7 @@ def remove_data_source_dumps(
|
|||||||
databases,
|
databases,
|
||||||
config,
|
config,
|
||||||
borgmatic_runtime_directory,
|
borgmatic_runtime_directory,
|
||||||
|
patterns,
|
||||||
dry_run,
|
dry_run,
|
||||||
): # pragma: no cover
|
): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import subprocess
|
|||||||
import borgmatic.borg.pattern
|
import borgmatic.borg.pattern
|
||||||
import borgmatic.config.paths
|
import borgmatic.config.paths
|
||||||
import borgmatic.execute
|
import borgmatic.execute
|
||||||
|
import borgmatic.hooks.data_source.config
|
||||||
import borgmatic.hooks.data_source.snapshot
|
import borgmatic.hooks.data_source.snapshot
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -299,6 +300,10 @@ def dump_data_sources(
|
|||||||
snapshot_mount_path,
|
snapshot_mount_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
|
||||||
|
patterns, dataset.contained_patterns
|
||||||
|
)
|
||||||
|
|
||||||
for pattern in dataset.contained_patterns:
|
for pattern in dataset.contained_patterns:
|
||||||
snapshot_pattern = make_borg_snapshot_pattern(
|
snapshot_pattern = make_borg_snapshot_pattern(
|
||||||
pattern,
|
pattern,
|
||||||
@@ -306,11 +311,9 @@ def dump_data_sources(
|
|||||||
normalized_runtime_directory,
|
normalized_runtime_directory,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Attempt to update the pattern in place, since pattern order matters to Borg.
|
borgmatic.hooks.data_source.config.replace_pattern(
|
||||||
try:
|
patterns, pattern, snapshot_pattern, last_contained_pattern_index
|
||||||
patterns[patterns.index(pattern)] = snapshot_pattern
|
)
|
||||||
except ValueError:
|
|
||||||
patterns.append(snapshot_pattern)
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -363,12 +366,12 @@ def get_all_snapshots(zfs_command):
|
|||||||
return tuple(line.rstrip() for line in list_output.splitlines())
|
return tuple(line.rstrip() for line in list_output.splitlines())
|
||||||
|
|
||||||
|
|
||||||
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, dry_run): # noqa: PLR0912
|
def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, patterns, dry_run): # noqa: PLR0912
|
||||||
'''
|
'''
|
||||||
Given a ZFS configuration dict, a configuration dict, the borgmatic runtime directory, and
|
Given a ZFS configuration dict, a configuration dict, the borgmatic runtime directory, the
|
||||||
whether this is a dry run, unmount and destroy any ZFS snapshots created by borgmatic. If this
|
configured patterns, and whether this is a dry run, unmount and destroy any ZFS snapshots
|
||||||
is a dry run or ZFS isn't configured in borgmatic's configuration, then don't actually remove
|
created by borgmatic. If this is a dry run or ZFS isn't configured in borgmatic's configuration,
|
||||||
anything.
|
then don't actually remove anything.
|
||||||
'''
|
'''
|
||||||
if hook_config is None:
|
if hook_config is None:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -65,12 +65,13 @@ class Loki_log_buffer:
|
|||||||
# Skip as there are not logs to send yet
|
# Skip as there are not logs to send yet
|
||||||
return
|
return
|
||||||
|
|
||||||
|
request_body = self.to_request()
|
||||||
self.root['streams'][0]['values'] = []
|
self.root['streams'][0]['values'] = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = requests.post(
|
result = requests.post(
|
||||||
self.url,
|
self.url,
|
||||||
data=self.to_request(),
|
data=request_body,
|
||||||
timeout=TIMEOUT_SECONDS,
|
timeout=TIMEOUT_SECONDS,
|
||||||
headers={
|
headers={
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import urllib
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
|
|||||||
dry_run_label = ' (dry run; not actually pinging)' if dry_run else ''
|
dry_run_label = ' (dry run; not actually pinging)' if dry_run else ''
|
||||||
|
|
||||||
data_source_name_url = hook_config.get('data_source_name_url')
|
data_source_name_url = hook_config.get('data_source_name_url')
|
||||||
|
environment = hook_config.get('environment')
|
||||||
monitor_slug = hook_config.get('monitor_slug')
|
monitor_slug = hook_config.get('monitor_slug')
|
||||||
match = DATA_SOURCE_NAME_URL_PATTERN.match(data_source_name_url)
|
match = DATA_SOURCE_NAME_URL_PATTERN.match(data_source_name_url)
|
||||||
|
|
||||||
@@ -65,10 +67,12 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
|
|||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
environment_query = f'&environment={urllib.parse.quote(environment)}' if environment else ''
|
||||||
|
|
||||||
logging.getLogger('urllib3').setLevel(logging.ERROR)
|
logging.getLogger('urllib3').setLevel(logging.ERROR)
|
||||||
try:
|
try:
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
f'{cron_url}?status={status}',
|
f'{cron_url}?status={status}{environment_query}',
|
||||||
timeout=TIMEOUT_SECONDS,
|
timeout=TIMEOUT_SECONDS,
|
||||||
headers={'User-Agent': 'borgmatic'},
|
headers={'User-Agent': 'borgmatic'},
|
||||||
)
|
)
|
||||||
|
|||||||
+9
-9
@@ -2,11 +2,11 @@ FROM docker.io/alpine:3.20.1 AS borgmatic
|
|||||||
|
|
||||||
COPY . /app
|
COPY . /app
|
||||||
RUN apk add --no-cache py3-pip py3-ruamel.yaml py3-ruamel.yaml.clib
|
RUN apk add --no-cache py3-pip py3-ruamel.yaml py3-ruamel.yaml.clib
|
||||||
RUN pip install --break-system-packages --no-cache /app && borgmatic config generate && chmod +r /etc/borgmatic/config.yaml
|
RUN pip install --break-system-packages --no-cache /app && borgmatic config generate && borgmatic config generate --destination /etc/borgmatic --split && chmod +r /etc/borgmatic/*.yaml
|
||||||
RUN borgmatic --help > /command-line.txt \
|
RUN mkdir /command-line \
|
||||||
|
&& borgmatic --help > /command-line/global.txt \
|
||||||
&& for action in repo-create transfer create prune compact check delete extract config "config bootstrap" "config generate" "config validate" export-tar mount umount repo-delete restore repo-list list repo-info info break-lock "key export" "key import" "key change-passphrase" recreate borg; do \
|
&& for action in repo-create transfer create prune compact check delete extract config "config bootstrap" "config generate" "config validate" export-tar mount umount repo-delete restore repo-list list repo-info info break-lock "key export" "key import" "key change-passphrase" recreate borg; do \
|
||||||
echo -e "\n--------------------------------------------------------------------------------\n" >> /command-line.txt \
|
borgmatic $action --help > /command-line/${action/ /-}.txt; done
|
||||||
&& borgmatic $action --help >> /command-line.txt; done
|
|
||||||
RUN /app/docs/fetch-contributors >> /contributors.html
|
RUN /app/docs/fetch-contributors >> /contributors.html
|
||||||
|
|
||||||
FROM docker.io/node:22.4.0-alpine AS html
|
FROM docker.io/node:22.4.0-alpine AS html
|
||||||
@@ -23,14 +23,14 @@ RUN npm install @11ty/eleventy \
|
|||||||
markdown-it \
|
markdown-it \
|
||||||
markdown-it-anchor \
|
markdown-it-anchor \
|
||||||
markdown-it-replace-link
|
markdown-it-replace-link
|
||||||
COPY --from=borgmatic /etc/borgmatic/config.yaml /source/docs/_includes/borgmatic/config.yaml
|
COPY --from=borgmatic /etc/borgmatic/* /source/docs/_includes/borgmatic/
|
||||||
COPY --from=borgmatic /command-line.txt /source/docs/_includes/borgmatic/command-line.txt
|
COPY --from=borgmatic /command-line/* /source/docs/_includes/borgmatic/command-line/
|
||||||
COPY --from=borgmatic /contributors.html /source/docs/_includes/borgmatic/contributors.html
|
COPY --from=borgmatic /contributors.html /source/docs/_includes/borgmatic/contributors.html
|
||||||
COPY . /source
|
COPY . /source
|
||||||
RUN NODE_ENV=${ENVIRONMENT} npx eleventy --input=/source/docs --output=/output/docs \
|
RUN NODE_ENV=${ENVIRONMENT} npx eleventy --input=/source/docs --output=/output
|
||||||
&& mv /output/docs/index.html /output/index.html
|
RUN npx -y pagefind --site /output
|
||||||
|
|
||||||
FROM docker.io/nginx:1.26.1-alpine
|
FROM docker.io/nginx:1.26.1-alpine
|
||||||
|
|
||||||
COPY --from=html /output /usr/share/nginx/html
|
COPY --from=html /output /usr/share/nginx/html
|
||||||
COPY --from=borgmatic /etc/borgmatic/config.yaml /usr/share/nginx/html/docs/reference/config.yaml
|
COPY --from=borgmatic /etc/borgmatic/config.yaml /usr/share/nginx/html/reference/config.yaml
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
.asciicast > iframe {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
@@ -48,9 +48,20 @@
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
padding-left: 0.625rem; /* 10px /16 */
|
padding-left: 0.625rem; /* 10px /16 */
|
||||||
}
|
}
|
||||||
/* Hide inactive menus 3 or more deep */
|
|
||||||
.elv-toc-list ul ul > li:not(.elv-toc-active) > ul > li:not(.elv-toc-active) {
|
/* Display inline menus 4 or more deep */
|
||||||
display: none;
|
.elv-toc-list ul ul ul > li {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elv-toc-list ul ul ul > li::before {
|
||||||
|
content: "•";
|
||||||
|
margin-left: 0.4em;
|
||||||
|
margin-right: -0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elv-toc-list ul ul ul > li > a {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* List items */
|
/* List items */
|
||||||
@@ -98,3 +109,29 @@
|
|||||||
.elv-cat-list-active {
|
.elv-cat-list-active {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.breadcrumb-item.active {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb ol,
|
||||||
|
#breadcrumb ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb li:not(:last-child)::after {
|
||||||
|
content: " ˃"
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb a:not(:hover) {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb a {
|
||||||
|
text-decoration-color: #00bcd4;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
<header class="elv-layout elv-layout-full elv-header{% if headerClass %} {{ headerClass }}{% endif %}">
|
<header class="elv-layout elv-layout-full elv-header{% if headerClass %} {{ headerClass }}{% endif %}">
|
||||||
{% if page.url != '/' %}<h3><a href="https://torsion.org/borgmatic/">borgmatic</a></h3>{% endif %}
|
{% if page.url != '/' %}<h3><a href="https://torsion.org/borgmatic/">borgmatic</a></h3>{% endif %}
|
||||||
|
<div class="container" id="breadcrumb">
|
||||||
|
{% set breadcrumb = collections.all | eleventyNavigationBreadcrumb(eleventyNavigation.key, {allowMissing: true}) %}
|
||||||
|
{# The replace() is a work-around for https://github.com/11ty/eleventy-navigation/issues/56 #}
|
||||||
|
{{ breadcrumb | eleventyNavigationToHtml | replace('href="/reference/', 'href="/borgmatic/reference/') | safe }}
|
||||||
|
<div data-pagefind-meta="title" aria-hidden="true" style="display: none">
|
||||||
|
{% for entry in breadcrumb %}{{ entry.title }} > {% endfor %}{{ title | safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h1 class="elv-hed">{{ title | safe }}</h1>
|
<h1 class="elv-hed">{{ title | safe }}</h1>
|
||||||
|
{% if page.url == '/' %}<h3>It's your data. Keep it that way.</h3>{% endif %}
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ h5 {
|
|||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2.666666666667em; /* 48px /18 */
|
font-size: 2.666666666667em; /* 48px /18 */
|
||||||
margin: 0 0 .5em;
|
margin: 0;
|
||||||
}
|
}
|
||||||
main .elv-toc + h1 {
|
main .elv-toc + h1 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
@@ -229,7 +229,7 @@ pre + .note {
|
|||||||
|
|
||||||
/* Layout */
|
/* Layout */
|
||||||
.elv-layout {
|
.elv-layout {
|
||||||
padding: 1rem;
|
padding: 0 1rem 1rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 42rem;
|
max-width: 42rem;
|
||||||
clear: both;
|
clear: both;
|
||||||
@@ -548,3 +548,15 @@ main .elv-toc + h1 .direct-link {
|
|||||||
.mdi.mdi-content-copy {
|
.mdi.mdi-content-copy {
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M19 21H8V7h11m0-2H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m-3-4H4a2 2 0 0 0-2 2v14h2V3h12V1Z'/%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M19 21H8V7h11m0-2H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m-3-4H4a2 2 0 0 0-2 2v14h2V3h12V1Z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#search-entrance {
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-entrance form {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,12 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="generator" content="{{ eleventy.generator }}">
|
<meta name="generator" content="{{ eleventy.generator }}">
|
||||||
<link rel="icon" href="https://torsion.org/borgmatic/docs/static/borgmatic.png" type="image/x-icon">
|
<link rel="icon" href="https://torsion.org/borgmatic/static/borgmatic.png" type="image/x-icon">
|
||||||
<title>{{ subtitle + ' - ' if subtitle}}{{ title }}</title>
|
{% if page.url == '/' %}
|
||||||
|
<title>borgmatic</title>
|
||||||
|
{% else %}
|
||||||
|
<title>borgmatic{% if subtitle or title %} - {% endif %}{{ subtitle + ' - ' if subtitle}}{{ title }}</title>
|
||||||
|
{% endif %}
|
||||||
{%- set css %}
|
{%- set css %}
|
||||||
{% include 'index.css' %}
|
{% include 'index.css' %}
|
||||||
{% include 'components/lists.css' %}
|
{% include 'components/lists.css' %}
|
||||||
@@ -14,7 +18,6 @@
|
|||||||
{% include 'components/toc.css' %}
|
{% include 'components/toc.css' %}
|
||||||
{% include 'components/info-blocks.css' %}
|
{% include 'components/info-blocks.css' %}
|
||||||
{% include 'prism-theme.css' %}
|
{% include 'prism-theme.css' %}
|
||||||
{% include 'asciinema.css' %}
|
|
||||||
{% endset %}
|
{% endset %}
|
||||||
<style>{{ css | safe }}</style>
|
<style>{{ css | safe }}</style>
|
||||||
{% if feedTitle and feedUrl %}
|
{% if feedTitle and feedUrl %}
|
||||||
|
|||||||
@@ -7,11 +7,21 @@ headerClass: elv-header-default
|
|||||||
|
|
||||||
<main class="elv-layout{% if layoutClass %} {{ layoutClass }}{% endif %}">
|
<main class="elv-layout{% if layoutClass %} {{ layoutClass }}{% endif %}">
|
||||||
<div id="documentation" class="elv-toc">
|
<div id="documentation" class="elv-toc">
|
||||||
|
{% if page.url != '/search/' %}
|
||||||
|
<div id="search-entrance">
|
||||||
|
🔍
|
||||||
|
<form action="/borgmatic/search/">
|
||||||
|
<label>
|
||||||
|
<input type="search" name="query" placeholder="Search">
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div>
|
<div>
|
||||||
{% set navPages = collections.all | eleventyNavigation %}
|
{% set navPages = collections.all | eleventyNavigation %}
|
||||||
{% macro renderNavListItem(entry) -%}
|
{% macro renderNavListItem(entry) -%}
|
||||||
<li{% if entry.url == page.url %} class="elv-toc-active"{% endif %}>
|
<li{% if entry.url == page.url %} class="elv-toc-active"{% endif %}>
|
||||||
<a {% if entry.url %}href="{% if borgmatic.environment == "production" %}https://torsion.org/borgmatic/docs{% else %}http://localhost:8080/docs{% endif %}{{ entry.url | url }}"{% endif %}>{{ entry.title }}</a>
|
<a {% if entry.url %}href="{% if borgmatic.environment == "production" %}https://torsion.org/borgmatic{% else %}http://localhost:8080/borgmatic{% endif %}{{ entry.url | url }}"{% endif %}>{{ entry.title }}</a>
|
||||||
{%- if entry.children.length -%}
|
{%- if entry.children.length -%}
|
||||||
<ul>
|
<ul>
|
||||||
{%- for child in entry.children %}{{ renderNavListItem(child) }}{% endfor -%}
|
{%- for child in entry.children %}{{ renderNavListItem(child) }}{% endfor -%}
|
||||||
@@ -26,7 +36,9 @@ headerClass: elv-header-default
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-pagefind-body>
|
||||||
{{ content | safe }}
|
{{ content | safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% include 'components/suggestion-link.html' %}
|
{% include 'components/suggestion-link.html' %}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,9 +1,37 @@
|
|||||||
services:
|
services:
|
||||||
|
traefik:
|
||||||
|
image: public.ecr.aws/docker/library/traefik:3.5.3
|
||||||
|
container_name: borgmatic-docs-traefik
|
||||||
|
command:
|
||||||
|
- "--global.checkNewVersion=false"
|
||||||
|
- "--global.sendAnonymousUsage=false"
|
||||||
|
- "--entrypoints.web.address=:8080"
|
||||||
|
- "--accesslog"
|
||||||
|
- "--accesslog.fields.headers.defaultmode=keep"
|
||||||
|
- "--providers.docker"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--api.insecure=false"
|
||||||
|
- "--api.dashboard=false"
|
||||||
|
- "--log.level=WARN"
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8080:8080"
|
||||||
|
volumes:
|
||||||
|
- ${CONTAINER_SOCKET_PATH:-/run/user/docker.sock}:/var/run/docker.sock:ro
|
||||||
docs:
|
docs:
|
||||||
image: borgmatic-docs
|
image: borgmatic-docs
|
||||||
container_name: borgmatic-docs
|
container_name: borgmatic-docs
|
||||||
ports:
|
labels:
|
||||||
- 8080:80
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.borgmatic-docs.rule=PathPrefix(`/borgmatic`)"
|
||||||
|
- "traefik.http.routers.borgmatic-docs.middlewares=borgmatic-trailing-slash-redirectregex,borgmatic-docs-redirectregex,borgmatic-stripprefix"
|
||||||
|
- "traefik.http.middlewares.borgmatic-trailing-slash-redirectregex.redirectregex.regex=^(.*)/borgmatic$$"
|
||||||
|
- "traefik.http.middlewares.borgmatic-trailing-slash-redirectregex.redirectregex.replacement=$${1}/borgmatic/"
|
||||||
|
- "traefik.http.middlewares.borgmatic-trailing-slash-redirectregex.redirectregex.permanent=true"
|
||||||
|
- "traefik.http.middlewares.borgmatic-docs-redirectregex.redirectregex.regex=^(.*)/borgmatic/docs/(.*)$$"
|
||||||
|
- "traefik.http.middlewares.borgmatic-docs-redirectregex.redirectregex.replacement=$${1}/borgmatic/$${2}"
|
||||||
|
- "traefik.http.middlewares.borgmatic-docs-redirectregex.redirectregex.permanent=true"
|
||||||
|
- "traefik.http.middlewares.borgmatic-stripprefix.stripprefix.prefixes=/borgmatic"
|
||||||
|
- "traefik.http.routers.borgmatic-docs.entrypoints=web"
|
||||||
build:
|
build:
|
||||||
dockerfile: docs/Dockerfile
|
dockerfile: docs/Dockerfile
|
||||||
context: ..
|
context: ..
|
||||||
@@ -16,6 +44,6 @@ services:
|
|||||||
- sh
|
- sh
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
echo; echo "You can view dev docs at http://localhost:8080"; echo
|
echo; echo "You can view dev docs at http://localhost:8080/borgmatic/"; echo
|
||||||
depends_on:
|
depends_on:
|
||||||
- docs
|
- docs
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 10
|
order: 10
|
||||||
---
|
---
|
||||||
## Preparation and cleanup hooks
|
|
||||||
|
|
||||||
If you find yourself performing preparation tasks before your backup runs or
|
If you find yourself performing preparation tasks before your backup runs or
|
||||||
doing cleanup work afterwards, borgmatic command hooks may be of interest. These
|
doing cleanup work afterwards, borgmatic command hooks may be of interest. These
|
||||||
are custom shell commands you can configure borgmatic to execute at various
|
are custom shell commands you can configure borgmatic to execute at various
|
||||||
@@ -14,7 +12,7 @@ points as it runs.
|
|||||||
|
|
||||||
(But if you're looking to backup a database, it's probably easier to use the
|
(But if you're looking to backup a database, it's probably easier to use the
|
||||||
[database backup
|
[database backup
|
||||||
feature](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/)
|
feature](https://torsion.org/borgmatic/how-to/backup-your-databases/)
|
||||||
instead.)
|
instead.)
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span> Command
|
<span class="minilink minilink-addedin">New in version 2.0.0</span> Command
|
||||||
@@ -40,7 +38,7 @@ commands:
|
|||||||
|
|
||||||
If you're coming from an older version of borgmatic, there is tooling to help
|
If you're coming from an older version of borgmatic, there is tooling to help
|
||||||
you [upgrade your
|
you [upgrade your
|
||||||
configuration](https://torsion.org/borgmatic/docs/how-to/upgrade/#upgrading-your-configuration)
|
configuration](https://torsion.org/borgmatic/how-to/upgrade/#upgrading-your-configuration)
|
||||||
to this new command hook format.
|
to this new command hook format.
|
||||||
|
|
||||||
Note that if a `run:` command contains a special YAML character such as a colon,
|
Note that if a `run:` command contains a special YAML character such as a colon,
|
||||||
@@ -74,69 +72,9 @@ commands:
|
|||||||
- echo "After successful create!"
|
- echo "After successful create!"
|
||||||
```
|
```
|
||||||
|
|
||||||
Each command in the `commands:` list has the following options:
|
See the [command hooks
|
||||||
|
documentation](https://torsion.org/borgmatic/reference/configuration/command-hooks/)
|
||||||
* `before` or `after`: Name for the point in borgmatic's execution that the commands should be run before or after, one of:
|
for additional details about how to configure command hooks.
|
||||||
* `action` runs before or after each action for each repository. This replaces the deprecated `before_create`, `after_prune`, etc.
|
|
||||||
* `repository` runs before or after all actions for each repository. This replaces the deprecated `before_actions` and `after_actions`.
|
|
||||||
* `configuration` runs before or after all actions and repositories in the current configuration file.
|
|
||||||
* `everything` runs before or after all configuration files. Errors here do not trigger `error` hooks or the `fail` state in monitoring hooks. This replaces the deprecated `before_everything` and `after_everything`.
|
|
||||||
* `error` runs after an error occurs—and it's only available for `after`. This replaces the deprecated `on_error` hook.
|
|
||||||
* `when`: Only trigger the hook when borgmatic is run with particular actions (`create`, `prune`, etc.) listed here. Defaults to running for all actions.
|
|
||||||
* `states`: <span class="minilink minilink-addedin">New in version 2.0.3</span> Only trigger the hook if borgmatic encounters one of the states (execution results) listed here. This state is evaluated only for the scope of the configured `action`, `repository`, etc., rather than for the entire borgmatic run. Only available for `after` hooks. Defaults to running the hook for all states. One or more of:
|
|
||||||
* `finish`: No errors occurred.
|
|
||||||
* `fail`: An error occurred.
|
|
||||||
* `run`: List of one or more shell commands or scripts to run when this command hook is triggered.
|
|
||||||
|
|
||||||
When command hooks run, they respect the `working_directory` option if it is
|
|
||||||
configured, meaning that the hook commands are run in that directory.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.4</span>If the exact
|
|
||||||
same `everything` command hook is present in multiple configuration files,
|
|
||||||
borgmatic only runs it once.
|
|
||||||
|
|
||||||
borgmatic's `--repository` flag does not impact which command hooks get run. But
|
|
||||||
you can use the `--config` flag to limit the configuration files (and thus
|
|
||||||
command hooks) used.
|
|
||||||
|
|
||||||
|
|
||||||
### Order of execution
|
|
||||||
|
|
||||||
Here's a way of visualizing how all of these command hooks slot into borgmatic's
|
|
||||||
execution.
|
|
||||||
|
|
||||||
Let's say you've got a borgmatic configuration file with a configured
|
|
||||||
repository. And suppose you configure several command hooks and then run
|
|
||||||
borgmatic for the `create` and `prune` actions. Here's the order of execution:
|
|
||||||
|
|
||||||
* Run `before: everything` hooks (from all configuration files).
|
|
||||||
* Run `before: configuration` hooks (from the first configuration file).
|
|
||||||
* Run `before: repository` hooks (for the first repository).
|
|
||||||
* Run `before: action` hooks for `create`.
|
|
||||||
* Actually run the `create` action (e.g. `borg create`).
|
|
||||||
* Run `after: action` hooks for `create`.
|
|
||||||
* Run `before: action` hooks for `prune`.
|
|
||||||
* Actually run the `prune` action (e.g. `borg prune`).
|
|
||||||
* Run `after: action` hooks for `prune`.
|
|
||||||
* Run `after: repository` hooks (for the first repository).
|
|
||||||
* Run `after: configuration` hooks (from the first configuration file).
|
|
||||||
* Run `after: error` hooks (if an error occurs).
|
|
||||||
* Run `after: everything` hooks (from all configuration files).
|
|
||||||
|
|
||||||
This same order of execution extends to multiple repositories and/or
|
|
||||||
configuration files.
|
|
||||||
|
|
||||||
Based on the above, you can see the difference between, say, an `after: action`
|
|
||||||
hook with `states: [fail]` and an `after: error` hook. The `after: action hook`
|
|
||||||
runs immediately after the create action fails for a particular repository—so
|
|
||||||
before any subsequent actions for that repository or other repositories even
|
|
||||||
have a chance to run. Whereas the `after: error` hook doesn't run until all
|
|
||||||
actions for—and repositories in—a configuration file have had a chance to
|
|
||||||
execute.
|
|
||||||
|
|
||||||
And if there are multiple hooks defined for a particular step (e.g. `before:
|
|
||||||
action` for `create`), then those hooks are run in the order they're defined in
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
|
|
||||||
### Deprecated command hooks
|
### Deprecated command hooks
|
||||||
@@ -247,83 +185,12 @@ borgmatic does not run `on_error` hooks if an error occurs within a
|
|||||||
`before_everything` or `after_everything` hook.
|
`before_everything` or `after_everything` hook.
|
||||||
|
|
||||||
|
|
||||||
## Variable interpolation
|
|
||||||
|
|
||||||
The command action hooks support interpolating particular runtime variables into
|
|
||||||
the commands that are run. Here's are a couple examples that assume you provide
|
|
||||||
separate shell scripts:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
commands:
|
|
||||||
- after: action
|
|
||||||
when: [prune]
|
|
||||||
run:
|
|
||||||
- record-prune.sh {configuration_filename} {repository}
|
|
||||||
- after: error
|
|
||||||
when: [create]
|
|
||||||
run:
|
|
||||||
- send-text-message.sh {configuration_filename} {repository}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example, when the hook is triggered, borgmatic interpolates runtime
|
|
||||||
values into each hook command: the borgmatic configuration filename and the
|
|
||||||
paths of the current Borg repository.
|
|
||||||
|
|
||||||
Here's the full set of supported variables you can use here:
|
|
||||||
|
|
||||||
* `configuration_filename`: borgmatic configuration filename in which the
|
|
||||||
hook was defined
|
|
||||||
* `log_file`
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.12</span>:
|
|
||||||
path of the borgmatic log file, only set when the `--log-file` flag is used
|
|
||||||
* `repository`: path of the current repository as configured in the current
|
|
||||||
borgmatic configuration file, if applicable to the current hook
|
|
||||||
* `repository_label` <span class="minilink minilink-addedin">New in version
|
|
||||||
1.8.12</span>: label of the current repository as configured in the current
|
|
||||||
borgmatic configuration file, if applicable to the current hook
|
|
||||||
* `error`: the error message itself, only applies to `error` hooks
|
|
||||||
* `output`: output of the command that failed, only applies to `error` hooks
|
|
||||||
(may be blank if an error occurred without running a command)
|
|
||||||
|
|
||||||
Not all command hooks support all variables. For instance, the `everything` and
|
|
||||||
`configuration` hooks don't support repository variables because those hooks
|
|
||||||
don't run in the context of a single repository. But the deprecated command
|
|
||||||
hooks (`before_backup`, `on_error`, etc.) do generally support variable
|
|
||||||
interpolation.
|
|
||||||
|
|
||||||
borgmatic automatically escapes these interpolated values to prevent shell
|
|
||||||
injection attacks. One implication is that you shouldn't wrap the interpolated
|
|
||||||
values in your own quotes, as that will interfere with the quoting performed by
|
|
||||||
borgmatic and result in your command receiving incorrect arguments. For
|
|
||||||
instance, this won't work:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
commands:
|
|
||||||
- after: error
|
|
||||||
run:
|
|
||||||
# Don't do this! It won't work, as the {error} value is already quoted.
|
|
||||||
- send-text-message.sh "Uh oh: {error}"
|
|
||||||
```
|
|
||||||
|
|
||||||
Do this instead:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
commands:
|
|
||||||
- after: error
|
|
||||||
run:
|
|
||||||
- send-text-message.sh {error}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that you can also interpolate [arbitrary environment
|
|
||||||
variables](https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/).
|
|
||||||
|
|
||||||
|
|
||||||
## Hook output
|
## Hook output
|
||||||
|
|
||||||
Any output produced by your hooks shows up both at the console and in syslog
|
Any output produced by your hooks shows up both at the console and in syslog
|
||||||
(when enabled). For more information, read about <a
|
(when enabled). For more information, see the <a
|
||||||
href="https://torsion.org/borgmatic/docs/how-to/inspect-your-backups/">inspecting
|
href="https://torsion.org/borgmatic/reference/command-line/logging/">logging
|
||||||
your backups</a>.
|
documentation</a>.
|
||||||
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 11
|
order: 11
|
||||||
---
|
---
|
||||||
## Occasional backups
|
|
||||||
|
|
||||||
A common situation is backing up to a repository that's only sometimes online.
|
A common situation is backing up to a repository that's only sometimes online.
|
||||||
For instance, you might send most of your backups to the cloud, but
|
For instance, you might send most of your backups to the cloud, but
|
||||||
occasionally you want to plug in an external hard drive or backup to your
|
occasionally you want to plug in an external hard drive or backup to your
|
||||||
@@ -25,16 +23,19 @@ or an offline server or a low battery—and exit gracefully? That's where the
|
|||||||
concept of "soft failure" come in.
|
concept of "soft failure" come in.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="caveats-and-details"></a>
|
||||||
|
|
||||||
|
|
||||||
## Soft failure command hooks
|
## Soft failure command hooks
|
||||||
|
|
||||||
This feature leverages [borgmatic command
|
This feature leverages [borgmatic command
|
||||||
hooks](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/),
|
hooks](https://torsion.org/borgmatic/how-to/add-preparation-and-cleanup-steps-to-backups/),
|
||||||
so familiarize yourself with them first. The idea is that you write a simple
|
so familiarize yourself with them first. The idea is that you write a simple
|
||||||
test in the form of a borgmatic command hook to see if backups should proceed or
|
test in the form of a borgmatic command hook to see if backups should proceed or
|
||||||
not.
|
not.
|
||||||
|
|
||||||
The way the test works is that if any of your hook commands return a special
|
The way the test works is that if any of your hook commands return a special
|
||||||
exit status of 75, that indicates to borgmatic that it's a temporary failure,
|
exit status of 75, that indicates to borgmatic that it's a temporary failure
|
||||||
and borgmatic should skip all subsequent actions for the current repository.
|
and borgmatic should skip all subsequent actions for the current repository.
|
||||||
|
|
||||||
If you return any status besides 75, then it's a standard success or error.
|
If you return any status besides 75, then it's a standard success or error.
|
||||||
@@ -42,7 +43,7 @@ If you return any status besides 75, then it's a standard success or error.
|
|||||||
|
|
||||||
So for instance, if you have an external drive that's only sometimes mounted,
|
So for instance, if you have an external drive that's only sometimes mounted,
|
||||||
declare its repository in its own [separate configuration
|
declare its repository in its own [separate configuration
|
||||||
file](https://torsion.org/borgmatic/docs/how-to/make-per-application-backups/),
|
file](https://torsion.org/borgmatic/how-to/make-per-application-backups/),
|
||||||
say at `/etc/borgmatic.d/removable.yaml`:
|
say at `/etc/borgmatic.d/removable.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -116,33 +117,6 @@ commands:
|
|||||||
|
|
||||||
Writing the battery script is left as an exercise to the reader.
|
Writing the battery script is left as an exercise to the reader.
|
||||||
|
|
||||||
|
See the [soft failure
|
||||||
## Caveats and details
|
documentation](https://torsion.org/borgmatic/reference/configuration/command-hooks/#soft-failure)
|
||||||
|
for additional details.
|
||||||
There are some caveats you should be aware of with this feature.
|
|
||||||
|
|
||||||
* You'll generally want to put a soft failure command in a `before` command
|
|
||||||
hook, so as to gate whether the backup action occurs. While a soft failure is
|
|
||||||
also supported in an `after` command hook, returning a soft failure there
|
|
||||||
won't prevent any actions from occurring, because they've already occurred!
|
|
||||||
Similarly, you can return a soft failure from an `error` command hook, but at
|
|
||||||
that point it's too late to prevent the error.
|
|
||||||
* Returning a soft failure does prevent further commands in the same hook from
|
|
||||||
executing. So, like a standard error, it is an "early out." Unlike a standard
|
|
||||||
error, borgmatic does not display it in angry red text or consider it a
|
|
||||||
failure.
|
|
||||||
* <span class="minilink minilink-addedin">New in version 1.9.0</span> Soft
|
|
||||||
failures in `action` or `before_*` command hooks only skip the current
|
|
||||||
repository rather than all repositories in a configuration file.
|
|
||||||
* If you're writing a soft failure script that you want to vary based on the
|
|
||||||
current repository, for instance so you can have multiple repositories in a
|
|
||||||
single configuration file, have a look at [command hook variable
|
|
||||||
interpolation](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/#variable-interpolation).
|
|
||||||
And there's always still the option of putting anything that you don't want
|
|
||||||
soft-failed (like always-online cloud backups) in separate configuration
|
|
||||||
files from your soft-failing repositories.
|
|
||||||
* The soft failure doesn't have to test anything related to a repository. You
|
|
||||||
can even perform a test that individual source directories are mounted and
|
|
||||||
available. Use your imagination!
|
|
||||||
* Soft failures are not currently implemented for `everything`,
|
|
||||||
`before_everything`, or `after_everything` command hooks.
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 8
|
order: 8
|
||||||
---
|
---
|
||||||
## Database dump hooks
|
|
||||||
|
|
||||||
If you want to backup a database, it's best practice with most database
|
If you want to backup a database, it's best practice with most database
|
||||||
systems to backup an exported database dump, rather than backing up your
|
systems to backup an exported database dump, rather than backing up your
|
||||||
database's internal file storage. That's because the internal storage can
|
database's internal file storage. That's because the internal storage can
|
||||||
@@ -29,33 +27,6 @@ mysql_databases:
|
|||||||
these and other database options in the `hooks:` section of your
|
these and other database options in the `hooks:` section of your
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.5.22</span> You can
|
|
||||||
also dump MongoDB databases. For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
mongodb_databases:
|
|
||||||
- name: messages
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.9</span>
|
|
||||||
Additionally, you can dump SQLite databases. For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
sqlite_databases:
|
|
||||||
- name: mydb
|
|
||||||
path: /var/lib/sqlite3/mydb.sqlite
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.2</span> If you're
|
|
||||||
using MariaDB, use the MariaDB database hook instead of `mysql_databases:` as
|
|
||||||
the MariaDB hook calls native MariaDB commands instead of the deprecated MySQL
|
|
||||||
ones. For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
mariadb_databases:
|
|
||||||
- name: comments
|
|
||||||
```
|
|
||||||
|
|
||||||
As part of each backup, borgmatic streams a database dump for each configured
|
As part of each backup, borgmatic streams a database dump for each configured
|
||||||
database directly to Borg, so it's included in the backup without consuming
|
database directly to Borg, so it's included in the backup without consuming
|
||||||
additional disk space. (The exceptions are the PostgreSQL/MongoDB `directory`
|
additional disk space. (The exceptions are the PostgreSQL/MongoDB `directory`
|
||||||
@@ -109,47 +80,17 @@ sqlite_databases:
|
|||||||
path: /var/lib/sqlite3/mydb.sqlite
|
path: /var/lib/sqlite3/mydb.sqlite
|
||||||
```
|
```
|
||||||
|
|
||||||
See your [borgmatic configuration
|
See the [data sources
|
||||||
file](https://torsion.org/borgmatic/docs/reference/configuration/) for
|
documentation](https://torsion.org/borgmatic/reference/configuration/data-sources/)
|
||||||
additional customization of the options passed to database commands (when
|
for details on additional options, including customizing the flags passed to
|
||||||
listing databases, restoring databases, etc.).
|
database commands when listing databases, restoring databases, etc.
|
||||||
|
|
||||||
|
<a id="runtime-directory"></a>
|
||||||
|
|
||||||
### Runtime directory
|
To support streaming database dumps to Borg, borgmatic uses a runtime directory
|
||||||
|
for temporary file storage. See the [runtime directory
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.0</span> To support
|
documentation](https://torsion.org/borgmatic/reference/configuration/runtime-directory/)
|
||||||
streaming database dumps to Borg, borgmatic uses a runtime directory for
|
for details.
|
||||||
temporary file storage, probing the following locations (in order) to find it:
|
|
||||||
|
|
||||||
1. The `user_runtime_directory` borgmatic configuration option.
|
|
||||||
2. The `XDG_RUNTIME_DIR` environment variable, usually `/run/user/$UID`
|
|
||||||
(where `$UID` is the current user's ID), automatically set by PAM on Linux
|
|
||||||
for a user with a session.
|
|
||||||
3. <span class="minilink minilink-addedin">New in version 1.9.2</span>The
|
|
||||||
`RUNTIME_DIRECTORY` environment variable, set by systemd if
|
|
||||||
`RuntimeDirectory=borgmatic` is added to borgmatic's systemd service file.
|
|
||||||
4. <span class="minilink minilink-addedin">New in version 1.9.1</span>The
|
|
||||||
`TMPDIR` environment variable, set on macOS for a user with a session,
|
|
||||||
among other operating systems.
|
|
||||||
5. <span class="minilink minilink-addedin">New in version 1.9.1</span>The
|
|
||||||
`TEMP` environment variable, set on various systems.
|
|
||||||
6. <span class="minilink minilink-addedin">New in version 1.9.2</span>
|
|
||||||
Hard-coded `/tmp`. <span class="minilink minilink-addedin">Prior to
|
|
||||||
version 1.9.2</span>This was instead hard-coded to `/run/user/$UID`.
|
|
||||||
|
|
||||||
You can see the runtime directory path that borgmatic selects by running with
|
|
||||||
`--verbosity 2` and looking for "Using runtime directory" in the output.
|
|
||||||
|
|
||||||
Regardless of the runtime directory selected, borgmatic stores its files
|
|
||||||
within a `borgmatic` subdirectory of the runtime directory. Additionally, in
|
|
||||||
the case of `TMPDIR`, `TEMP`, and the hard-coded `/tmp`, borgmatic creates a
|
|
||||||
randomly named subdirectory in an effort to reduce path collisions in shared
|
|
||||||
system temporary directories.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.9.0</span>
|
|
||||||
borgmatic created temporary streaming database dumps within the `~/.borgmatic`
|
|
||||||
directory by default. At that time, the path was configurable by the
|
|
||||||
`borgmatic_source_directory` configuration option (now deprecated).
|
|
||||||
|
|
||||||
|
|
||||||
### All databases
|
### All databases
|
||||||
@@ -269,9 +210,11 @@ the `pg_restore_command` and `psql_command` options.
|
|||||||
|
|
||||||
If you choose to use the `pg_dump` command within the container, and you're
|
If you choose to use the `pg_dump` command within the container, and you're
|
||||||
using the `directory` format in particular, you'll also need to mount the
|
using the `directory` format in particular, you'll also need to mount the
|
||||||
[runtime directory](#runtime-directory) from your host into the container at the
|
[runtime
|
||||||
same path. Otherwise, the `directory` format dump will remain locked away inside
|
directory](https://torsion.org/borgmatic/reference/configuration/runtime-directory/)
|
||||||
the database container where Borg can't read it.
|
from your host into the container at the same path. Otherwise, the `directory`
|
||||||
|
format dump will remain locked away inside the database container where Borg
|
||||||
|
can't read it.
|
||||||
|
|
||||||
For example, with Docker Compose and a runtime directory located at
|
For example, with Docker Compose and a runtime directory located at
|
||||||
`/run/user/1000`:
|
`/run/user/1000`:
|
||||||
@@ -328,7 +271,7 @@ password to the temporary `pg_dump` container.
|
|||||||
|
|
||||||
Similar command override options are available for (some of) the other
|
Similar command override options are available for (some of) the other
|
||||||
supported database types as well. See the [configuration
|
supported database types as well. See the [configuration
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/) for
|
reference](https://torsion.org/borgmatic/reference/configuration/) for
|
||||||
details.
|
details.
|
||||||
|
|
||||||
|
|
||||||
@@ -355,8 +298,10 @@ hooks:
|
|||||||
### External passwords
|
### External passwords
|
||||||
|
|
||||||
If you don't want to keep your database passwords in your borgmatic
|
If you don't want to keep your database passwords in your borgmatic
|
||||||
configuration file, you can instead pass them in [from external credential
|
configuration file, you can instead pass them in from [external credential
|
||||||
sources](https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/).
|
sources](https://torsion.org/borgmatic/reference/configuration/credentials/) or
|
||||||
|
[environment
|
||||||
|
variables](https://torsion.org/borgmatic/reference/configuration/environment-variables/).
|
||||||
|
|
||||||
|
|
||||||
### Configuration backups
|
### Configuration backups
|
||||||
@@ -370,17 +315,23 @@ bring back any missing configuration files in order to restore a database.
|
|||||||
<span class="minilink minilink-addedin">New in version 1.7.15</span> borgmatic
|
<span class="minilink minilink-addedin">New in version 1.7.15</span> borgmatic
|
||||||
automatically includes configuration files in your backup. See [the
|
automatically includes configuration files in your backup. See [the
|
||||||
documentation on the `config bootstrap`
|
documentation on the `config bootstrap`
|
||||||
action](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/#extract-the-configuration-files-used-to-create-an-archive)
|
action](https://torsion.org/borgmatic/how-to/extract-a-backup/#extract-the-configuration-files-used-to-create-an-archive)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
|
||||||
## Supported databases
|
## Supported databases
|
||||||
|
|
||||||
As of now, borgmatic supports PostgreSQL, MariaDB, MySQL, MongoDB, and SQLite
|
borgmatic directly supports [several database
|
||||||
databases directly. But see below about general-purpose preparation and
|
systems](https://torsion.org/borgmatic/reference/configuration/data-sources/).
|
||||||
cleanup hooks as a work-around with other database systems. Also, please [file
|
But if you're looking to backup an unsupported databases system, you can use
|
||||||
a ticket](https://torsion.org/borgmatic/#issues) for additional database
|
general-purpose [preparation and cleanup
|
||||||
systems that you'd like supported.
|
hooks](https://torsion.org/borgmatic/how-to/add-preparation-and-cleanup-steps-to-backups/)
|
||||||
|
as a work-around. These hooks allows you to trigger arbitrary commands or
|
||||||
|
scripts before and after backups to create and cleanup database dumps for any
|
||||||
|
database system.
|
||||||
|
|
||||||
|
Also, please [file a ticket](https://torsion.org/borgmatic/#issues) for
|
||||||
|
additional database systems that you'd like borgmatic to officially support.
|
||||||
|
|
||||||
|
|
||||||
## Database restoration
|
## Database restoration
|
||||||
@@ -414,8 +365,6 @@ most up-to-date files and therefore the latest timestamp, run a command like:
|
|||||||
borgmatic restore --archive host-2023-01-02T04:06:07.080910
|
borgmatic restore --archive host-2023-01-02T04:06:07.080910
|
||||||
```
|
```
|
||||||
|
|
||||||
(No borgmatic `restore` action? Upgrade borgmatic!)
|
|
||||||
|
|
||||||
Or you can simplify this to:
|
Or you can simplify this to:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -431,6 +380,20 @@ restoring dumps from the selected archive. So be very careful when and where
|
|||||||
you run it.
|
you run it.
|
||||||
|
|
||||||
|
|
||||||
|
### Configuration file selection
|
||||||
|
|
||||||
|
If you have a multi-configuration-file setup and you want to restore a database
|
||||||
|
from a single configuration file, use the `--config` flag with the path of the
|
||||||
|
configuration file to use for the restore. For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
borgmatic restore --config /path/to/config.yaml --archive latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Without `--config`, borgmatic tries to run the `restore` action once for each
|
||||||
|
configuration file it finds.
|
||||||
|
|
||||||
|
|
||||||
### Repository selection
|
### Repository selection
|
||||||
|
|
||||||
If you have a single repository in your borgmatic configuration file(s), no
|
If you have a single repository in your borgmatic configuration file(s), no
|
||||||
@@ -590,7 +553,7 @@ postgresql_databases:
|
|||||||
### Manual restoration
|
### Manual restoration
|
||||||
|
|
||||||
If you prefer to restore a database without the help of borgmatic, first
|
If you prefer to restore a database without the help of borgmatic, first
|
||||||
[extract](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/) an
|
[extract](https://torsion.org/borgmatic/how-to/extract-a-backup/) an
|
||||||
archive containing a database dump.
|
archive containing a database dump.
|
||||||
|
|
||||||
borgmatic extracts the dump file into the `borgmatic/` directory within the
|
borgmatic extracts the dump file into the `borgmatic/` directory within the
|
||||||
@@ -607,7 +570,7 @@ After extraction, you can manually restore the dump file using native database
|
|||||||
commands like `pg_restore`, `mysql`, `mongorestore`, `sqlite`, or similar.
|
commands like `pg_restore`, `mysql`, `mongorestore`, `sqlite`, or similar.
|
||||||
|
|
||||||
Also see the documentation on [listing database
|
Also see the documentation on [listing database
|
||||||
dumps](https://torsion.org/borgmatic/docs/how-to/inspect-your-backups/#listing-database-dumps).
|
dumps](https://torsion.org/borgmatic/how-to/inspect-your-backups/#listing-database-dumps).
|
||||||
|
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
@@ -653,16 +616,6 @@ starting from version 1.7.15, borgmatic includes your configuration files
|
|||||||
automatically.
|
automatically.
|
||||||
|
|
||||||
|
|
||||||
## Preparation and cleanup hooks
|
|
||||||
|
|
||||||
If this database integration is too limited for needs, borgmatic also supports
|
|
||||||
general-purpose [preparation and cleanup
|
|
||||||
hooks](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/).
|
|
||||||
These hooks allows you to trigger arbitrary commands or scripts before and
|
|
||||||
after backups. So if necessary, you can use these hooks to create database
|
|
||||||
dumps with any database system.
|
|
||||||
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Authentication errors
|
### Authentication errors
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 13
|
order: 13
|
||||||
---
|
---
|
||||||
## When things go wrong
|
|
||||||
|
|
||||||
After Borg runs, it indicates whether it succeeded via its exit code, a
|
After Borg runs, it indicates whether it succeeded via its exit code, a
|
||||||
numeric ID indicating success, warning, or error. borgmatic consumes this exit
|
numeric ID indicating success, warning, or error. borgmatic consumes this exit
|
||||||
code to decide how to respond. Normally, a Borg error results in a borgmatic
|
code to decide how to respond. Normally, a Borg error results in a borgmatic
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 4
|
order: 4
|
||||||
---
|
---
|
||||||
## Biggish data
|
<a id="a-la-carte-actions"></a>
|
||||||
|
|
||||||
Borg itself is great for efficiently de-duplicating data across successive
|
Borg itself is great for efficiently de-duplicating data across successive
|
||||||
backup archives, even when dealing with very large repositories. But you may
|
backup archives, even when dealing with very large repositories. But you may
|
||||||
@@ -14,367 +14,29 @@ and `check` works well on small repositories, it's not so great on larger
|
|||||||
ones. That's because running the default pruning, compact, and consistency
|
ones. That's because running the default pruning, compact, and consistency
|
||||||
checks take a long time on large repositories.
|
checks take a long time on large repositories.
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.7.9</span> The
|
See the [actions
|
||||||
default action ordering was `prune`, `compact`, `create`, and `check`.
|
documentation](https://torsion.org/borgmatic/reference/command-line/actions/)
|
||||||
|
for details on customizing the actions that borgmatic runs.
|
||||||
|
|
||||||
### A la carte actions
|
|
||||||
|
|
||||||
If you find yourself wanting to customize the actions, you have some options.
|
<a id="spot-check"></a>
|
||||||
First, you can run borgmatic's `create`, `prune`, `compact`, or `check`
|
<a id="check-frequency"></a>
|
||||||
actions separately. For instance, the following optional actions are
|
<a id="check-days"></a>
|
||||||
available (among others):
|
<a id="running-only-checks"></a>
|
||||||
|
<a id="disabling-checks"></a>
|
||||||
```bash
|
|
||||||
borgmatic create
|
|
||||||
borgmatic prune
|
|
||||||
borgmatic compact
|
|
||||||
borgmatic check
|
|
||||||
```
|
|
||||||
|
|
||||||
You can run borgmatic with only one of these actions provided, or you can mix
|
|
||||||
and match any number of them in a single borgmatic run. This supports
|
|
||||||
approaches like skipping certain actions while running others. For instance,
|
|
||||||
this skips `prune` and `compact` and only runs `create` and `check`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create check
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.9</span> borgmatic
|
|
||||||
now respects your specified command-line action order, running actions in the
|
|
||||||
order you specify. In previous versions, borgmatic ran your specified actions
|
|
||||||
in a fixed ordering regardless of the order they appeared on the command-line.
|
|
||||||
|
|
||||||
But instead of running actions together, another option is to run backups with
|
|
||||||
`create` on a frequent schedule (e.g. with `borgmatic create` called from one
|
|
||||||
cron job), while only running expensive consistency checks with `check` on a
|
|
||||||
much less frequent basis (e.g. with `borgmatic check` called from a separate
|
|
||||||
cron job).
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.5</span> Instead of
|
|
||||||
(or in addition to) specifying actions on the command-line, you can configure
|
|
||||||
borgmatic to [skip particular
|
|
||||||
actions](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#skipping-actions).
|
|
||||||
|
|
||||||
|
|
||||||
### Consistency check configuration
|
### Consistency check configuration
|
||||||
|
|
||||||
Another option is to customize your consistency checks. By default, if you
|
Another way of dealing with large backups is to customize your consistency
|
||||||
omit consistency checks from configuration, borgmatic runs full-repository
|
checks. By default, if you omit consistency checks from configuration, borgmatic
|
||||||
checks (`repository`) and per-archive checks (`archives`) within each
|
runs full-repository checks and per-archive checks within each repository on a
|
||||||
repository, running the checks on a monthly basis. (See below about setting
|
monthly basis.
|
||||||
your own check frequency.)
|
|
||||||
|
But if you find that archive checks are too slow and/or you'd like to customize
|
||||||
But if you find that archive checks are too slow, for example, you can
|
the check frequency, see the [consistency checks
|
||||||
configure borgmatic to run repository checks only. Configure this in the
|
documentation](https://torsion.org/borgmatic/reference/configuration/consistency-checks/)
|
||||||
`consistency` section of borgmatic configuration:
|
for details.
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- name: repository
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `consistency:` section of your configuration.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.6.2</span> The
|
|
||||||
`checks` option was a plain list of strings without the `name:` part, and
|
|
||||||
borgmatic ran each configured check every time checks were run. For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- repository
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Here are the available checks, roughly from fastest to slowest:
|
|
||||||
|
|
||||||
* `archives`: Checks all of the archives' metadata in the repository.
|
|
||||||
* `repository`: Checks the consistency of the whole repository. The checks run
|
|
||||||
on the server and do not cause significant network traffic.
|
|
||||||
* `extract`: Performs an extraction dry-run of the latest archive.
|
|
||||||
* `data`: Verifies the data integrity of all archives contents, decrypting
|
|
||||||
and decompressing all data.
|
|
||||||
* `spot`: Compares file counts and contents between your source files and the
|
|
||||||
latest archive.
|
|
||||||
|
|
||||||
Note that the `data` check is a more thorough version of the `archives` check,
|
|
||||||
so enabling the `data` check implicitly enables the `archives` check as well.
|
|
||||||
|
|
||||||
See [Borg's check
|
|
||||||
documentation](https://borgbackup.readthedocs.io/en/stable/usage/check.html)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
|
|
||||||
### Spot check
|
|
||||||
|
|
||||||
The various consistency checks all have trade-offs around speed and
|
|
||||||
thoroughness, but most of them don't even look at your original source
|
|
||||||
files—arguably one important way to ensure your backups contain the files
|
|
||||||
you'll want to restore in the case of catastrophe (or an accidentally deleted
|
|
||||||
file). Because if something goes wrong with your source files, most
|
|
||||||
consistency checks will still pass with flying colors and you won't discover
|
|
||||||
there's a problem until you go to restore.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.10</span> That's
|
|
||||||
where the spot check comes in. This check actually compares your source file
|
|
||||||
counts and data against those in the latest archive, potentially catching
|
|
||||||
problems like incorrect excludes, inadvertent deletes, files changed by
|
|
||||||
malware, etc.
|
|
||||||
|
|
||||||
But because an exhaustive comparison of all source files against the latest
|
|
||||||
archive might be too slow, the spot check supports *sampling* a percentage of
|
|
||||||
your source files for the comparison, ensuring they fall within configured
|
|
||||||
tolerances.
|
|
||||||
|
|
||||||
Here's how it works. Start by installing the `xxhash` OS package if you don't
|
|
||||||
already have it, so the spot check can run the `xxh64sum` command and
|
|
||||||
efficiently hash files for comparison. Then add something like the following
|
|
||||||
to your borgmatic configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- name: spot
|
|
||||||
count_tolerance_percentage: 10
|
|
||||||
data_sample_percentage: 1
|
|
||||||
data_tolerance_percentage: 0.5
|
|
||||||
```
|
|
||||||
|
|
||||||
The `count_tolerance_percentage` is the percentage delta between the source
|
|
||||||
directories file count and the latest backup archive file count that is
|
|
||||||
allowed before the entire consistency check fails. For instance, if the spot
|
|
||||||
check runs and finds 100 source files on disk and 105 files in the latest
|
|
||||||
archive, that would be within the configured 10% count tolerance and the check
|
|
||||||
would succeed. But if there were 100 source files and 200 archive files, the
|
|
||||||
check would fail. (100 source files and only 50 archive files would also
|
|
||||||
fail.)
|
|
||||||
|
|
||||||
The `data_sample_percentage` is the percentage of total files in the source
|
|
||||||
directories to randomly sample and compare to their corresponding files in the
|
|
||||||
latest backup archive. A higher value allows a more accurate check—and a
|
|
||||||
slower one. The comparison is performed by hashing the selected source files
|
|
||||||
and counting hashes that don't match the latest archive. For instance, if you
|
|
||||||
have 1,000 source files and your sample percentage is 1%, then only 10 source
|
|
||||||
files will be compared against the latest archive. These sampled files are
|
|
||||||
selected randomly each time, so in effect the spot check is probabilistic.
|
|
||||||
|
|
||||||
The `data_tolerance_percentage` is the percentage of total files in the source
|
|
||||||
directories that can fail a spot check data comparison without failing the
|
|
||||||
entire consistency check. The value must be lower than or equal to the
|
|
||||||
`data_sample_percentage`, because `data_tolerance_percentage` only looks at
|
|
||||||
at the sampled files as determined by `data_sample_percentage`.
|
|
||||||
|
|
||||||
All three options are required when using the spot check. And because the
|
|
||||||
check relies on these configured tolerances, it may not be a
|
|
||||||
set-it-and-forget-it type of consistency check, at least until you get the
|
|
||||||
tolerances dialed in so there are minimal false positives or negatives. It is
|
|
||||||
recommended you run `borgmatic check` several times after configuring the spot
|
|
||||||
check, tweaking your tolerances as needed. For certain workloads where your
|
|
||||||
source files experience wild swings of file contents or counts, the spot check
|
|
||||||
may not suitable at all.
|
|
||||||
|
|
||||||
What if you add, delete, or change a bunch of your source files and you don't
|
|
||||||
want the spot check to fail the next time it's run? Run `borgmatic create` to
|
|
||||||
create a new backup, thereby allowing the next spot check to run against an
|
|
||||||
archive that contains your recent changes.
|
|
||||||
|
|
||||||
Because the spot check only looks at the most recent archive, you may not want
|
|
||||||
to run it immediately after a `create` action (borgmatic's default behavior).
|
|
||||||
Instead, it may make more sense to run the spot check on a separate schedule
|
|
||||||
from `create`.
|
|
||||||
|
|
||||||
|
|
||||||
### Check frequency
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.6.2</span> You can
|
|
||||||
optionally configure checks to run on a periodic basis rather than every time
|
|
||||||
borgmatic runs checks. For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- name: repository
|
|
||||||
frequency: 2 weeks
|
|
||||||
- name: archives
|
|
||||||
frequency: 1 month
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `consistency:` section of your configuration.
|
|
||||||
|
|
||||||
This tells borgmatic to run the `repository` consistency check at most once
|
|
||||||
every two weeks for a given repository and the `archives` check at most once a
|
|
||||||
month. The `frequency` value is a number followed by a unit of time, e.g. `3
|
|
||||||
days`, `1 week`, `2 months`, etc. The set of possible time units is as
|
|
||||||
follows (singular or plural):
|
|
||||||
|
|
||||||
* `second`
|
|
||||||
* `minute`
|
|
||||||
* `hour`
|
|
||||||
* `day`
|
|
||||||
* `week` (7 days)
|
|
||||||
* `month` (30 days)
|
|
||||||
* `year` (365 days)
|
|
||||||
|
|
||||||
The `frequency` defaults to `always` for a check configured without a
|
|
||||||
`frequency`, which means run this check every time checks run. But if you omit
|
|
||||||
consistency checks from configuration entirely, borgmatic runs full-repository
|
|
||||||
checks (`repository`) and per-archive checks (`archives`) within each
|
|
||||||
repository, at most once a month.
|
|
||||||
|
|
||||||
Unlike a real scheduler like cron, borgmatic only makes a best effort to run
|
|
||||||
checks on the configured frequency. It compares that frequency with how long
|
|
||||||
it's been since the last check for a given repository If it hasn't been long
|
|
||||||
enough, the check is skipped. And you still have to run `borgmatic check` (or
|
|
||||||
`borgmatic` without actions) in order for checks to run, even when a
|
|
||||||
`frequency` is configured!
|
|
||||||
|
|
||||||
This also applies *across* configuration files that have the same repository
|
|
||||||
configured. Make sure you have the same check frequency configured in each
|
|
||||||
though—or the most frequently configured check will apply.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.0</span>To support
|
|
||||||
this frequency logic, borgmatic records check timestamps within the
|
|
||||||
`~/.local/state/borgmatic/checks` directory. To override the `~/.local/state`
|
|
||||||
portion of this path, set the `user_state_directory` configuration option.
|
|
||||||
Alternatively, set the `XDG_STATE_HOME` environment variable.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.2</span>The
|
|
||||||
`STATE_DIRECTORY` environment variable also works for this purpose. It's set
|
|
||||||
by systemd if `StateDirectory=borgmatic` is added to borgmatic's systemd
|
|
||||||
service file.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.9.0</span>
|
|
||||||
borgmatic recorded check timestamps within the `~/.borgmatic` directory. At
|
|
||||||
that time, the path was configurable by the `borgmatic_source_directory`
|
|
||||||
configuration option (now deprecated).
|
|
||||||
|
|
||||||
If you want to temporarily ignore your configured frequencies, you can invoke
|
|
||||||
`borgmatic check --force` to run checks unconditionally.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.6</span> `borgmatic
|
|
||||||
check --force` runs `check` even if it's specified in the `skip_actions`
|
|
||||||
option.
|
|
||||||
|
|
||||||
|
|
||||||
### Check days
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.13</span> You can
|
|
||||||
optionally configure checks to only run on particular days of the week. For
|
|
||||||
instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- name: repository
|
|
||||||
only_run_on:
|
|
||||||
- Saturday
|
|
||||||
- Sunday
|
|
||||||
- name: archives
|
|
||||||
only_run_on:
|
|
||||||
- weekday
|
|
||||||
- name: spot
|
|
||||||
only_run_on:
|
|
||||||
- Friday
|
|
||||||
- weekend
|
|
||||||
```
|
|
||||||
|
|
||||||
Each day of the week is specified in the current locale (system
|
|
||||||
language/country settings). `weekend` and `weekday` are also accepted.
|
|
||||||
|
|
||||||
As with `frequency`, borgmatic only makes a best effort to run checks on the
|
|
||||||
given day of the week. For instance, if you run `borgmatic check` daily, then
|
|
||||||
every day borgmatic will have an opportunity to determine whether your checks
|
|
||||||
are configured to run on that day. If they are, then the checks run. If not,
|
|
||||||
they are skipped.
|
|
||||||
|
|
||||||
For instance, with the above configuration, if borgmatic is run on a Saturday,
|
|
||||||
the `repository` check will run. But on a Monday? The repository check will
|
|
||||||
get skipped. And if borgmatic is never run on a Saturday or a Sunday, that
|
|
||||||
check will never get a chance to run.
|
|
||||||
|
|
||||||
Also, the day of the week configuration applies *after* any configured
|
|
||||||
`frequency` for a check. So for instance, imagine the following configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- name: repository
|
|
||||||
frequency: 2 weeks
|
|
||||||
only_run_on:
|
|
||||||
- Monday
|
|
||||||
```
|
|
||||||
|
|
||||||
If you run borgmatic daily with that configuration, then borgmatic will first
|
|
||||||
wait two weeks after the previous check before running the check again—on the
|
|
||||||
first Monday after the `frequency` duration elapses.
|
|
||||||
|
|
||||||
|
|
||||||
### Running only checks
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.1</span> If you
|
|
||||||
would like to only run consistency checks without creating backups (for
|
|
||||||
instance with the `check` action on the command-line), you can omit
|
|
||||||
the `source_directories` option entirely.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.7.1</span> In older
|
|
||||||
versions of borgmatic, instead specify an empty `source_directories` value, as
|
|
||||||
it is a mandatory option there:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
location:
|
|
||||||
source_directories: []
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Disabling checks
|
|
||||||
|
|
||||||
If that's still too slow, you can disable consistency checks entirely,
|
|
||||||
either for a single repository or for all repositories.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.5</span> Disabling
|
|
||||||
all consistency checks looks like this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
skip_actions:
|
|
||||||
- check
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.5</span> Use this
|
|
||||||
configuration instead:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- name: disabled
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
`checks:` in the `consistency:` section of your configuration.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.6.2</span>
|
|
||||||
`checks:` was a plain list of strings without the `name:` part. For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
checks:
|
|
||||||
- disabled
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have multiple repositories in your borgmatic configuration file,
|
|
||||||
you can keep running consistency checks, but only against a subset of the
|
|
||||||
repositories:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
check_repositories:
|
|
||||||
- path/of/repository_to_check.borg
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, you can override your configuration file's consistency checks and
|
|
||||||
run particular checks via the command-line. For instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic check --only data --only extract
|
|
||||||
```
|
|
||||||
|
|
||||||
This is useful for running slow consistency checks on an infrequent basis,
|
|
||||||
separate from your regular checks. It is still subject to any configured
|
|
||||||
check frequencies unless the `--force` flag is used.
|
|
||||||
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 15
|
order: 15
|
||||||
---
|
---
|
||||||
## Source code
|
<span data-pagefind-weight="0.25">
|
||||||
|
|
||||||
To get set up to develop on borgmatic, first [`install
|
To get set up to develop on borgmatic, first [`install
|
||||||
uv`](https://docs.astral.sh/uv/) to make managing your borgmatic environment
|
uv`](https://docs.astral.sh/uv/) to make managing your borgmatic environment
|
||||||
@@ -35,7 +35,7 @@ uv tool install --editable .
|
|||||||
```
|
```
|
||||||
|
|
||||||
Or to work on the [Apprise
|
Or to work on the [Apprise
|
||||||
hook](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#apprise-hook),
|
hook](https://torsion.org/borgmatic/reference/configuration/monitoring/apprise/),
|
||||||
change that last line to:
|
change that last line to:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -43,7 +43,7 @@ uv tool install --editable .[Apprise]
|
|||||||
```
|
```
|
||||||
|
|
||||||
To get oriented with the borgmatic source code, have a look at the [source
|
To get oriented with the borgmatic source code, have a look at the [source
|
||||||
code reference](https://torsion.org/borgmatic/docs/reference/source-code/).
|
code reference](https://torsion.org/borgmatic/reference/source-code/).
|
||||||
|
|
||||||
|
|
||||||
### Source packages
|
### Source packages
|
||||||
@@ -113,6 +113,10 @@ them:
|
|||||||
tox -e spell
|
tox -e spell
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See the [code style
|
||||||
|
documentation](https://torsion.org/borgmatic/reference/source-code/#code-style)
|
||||||
|
for more specifics about borgmatic's own code style.
|
||||||
|
|
||||||
|
|
||||||
### End-to-end tests
|
### End-to-end tests
|
||||||
|
|
||||||
@@ -157,36 +161,6 @@ Then you'll be able to run end-to-end tests as per normal, and the test script
|
|||||||
will automatically use your non-root Podman socket instead of a Docker socket.
|
will automatically use your non-root Podman socket instead of a Docker socket.
|
||||||
|
|
||||||
|
|
||||||
## Code style
|
|
||||||
|
|
||||||
When writing code for borgmatic, start with [PEP
|
|
||||||
8](https://www.python.org/dev/peps/pep-0008/). But then, apply the following
|
|
||||||
deviations from it:
|
|
||||||
|
|
||||||
* For strings, prefer single quotes over double quotes.
|
|
||||||
* Limit all lines to a maximum of 100 characters.
|
|
||||||
* Use trailing commas within multiline values or argument lists.
|
|
||||||
* For multiline constructs, put opening and closing delimiters on lines
|
|
||||||
separate from their contents.
|
|
||||||
* Within multiline constructs, use standard four-space indentation. Don't align
|
|
||||||
indentation with an opening delimiter.
|
|
||||||
* In general, spell out words in variable names instead of shortening them.
|
|
||||||
So, think `index` instead of `idx`. There are some notable exceptions to
|
|
||||||
this though (like `config`).
|
|
||||||
* Favor blank lines around logical code groupings, `if` statements,
|
|
||||||
`return`s, etc. Readability is more important than packing code tightly.
|
|
||||||
* Import fully qualified Python modules instead of importing individual
|
|
||||||
functions, classes, or constants. E.g., do `import os.path` instead of
|
|
||||||
`from os import path`. (Some exceptions to this are made in tests.)
|
|
||||||
* Only use classes and OOP as a last resort, such as when integrating with
|
|
||||||
Python libraries that require it.
|
|
||||||
* Prefer functional code where it makes sense, e.g. when constructing a
|
|
||||||
command (to subsequently execute imperatively).
|
|
||||||
|
|
||||||
Since borgmatic uses Ruff for code lining and formatting, many other code style
|
|
||||||
requirements are also enforced when running automated tests.
|
|
||||||
|
|
||||||
|
|
||||||
## Continuous integration
|
## Continuous integration
|
||||||
|
|
||||||
Each commit to
|
Each commit to
|
||||||
@@ -217,7 +191,7 @@ This script assumes you have permission to run `docker`. If you don't, then
|
|||||||
you may need to run with `sudo`.
|
you may need to run with `sudo`.
|
||||||
|
|
||||||
After you run the script, you can point your web browser at
|
After you run the script, you can point your web browser at
|
||||||
http://localhost:8080 to view the documentation with your changes.
|
http://localhost:8080/borgmatic/ to view the documentation with your changes.
|
||||||
|
|
||||||
To close the documentation server, ctrl-C the script. Note that it does not
|
To close the documentation server, ctrl-C the script. Note that it does not
|
||||||
currently auto-reload, so you'll need to stop it and re-run it for any
|
currently auto-reload, so you'll need to stop it and re-run it for any
|
||||||
@@ -233,3 +207,5 @@ borgmatic's developer build for documentation optionally supports using
|
|||||||
Setting up Podman is outside the scope of this documentation. But once you
|
Setting up Podman is outside the scope of this documentation. But once you
|
||||||
install and configure Podman, then `scripts/dev-docs` should automatically use
|
install and configure Podman, then `scripts/dev-docs` should automatically use
|
||||||
Podman instead of Docker.
|
Podman instead of Docker.
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 7
|
order: 7
|
||||||
---
|
---
|
||||||
## Extract
|
|
||||||
|
|
||||||
When the worst happens—or you want to test your backups—the first step is
|
When the worst happens—or you want to test your backups—the first step is
|
||||||
to figure out which archive to extract. A good way to do that is to use the
|
to figure out which archive to extract. A good way to do that is to use the
|
||||||
`repo-list` action:
|
`repo-list` action:
|
||||||
@@ -32,8 +30,6 @@ and therefore the latest timestamp, run a command like:
|
|||||||
borgmatic extract --archive host-2023-01-02T04:06:07.080910
|
borgmatic extract --archive host-2023-01-02T04:06:07.080910
|
||||||
```
|
```
|
||||||
|
|
||||||
(No borgmatic `extract` action? Upgrade borgmatic!)
|
|
||||||
|
|
||||||
Or simplify this to:
|
Or simplify this to:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -79,7 +75,7 @@ run the `extract` command above, borgmatic will extract `/var/path/1` and
|
|||||||
|
|
||||||
If you're not sure which archive contains the files you're looking for, you
|
If you're not sure which archive contains the files you're looking for, you
|
||||||
can [search across
|
can [search across
|
||||||
archives](https://torsion.org/borgmatic/docs/how-to/inspect-your-backups/#searching-for-a-file).
|
archives](https://torsion.org/borgmatic/how-to/inspect-your-backups/#searching-for-a-file).
|
||||||
|
|
||||||
|
|
||||||
## Extract to a particular destination
|
## Extract to a particular destination
|
||||||
@@ -100,7 +96,7 @@ files with extracted files unless that is your intent.
|
|||||||
|
|
||||||
The `borgmatic extract` command only extracts files. To restore a database,
|
The `borgmatic extract` command only extracts files. To restore a database,
|
||||||
please see the [documentation on database backups and
|
please see the [documentation on database backups and
|
||||||
restores](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/).
|
restores](https://torsion.org/borgmatic/how-to/backup-your-databases/).
|
||||||
borgmatic does not perform database restoration as part of `borgmatic extract`
|
borgmatic does not perform database restoration as part of `borgmatic extract`
|
||||||
so that you can extract files from your archive without impacting your live
|
so that you can extract files from your archive without impacting your live
|
||||||
databases.
|
databases.
|
||||||
@@ -167,7 +163,7 @@ To support this, borgmatic creates a manifest file that records the paths of
|
|||||||
all the borgmatic configuration files stored within an archive. The file gets
|
all the borgmatic configuration files stored within an archive. The file gets
|
||||||
written to borgmatic's runtime directory on disk and then stored within the
|
written to borgmatic's runtime directory on disk and then stored within the
|
||||||
archive. See the [runtime directory
|
archive. See the [runtime directory
|
||||||
documentation](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory)
|
documentation](https://torsion.org/borgmatic/reference/configuration/runtime-directory/)
|
||||||
for how and where that happens.
|
for how and where that happens.
|
||||||
|
|
||||||
To extract the configuration files from an archive, use the `config bootstrap`
|
To extract the configuration files from an archive, use the `config bootstrap`
|
||||||
@@ -199,8 +195,9 @@ If you want to extract the configuration file from a specific archive, use the
|
|||||||
borgmatic config bootstrap --repository repo.borg --archive host-2023-01-02T04:06:07.080910 --destination /tmp
|
borgmatic config bootstrap --repository repo.borg --archive host-2023-01-02T04:06:07.080910 --destination /tmp
|
||||||
```
|
```
|
||||||
|
|
||||||
See the output of `config bootstrap --help` for additional flags you may need
|
See the [config bootstrap action
|
||||||
for bootstrapping.
|
documentation](https://torsion.org/borgmatic/reference/command-line/actions/config-bootstrap/)
|
||||||
|
for additional flags you may need for bootstrapping.
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.3</span>
|
<span class="minilink minilink-addedin">New in version 1.9.3</span>
|
||||||
If your borgmatic configuration files contain sensitive information you don't
|
If your borgmatic configuration files contain sensitive information you don't
|
||||||
|
|||||||
@@ -5,42 +5,18 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 5
|
order: 5
|
||||||
---
|
---
|
||||||
## Backup progress
|
|
||||||
|
|
||||||
By default, borgmatic runs proceed silently except in the case of warnings or
|
By default, borgmatic runs proceed silently except in the case of warnings or
|
||||||
errors. But if you'd like to to get additional information about the progress of
|
errors. But if you'd like to to get additional information about the progress of
|
||||||
the backup as it proceeds, use the verbosity option:
|
the backup as it proceeds, see the [logging
|
||||||
|
documentation](https://torsion.org/borgmatic/reference/command-line/logging/)
|
||||||
```bash
|
for details.
|
||||||
borgmatic --verbosity 1
|
|
||||||
```
|
|
||||||
|
|
||||||
This lists the files that borgmatic is archiving, which are those that are new
|
|
||||||
or changed since the last backup.
|
|
||||||
|
|
||||||
Or, for even more progress and debug spew:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic --verbosity 2
|
|
||||||
```
|
|
||||||
|
|
||||||
The full set of verbosity levels are:
|
|
||||||
|
|
||||||
* `-2`: disable output entirely <span class="minilink minilink-addedin">New in borgmatic 1.7.14</span>
|
|
||||||
* `-1`: only show errors
|
|
||||||
* `0`: default output
|
|
||||||
* `1`: some additional output (informational level)
|
|
||||||
* `2`: lots of additional output (debug level)
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the
|
|
||||||
verbosity in your borgmatic configuration via the `verbosity` option.
|
|
||||||
|
|
||||||
|
|
||||||
## Backup summary
|
## Backup summary
|
||||||
|
|
||||||
If you're less concerned with progress during a backup, and you only want to
|
If you're less concerned with progress during a backup, and you only want to see
|
||||||
see the summary of archive statistics at the end, you can use the stats
|
the summary of archive statistics at the end, use the stats option when
|
||||||
option when performing a backup:
|
performing a backup:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
borgmatic --stats
|
borgmatic --stats
|
||||||
@@ -63,8 +39,6 @@ with `--format`. Refer to the [borg list --format
|
|||||||
documentation](https://borgbackup.readthedocs.io/en/stable/usage/list.html#the-format-specifier-syntax)
|
documentation](https://borgbackup.readthedocs.io/en/stable/usage/list.html#the-format-specifier-syntax)
|
||||||
for available values.
|
for available values.
|
||||||
|
|
||||||
(No borgmatic `list` or `info` actions? Upgrade borgmatic!)
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.0</span> There are
|
<span class="minilink minilink-addedin">New in version 1.9.0</span> There are
|
||||||
also `repo-list` and `repo-info` actions for displaying repository information
|
also `repo-list` and `repo-info` actions for displaying repository information
|
||||||
with Borg 2.x:
|
with Borg 2.x:
|
||||||
@@ -75,7 +49,7 @@ borgmatic repo-info
|
|||||||
```
|
```
|
||||||
|
|
||||||
See the [borgmatic command-line
|
See the [borgmatic command-line
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/command-line/) for
|
reference](https://torsion.org/borgmatic/reference/command-line/) for
|
||||||
more information.
|
more information.
|
||||||
|
|
||||||
|
|
||||||
@@ -90,9 +64,8 @@ purpose. For instance, if you're looking for a `foo.txt`:
|
|||||||
borgmatic list --find foo.txt
|
borgmatic list --find foo.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
This will list your archives and indicate those with files matching
|
This lists your archives and indicate those with files matching `*foo.txt*`
|
||||||
`*foo.txt*` anywhere in the archive. The `--find` parameter can alternatively
|
anywhere in the archive. The `--find` parameter can alternatively be a [Borg
|
||||||
be a [Borg
|
|
||||||
pattern](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-patterns).
|
pattern](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-patterns).
|
||||||
|
|
||||||
To limit the archives searched, use the standard `list` parameters for
|
To limit the archives searched, use the standard `list` parameters for
|
||||||
@@ -105,8 +78,8 @@ borgmatic list --find foo.txt --last 5
|
|||||||
|
|
||||||
## Listing database dumps
|
## Listing database dumps
|
||||||
|
|
||||||
If you have enabled borgmatic's [database
|
If you've enabled borgmatic's [database
|
||||||
hooks](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/), you
|
hooks](https://torsion.org/borgmatic/how-to/backup-your-databases/), you
|
||||||
can list backed up database dumps via borgmatic. For example:
|
can list backed up database dumps via borgmatic. For example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -123,7 +96,7 @@ doesn't store the leading `/`.)
|
|||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.2 and
|
<span class="minilink minilink-addedin">With Borg version 1.2 and
|
||||||
earlier</span>Database dump files are stored at a path dependent on the [runtime
|
earlier</span>Database dump files are stored at a path dependent on the [runtime
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory)
|
directory](https://torsion.org/borgmatic/reference/configuration/runtime-directory/)
|
||||||
in use at the time the archive was created, as Borg 1.2 and earlier do not
|
in use at the time the archive was created, as Borg 1.2 and earlier do not
|
||||||
support path rewriting.
|
support path rewriting.
|
||||||
|
|
||||||
@@ -133,103 +106,13 @@ the backup archive (where `~` was expanded to the home directory of the user
|
|||||||
who performed the backup). This applied with all versions of Borg.
|
who performed the backup). This applied with all versions of Borg.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="rate-limiting"></a>
|
||||||
|
<a id="logging-to-file"></a>
|
||||||
|
|
||||||
|
|
||||||
## Logging
|
## Logging
|
||||||
|
|
||||||
By default, borgmatic logs to the console. You can enable simultaneous syslog
|
By default, borgmatic only logs to the console. But to enable simultaneous
|
||||||
logging and customize its log level with the `--syslog-verbosity` flag, which
|
syslog or file logging, see the [logging
|
||||||
is independent from the console logging `--verbosity` flag described above.
|
documentation](https://torsion.org/borgmatic/reference/command-line/logging/)
|
||||||
For instance, to enable syslog logging, run:
|
for details.
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic --syslog-verbosity 1
|
|
||||||
```
|
|
||||||
|
|
||||||
To increase syslog logging further to include debugging information, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic --syslog-verbosity 2
|
|
||||||
```
|
|
||||||
|
|
||||||
See above for further details about the verbosity levels.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the
|
|
||||||
syslog verbosity in your borgmatic configuration via the `syslog_verbosity`
|
|
||||||
option.
|
|
||||||
|
|
||||||
Where these logs show up depends on your particular system. If you're using
|
|
||||||
systemd, try running `journalctl -xe`. Otherwise, try viewing
|
|
||||||
`/var/log/syslog` or similar.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.3</span>borgmatic
|
|
||||||
logged to syslog by default whenever run at a non-interactive console.
|
|
||||||
|
|
||||||
### Rate limiting
|
|
||||||
|
|
||||||
If you are using rsyslog or systemd's journal, be aware that by default they
|
|
||||||
both throttle the rate at which logging occurs. So you may need to change
|
|
||||||
either [the global rate
|
|
||||||
limit](https://www.rootusers.com/how-to-change-log-rate-limiting-in-linux/) or
|
|
||||||
[the per-service rate
|
|
||||||
limit](https://www.freedesktop.org/software/systemd/man/journald.conf.html#RateLimitIntervalSec=)
|
|
||||||
if you're finding that borgmatic logs are missing.
|
|
||||||
|
|
||||||
Note that the [sample borgmatic systemd service
|
|
||||||
file](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#systemd)
|
|
||||||
already has this rate limit disabled for systemd's journal.
|
|
||||||
|
|
||||||
|
|
||||||
### Logging to file
|
|
||||||
|
|
||||||
If you don't want to use syslog, and you'd rather borgmatic log to a plain
|
|
||||||
file, use the `--log-file` flag:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic --log-file /path/to/file.log
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that if you use the `--log-file` flag, you are responsible for rotating
|
|
||||||
the log file so it doesn't grow too large, for example with
|
|
||||||
[logrotate](https://wiki.archlinux.org/index.php/Logrotate).
|
|
||||||
|
|
||||||
You can use the `--log-file-verbosity` flag to customize the log file's log level:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic --log-file /path/to/file.log --log-file-verbosity 2
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the log
|
|
||||||
file verbosity in your borgmatic configuration via the `log_file_verbosity`
|
|
||||||
option.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.11</span> Use the
|
|
||||||
`--log-file-format` flag to override the default log message format. This
|
|
||||||
format string can contain a series of named placeholders wrapped in curly
|
|
||||||
brackets. For instance, the default log format is: `[{asctime}] {levelname}:
|
|
||||||
{message}`. This means each log message is recorded as the log time (in square
|
|
||||||
brackets), a logging level name, a colon, and the actual log message.
|
|
||||||
|
|
||||||
So if you only want each log message to get logged *without* a timestamp or a
|
|
||||||
logging level name:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic --log-file /path/to/file.log --log-file-format "{message}"
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is a list of available placeholders:
|
|
||||||
|
|
||||||
* `{asctime}`: time the log message was created
|
|
||||||
* `{levelname}`: level of the log message (`INFO`, `DEBUG`, etc.)
|
|
||||||
* `{lineno}`: line number in the source file where the log message originated
|
|
||||||
* `{message}`: actual log message
|
|
||||||
* `{pathname}`: path of the source file where the log message originated
|
|
||||||
|
|
||||||
See the [Python logging
|
|
||||||
documentation](https://docs.python.org/3/library/logging.html#logrecord-attributes)
|
|
||||||
for additional placeholders.
|
|
||||||
|
|
||||||
Note that this `--log-file-format` flag only applies to the specified
|
|
||||||
`--log-file` and not to syslog or other logging.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the
|
|
||||||
defaults for these flags in your borgmatic configuration via the `log_file` and
|
|
||||||
`log_file_format` options.
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 3
|
order: 3
|
||||||
---
|
---
|
||||||
## Multiple repositories
|
|
||||||
|
|
||||||
If you really care about your data, you probably want more than one backup of
|
If you really care about your data, you probably want more than one backup of
|
||||||
it. borgmatic supports this in its configuration by specifying multiple backup
|
it. borgmatic supports this in its configuration by specifying multiple backup
|
||||||
repositories. Here's an example:
|
repositories. Here's an example:
|
||||||
@@ -51,7 +49,7 @@ for more information on how to specify local and remote repository paths.
|
|||||||
What if you want borgmatic to backup to multiple repositories—while also
|
What if you want borgmatic to backup to multiple repositories—while also
|
||||||
setting different options for each one? In that case, you'll need to use
|
setting different options for each one? In that case, you'll need to use
|
||||||
[a separate borgmatic configuration file for each
|
[a separate borgmatic configuration file for each
|
||||||
repository](https://torsion.org/borgmatic/docs/how-to/make-per-application-backups/)
|
repository](https://torsion.org/borgmatic/how-to/make-per-application-backups/)
|
||||||
instead of the multiple repositories in one configuration file as described
|
instead of the multiple repositories in one configuration file as described
|
||||||
above. That's because all of the repositories in a particular configuration
|
above. That's because all of the repositories in a particular configuration
|
||||||
file get the same options applied.
|
file get the same options applied.
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: How to make per-application backups
|
title: How to make per-application backups
|
||||||
eleventyNavigation:
|
eleventyNavigation:
|
||||||
key: 🔀 Make per-application backups
|
key: 🗂️ Make per-application backups
|
||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 1
|
order: 1
|
||||||
---
|
---
|
||||||
## Multiple backup configurations
|
|
||||||
|
|
||||||
You may find yourself wanting to create different backup policies for
|
You may find yourself wanting to create different backup policies for
|
||||||
different applications on your system or even for different backup
|
different applications on your system or even for different backup
|
||||||
repositories. For instance, you might want one backup configuration for your
|
repositories. For instance, you might want one backup configuration for your
|
||||||
@@ -50,638 +48,49 @@ Additionally, the `~/.config/borgmatic.d/` directory works the same way as
|
|||||||
`/etc/borgmatic.d`.
|
`/etc/borgmatic.d`.
|
||||||
|
|
||||||
If you need even more customizability, you can specify alternate configuration
|
If you need even more customizability, you can specify alternate configuration
|
||||||
paths on the command-line with borgmatic's `--config` flag. (See `borgmatic
|
paths on the command-line with borgmatic's `--config` flag. (See the
|
||||||
--help` for more information.) For instance, if you want to schedule your
|
[command-line
|
||||||
various borgmatic backups to run at different times, you'll need multiple
|
documentation](https://torsion.org/borgmatic/reference/command-line/) for more
|
||||||
entries in your [scheduling software of
|
information.) For instance, if you want to schedule your various borgmatic
|
||||||
choice](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot),
|
backups to run at different times, you'll need multiple entries in your
|
||||||
each entry using borgmatic's `--config` flag instead of relying on
|
[scheduling software of
|
||||||
|
choice](https://torsion.org/borgmatic/how-to/set-up-backups/#autopilot), each
|
||||||
|
entry using borgmatic's `--config` flag instead of relying on
|
||||||
`/etc/borgmatic.d`.
|
`/etc/borgmatic.d`.
|
||||||
|
|
||||||
|
|
||||||
## Archive naming
|
## Limitations
|
||||||
|
|
||||||
If you've got multiple borgmatic configuration files, you might want to create
|
borgmatic does not currently support its own parallelism—being run multiple
|
||||||
archives with different naming schemes for each one. This is especially handy
|
times on the same machine simultaneously. In particular, many of the [data
|
||||||
if each configuration file is backing up to the same Borg repository but you
|
source
|
||||||
still want to be able to distinguish backup archives for one application from
|
hooks](https://torsion.org/borgmatic/reference/configuration/data-sources/) rely
|
||||||
another.
|
on global borgmatic runtime files which can't be shared across processes, and
|
||||||
|
therefore multiple borgmatic instances on the same machine would interfere with
|
||||||
borgmatic supports this use case with an `archive_name_format` option. The
|
each other.
|
||||||
idea is that you define a string format containing a number of [Borg
|
|
||||||
placeholders](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-placeholders),
|
A single borgmatic instance also doesn't currently support running multiple Borg
|
||||||
and borgmatic uses that format to name any new archive it creates. For
|
instances in parallel on the same machine.
|
||||||
instance:
|
|
||||||
|
|
||||||
```yaml
|
<a id="archive-naming"></a>
|
||||||
archive_name_format: home-directories-{now}
|
<a id="configuration-includes"></a>
|
||||||
```
|
<a id="configuration-overrides"></a>
|
||||||
|
<a id="constant-interpolation"></a>
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `storage:` section of your configuration.
|
## Related features
|
||||||
|
|
||||||
This example means that when borgmatic creates an archive, its name will start
|
Once you've got multiple configuration files, there are a few other borgmatic
|
||||||
with the string `home-directories-` and end with a timestamp for its creation
|
features that you might find handy:
|
||||||
time. If `archive_name_format` is unspecified, the default with Borg 1 is
|
|
||||||
`{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}`, meaning your system hostname plus a
|
* Use different archive naming schemes in each configuration file with the
|
||||||
timestamp in a particular format.
|
[archive name
|
||||||
|
format](https://torsion.org/borgmatic/reference/configuration/archive-name-format/)
|
||||||
<span class="minilink minilink-addedin">New in borgmatic version 1.9.0 with
|
feature.
|
||||||
Borg version 2.x</span>The default is just `{hostname}`, as Borg 2 does not
|
* Share common options across configuration files with
|
||||||
require unique archive names; identical archive names form a common "series"
|
[includes](https://torsion.org/borgmatic/reference/configuration/includes/).
|
||||||
that can be targeted together.
|
* Override configuration file options from the command-line with
|
||||||
|
[overrides](https://torsion.org/borgmatic/reference/command-line/overrides/).
|
||||||
|
* Also check out the
|
||||||
### Archive filtering
|
[constants](https://torsion.org/borgmatic/reference/configuration/constants/)
|
||||||
|
feature for defining custom per-configuration-file constants.
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.11</span> borgmatic
|
|
||||||
uses the `archive_name_format` option to automatically limit which archives
|
|
||||||
get used for actions operating on multiple archives. This prevents, for
|
|
||||||
instance, duplicate archives from showing up in `repo-list` or `info`
|
|
||||||
results—even if the same repository appears in multiple borgmatic
|
|
||||||
configuration files. To take advantage of this feature, use a different
|
|
||||||
`archive_name_format` in each configuration file.
|
|
||||||
|
|
||||||
Under the hood, borgmatic accomplishes this by substituting globs for certain
|
|
||||||
ephemeral data placeholders in your `archive_name_format`—and using the result
|
|
||||||
to filter archives when running supported actions.
|
|
||||||
|
|
||||||
For instance, let's say that you have this in your configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
archive_name_format: {hostname}-user-data-{now}
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `storage:` section of your configuration.
|
|
||||||
|
|
||||||
borgmatic considers `{now}` an emphemeral data placeholder that will probably
|
|
||||||
change per archive, while `{hostname}` won't. So it turns the example value
|
|
||||||
into `{hostname}-user-data-*` and applies it to filter down the set of
|
|
||||||
archives used for actions like `repo-list`, `info`, `prune`, `check`, etc.
|
|
||||||
|
|
||||||
The end result is that when borgmatic runs the actions for a particular
|
|
||||||
application-specific configuration file, it only operates on the archives
|
|
||||||
created for that application. But this doesn't apply to actions like `compact`
|
|
||||||
that operate on an entire repository.
|
|
||||||
|
|
||||||
If this behavior isn't quite smart enough for your needs, you can use the
|
|
||||||
`match_archives` option to override the pattern that borgmatic uses for
|
|
||||||
filtering archives. For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
archive_name_format: {hostname}-user-data-{now}
|
|
||||||
match_archives: sh:myhost-user-data-*
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.x</span>Use a shell
|
|
||||||
pattern for the `match_archives` value and see the [Borg patterns
|
|
||||||
documentation](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-patterns)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 2.x</span>See the
|
|
||||||
[match archives
|
|
||||||
documentation](https://borgbackup.readthedocs.io/en/2.0.0b16/usage/help.html#borg-help-match-archives).
|
|
||||||
|
|
||||||
Some borgmatic command-line actions also have a `--match-archives` flag that
|
|
||||||
overrides both the auto-matching behavior and the `match_archives`
|
|
||||||
configuration option.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.7.11</span> The way
|
|
||||||
to limit the archives used for the `prune` action was a `prefix` option in the
|
|
||||||
`retention` section for matching against the start of archive names. And the
|
|
||||||
option for limiting the archives used for the `check` action was a separate
|
|
||||||
`prefix` in the `consistency` section. Both of these options are deprecated in
|
|
||||||
favor of the auto-matching behavior (or `match_archives`/`--match-archives`)
|
|
||||||
in newer versions of borgmatic.
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration includes
|
|
||||||
|
|
||||||
Once you have multiple different configuration files, you might want to share
|
|
||||||
common configuration options across these files without having to copy and paste
|
|
||||||
them. To achieve this, you can put fragments of common configuration options
|
|
||||||
into a file and then include or inline that file into one or more borgmatic
|
|
||||||
configuration files.
|
|
||||||
|
|
||||||
Let's say that you want to include common consistency check configuration across all
|
|
||||||
of your configuration files. You could do that in each configuration file with
|
|
||||||
the following:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
checks:
|
|
||||||
!include /etc/borgmatic/common_checks.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> These
|
|
||||||
options were organized into sections like `location:` and `consistency:`.
|
|
||||||
|
|
||||||
The contents of `common_checks.yaml` could be:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: repository
|
|
||||||
frequency: 3 weeks
|
|
||||||
- name: archives
|
|
||||||
frequency: 2 weeks
|
|
||||||
```
|
|
||||||
|
|
||||||
To prevent borgmatic from trying to load these configuration fragments by
|
|
||||||
themselves and complaining that they are not valid configuration files, you
|
|
||||||
should put them in a directory other than `/etc/borgmatic.d/`. (A subdirectory
|
|
||||||
is fine.)
|
|
||||||
|
|
||||||
When a configuration include is a relative path, borgmatic loads it from either
|
|
||||||
the current working directory or from the directory containing the file doing
|
|
||||||
the including.
|
|
||||||
|
|
||||||
Note that this form of include must be a value rather than an option name. For
|
|
||||||
example, this will not work:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
# Don't do this. It won't work!
|
|
||||||
!include /etc/borgmatic/common_checks.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
But if you do want to merge in a option name *and* its values, keep reading!
|
|
||||||
|
|
||||||
|
|
||||||
## Include merging
|
|
||||||
|
|
||||||
If you need to get even fancier and merge in common configuration options, you
|
|
||||||
can perform a YAML merge of included configuration using the YAML `<<` key.
|
|
||||||
For instance, here's an example of a main configuration file that pulls in
|
|
||||||
retention and consistency checks options via a single include:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
<<: !include /etc/borgmatic/common.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
This is what `common.yaml` might look like:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
keep_hourly: 24
|
|
||||||
keep_daily: 7
|
|
||||||
|
|
||||||
checks:
|
|
||||||
- name: repository
|
|
||||||
frequency: 3 weeks
|
|
||||||
- name: archives
|
|
||||||
frequency: 2 weeks
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> These
|
|
||||||
options were organized into sections like `retention:` and `consistency:`.
|
|
||||||
|
|
||||||
Once this include gets merged in, the resulting configuration has all of the
|
|
||||||
options from the original configuration file *and* the options from the
|
|
||||||
include.
|
|
||||||
|
|
||||||
Note that this `<<` include merging syntax is only for merging in mappings
|
|
||||||
(configuration options and their values). If you'd like to include a single
|
|
||||||
value directly, please see above about standard includes.
|
|
||||||
|
|
||||||
|
|
||||||
### Multiple merge includes
|
|
||||||
|
|
||||||
borgmatic has a limitation preventing multiple `<<` include merges per file or
|
|
||||||
option value. This means you can do a single `<<` merge at the global level,
|
|
||||||
another `<<` within each nested option value, etc. (This is a YAML
|
|
||||||
limitation.) For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
# This won't work! You can't do multiple merges like this at the same level.
|
|
||||||
<<: !include common1.yaml
|
|
||||||
<<: !include common2.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
But read on for a way around this.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.1</span> You can
|
|
||||||
include and merge multiple configuration files all at once. For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
<<: !include [common1.yaml, common2.yaml, common3.yaml]
|
|
||||||
```
|
|
||||||
|
|
||||||
This merges in each included configuration file in turn, such that later files
|
|
||||||
replace the options in earlier ones.
|
|
||||||
|
|
||||||
Here's another way to do the same thing:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
<<: !include
|
|
||||||
- common1.yaml
|
|
||||||
- common2.yaml
|
|
||||||
- common3.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Deep merge
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.6.0</span> borgmatic
|
|
||||||
performs a deep merge of merged include files, meaning that values are merged
|
|
||||||
at all levels in the two configuration files. This allows you to include
|
|
||||||
common configuration—up to full borgmatic configuration files—while overriding
|
|
||||||
only the parts you want to customize.
|
|
||||||
|
|
||||||
For instance, here's an example of a main configuration file that pulls in
|
|
||||||
options via an include and then overrides one of them locally:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
<<: !include /etc/borgmatic/common.yaml
|
|
||||||
|
|
||||||
constants:
|
|
||||||
base_directory: /opt
|
|
||||||
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
```
|
|
||||||
|
|
||||||
This is what `common.yaml` might look like:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
constants:
|
|
||||||
app_name: myapp
|
|
||||||
base_directory: /var/lib
|
|
||||||
```
|
|
||||||
|
|
||||||
Once this include gets merged in, the resulting configuration would have an
|
|
||||||
`app_name` value of `myapp` and an overridden `base_directory` value of
|
|
||||||
`/opt`.
|
|
||||||
|
|
||||||
When there's an option collision between the local file and the merged
|
|
||||||
include, the local file's option takes precedence.
|
|
||||||
|
|
||||||
|
|
||||||
#### List merge
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.6.1</span> Colliding
|
|
||||||
list values are appended together.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.12</span> If there
|
|
||||||
is a list value from an include that you *don't* want in your local
|
|
||||||
configuration file, you can omit it with an `!omit` tag. For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
<<: !include /etc/borgmatic/common.yaml
|
|
||||||
|
|
||||||
source_directories:
|
|
||||||
- !omit /home
|
|
||||||
- /var
|
|
||||||
```
|
|
||||||
|
|
||||||
And `common.yaml` like this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
source_directories:
|
|
||||||
- /home
|
|
||||||
- /etc
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put the
|
|
||||||
`source_directories` option in the `location:` section of your configuration.
|
|
||||||
|
|
||||||
Once this include gets merged in, the resulting configuration will have a
|
|
||||||
`source_directories` value of `/etc` and `/var`—with `/home` omitted.
|
|
||||||
|
|
||||||
This feature currently only works on scalar (e.g. string or number) list items
|
|
||||||
and will not work elsewhere in a configuration file. Be sure to put the
|
|
||||||
`!omit` tag *before* the list item (after the dash). Putting `!omit` after the
|
|
||||||
list item will not work, as it gets interpreted as part of the string. Here's
|
|
||||||
an example of some things not to do:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
<<: !include /etc/borgmatic/common.yaml
|
|
||||||
|
|
||||||
source_directories:
|
|
||||||
# Do not do this! It will not work. "!omit" belongs before "/home".
|
|
||||||
- /home !omit
|
|
||||||
|
|
||||||
# Do not do this either! "!omit" only works on scalar list items.
|
|
||||||
repositories: !omit
|
|
||||||
# Also do not do this for the same reason! This is a list item, but it's
|
|
||||||
# not a scalar.
|
|
||||||
- !omit path: repo.borg
|
|
||||||
```
|
|
||||||
|
|
||||||
Additionally, the `!omit` tag only works in a configuration file that also
|
|
||||||
performs a merge include with `<<: !include`. It doesn't make sense within,
|
|
||||||
for instance, an included configuration file itself (unless it in turn
|
|
||||||
performs its own merge include). That's because `!omit` only applies to the
|
|
||||||
file doing the include; it doesn't work in reverse or propagate through
|
|
||||||
includes.
|
|
||||||
|
|
||||||
|
|
||||||
### Shallow merge
|
|
||||||
|
|
||||||
Even though deep merging is generally pretty handy for included files,
|
|
||||||
sometimes you want specific options in the local file to take precedence over
|
|
||||||
included options—without any merging occurring for them.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.12</span> That's
|
|
||||||
where the `!retain` tag comes in. Whenever you're merging an included file
|
|
||||||
into your configuration file, you can optionally add the `!retain` tag to
|
|
||||||
particular local mappings or lists to retain the local values and ignore
|
|
||||||
included values.
|
|
||||||
|
|
||||||
For instance, start with this configuration file containing the `!retain` tag
|
|
||||||
on the `retention` mapping:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
<<: !include /etc/borgmatic/common.yaml
|
|
||||||
|
|
||||||
repositories:
|
|
||||||
- path: repo.borg
|
|
||||||
|
|
||||||
checks: !retain
|
|
||||||
- name: repository
|
|
||||||
```
|
|
||||||
|
|
||||||
And `common.yaml` like this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repositories:
|
|
||||||
- path: common.borg
|
|
||||||
|
|
||||||
checks:
|
|
||||||
- name: archives
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> These
|
|
||||||
options were organized into sections like `location:` and `consistency:`.
|
|
||||||
|
|
||||||
Once this include gets merged in, the resulting configuration will have a
|
|
||||||
`checks` value with a name of `repository` and no other values. That's because
|
|
||||||
the `!retain` tag says to retain the local version of `checks` and ignore any
|
|
||||||
values coming in from the include. But because the `repositories` list doesn't
|
|
||||||
have a `!retain` tag, it still gets merged together to contain both
|
|
||||||
`common.borg` and `repo.borg`.
|
|
||||||
|
|
||||||
The `!retain` tag can only be placed on mappings (keys/values) and lists, and
|
|
||||||
it goes right after the name of the option (and its colon) on the same line.
|
|
||||||
The effects of `!retain` are recursive, meaning that if you place a `!retain`
|
|
||||||
tag on a top-level mapping, even deeply nested values within it will not be
|
|
||||||
merged.
|
|
||||||
|
|
||||||
Additionally, the `!retain` tag only works in a configuration file that also
|
|
||||||
performs a merge include with `<<: !include`. It doesn't make sense within,
|
|
||||||
for instance, an included configuration file itself (unless it in turn
|
|
||||||
performs its own merge include). That's because `!retain` only applies to the
|
|
||||||
file doing the include; it doesn't work in reverse or propagate through
|
|
||||||
includes.
|
|
||||||
|
|
||||||
|
|
||||||
## Debugging includes
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.15</span> If you'd
|
|
||||||
like to see what the loaded configuration looks like after includes get merged
|
|
||||||
in, run the `validate` action on your configuration file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo borgmatic config validate --show
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">In version 1.7.12 through
|
|
||||||
1.7.14</span> Use this command instead:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo validate-borgmatic-config --show
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll need to specify your configuration file with `--config` if it's not in
|
|
||||||
a default location.
|
|
||||||
|
|
||||||
This will output the merged configuration as borgmatic sees it, which can be
|
|
||||||
helpful for understanding how your includes work in practice.
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration overrides
|
|
||||||
|
|
||||||
In more complex multi-application setups, you may want to override particular
|
|
||||||
borgmatic configuration file options at the time you run borgmatic. For
|
|
||||||
instance, you could reuse a common configuration file for multiple
|
|
||||||
applications, but then set the repository for each application at runtime. Or
|
|
||||||
you might want to try a variant of an option for testing purposes without
|
|
||||||
actually touching your configuration file.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>
|
|
||||||
Whatever the reason, you can override borgmatic configuration options at the
|
|
||||||
command-line, as there's a command-line flag corresponding to every
|
|
||||||
configuration option (with its underscores converted to dashes).
|
|
||||||
|
|
||||||
For instance, to override the `compression` configuration option, use the
|
|
||||||
corresponding `--compression` flag on the command-line:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --compression zstd
|
|
||||||
```
|
|
||||||
|
|
||||||
What this does is load your given configuration files and for each one, disregard
|
|
||||||
the configured value for the `compression` option and use the value given on the
|
|
||||||
command-line instead—but just for the duration of the borgmatic run.
|
|
||||||
|
|
||||||
You can override nested configuration options too by separating such option
|
|
||||||
names with a period. For instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --bootstrap.store-config-files false
|
|
||||||
```
|
|
||||||
|
|
||||||
You can even set complex option data structures by using inline YAML syntax. For
|
|
||||||
example, set the `repositories` option with a YAML list of key/value pairs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --repositories "[{path: /mnt/backup, label: local}]"
|
|
||||||
```
|
|
||||||
|
|
||||||
If your override value contains characters like colons or spaces, then you'll
|
|
||||||
need to use quotes for it to parse correctly.
|
|
||||||
|
|
||||||
You can also set individual nested options within existing list elements:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --repositories[0].path /mnt/backup
|
|
||||||
```
|
|
||||||
|
|
||||||
This updates the `path` option for the first repository in `repositories`.
|
|
||||||
Change the `[0]` index as needed to address different list elements. And note
|
|
||||||
that this only works for elements already set in configuration; you can't append
|
|
||||||
new list elements from the command-line.
|
|
||||||
|
|
||||||
See the [command-line reference
|
|
||||||
documentation](https://torsion.org/borgmatic/docs/reference/command-line/) for
|
|
||||||
the full set of available arguments, including examples of each for the complex
|
|
||||||
values.
|
|
||||||
|
|
||||||
There are a handful of configuration options that don't have corresponding
|
|
||||||
command-line flags at the global scope, but instead have flags within individual
|
|
||||||
borgmatic actions. For instance, the `list_details` option can be overridden by
|
|
||||||
the `--list` flag that's only present on particular actions. Similarly with
|
|
||||||
`progress` and `--progress`, `statistics` and `--stats`, and `match_archives`
|
|
||||||
and `--match-archives`.
|
|
||||||
|
|
||||||
Also note that if you want to pass a command-line flag itself as a value to one
|
|
||||||
of these override flags, that may not work. For instance, specifying
|
|
||||||
`--extra-borg-options.create --no-cache-sync` results in an error, because
|
|
||||||
`--no-cache-sync` gets interpreted as a borgmatic option (which in this case
|
|
||||||
doesn't exist) rather than a Borg option.
|
|
||||||
|
|
||||||
An alternate to command-line overrides is passing in your values via
|
|
||||||
[environment
|
|
||||||
variables](https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/).
|
|
||||||
|
|
||||||
|
|
||||||
### Deprecated overrides
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 2.0.0</span>
|
|
||||||
Configuration overrides were performed with an `--override` flag. You can still
|
|
||||||
use `--override` with borgmatic 2.0.0+, but it's deprecated in favor of the new
|
|
||||||
command-line flags described above.
|
|
||||||
|
|
||||||
Here's an example of `--override`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --override remote_path=/usr/local/bin/borg1
|
|
||||||
```
|
|
||||||
|
|
||||||
What this does is load your given configuration files and for each one, disregard
|
|
||||||
the configured value for the `remote_path` option and use the value given on the
|
|
||||||
command-line instead—but just for the duration of the borgmatic run.
|
|
||||||
|
|
||||||
You can even override nested values or multiple values at once. For instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --override parent_option.option1=value1 --override parent_option.option2=value2
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Don't
|
|
||||||
forget to specify the section that an option is in. That looks like a prefix
|
|
||||||
on the option name, e.g. `location.repositories`.
|
|
||||||
|
|
||||||
Note that each value is parsed as an actual YAML string, so you can set list
|
|
||||||
values by using brackets. For instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --override repositories=[test1.borg,test2.borg]
|
|
||||||
```
|
|
||||||
|
|
||||||
Or a single list element:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --override repositories=[/root/test.borg]
|
|
||||||
```
|
|
||||||
|
|
||||||
Or a single list element that is a key/value pair:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --override repositories="[{path: test.borg, label: test}]"
|
|
||||||
```
|
|
||||||
|
|
||||||
If your override value contains characters like colons or spaces, then you'll
|
|
||||||
need to use quotes for it to parse correctly.
|
|
||||||
|
|
||||||
Another example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
borgmatic create --override repositories="['user@server:test.borg']"
|
|
||||||
```
|
|
||||||
|
|
||||||
There is not currently a way to override a single element of a list without
|
|
||||||
replacing the whole list.
|
|
||||||
|
|
||||||
Using the `[ ]` list syntax is required when overriding an option of the list
|
|
||||||
type (like `location.repositories`). See the [configuration
|
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/) for
|
|
||||||
which options are list types. (YAML list values look like `- this` with an
|
|
||||||
indentation and a leading dash.)
|
|
||||||
|
|
||||||
|
|
||||||
## Constant interpolation
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.10</span> Another
|
|
||||||
tool is borgmatic's support for defining custom constants. This is similar to
|
|
||||||
the [variable interpolation
|
|
||||||
feature](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/#variable-interpolation)
|
|
||||||
for command hooks, but the constants feature lets you substitute your own
|
|
||||||
custom values into any option values in the entire configuration file.
|
|
||||||
|
|
||||||
Here's an example usage:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
constants:
|
|
||||||
user: foo
|
|
||||||
archive_prefix: bar
|
|
||||||
|
|
||||||
source_directories:
|
|
||||||
- /home/{user}/.config
|
|
||||||
- /home/{user}/.ssh
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
archive_name_format: '{archive_prefix}-{now}'
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Don't
|
|
||||||
forget to specify the section (like `location:` or `storage:`) that any option
|
|
||||||
is in.
|
|
||||||
|
|
||||||
In this example, when borgmatic runs, all instances of `{user}` get replaced
|
|
||||||
with `foo` and all instances of `{archive_prefix}` get replaced with `bar`.
|
|
||||||
And `{now}` doesn't get replaced with anything, but gets passed directly to
|
|
||||||
Borg, which has its own
|
|
||||||
[placeholders](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-placeholders)
|
|
||||||
using the same syntax as borgmatic constants. So borgmatic options like
|
|
||||||
`archive_name_format` that get passed directly to Borg can use either Borg
|
|
||||||
placeholders or borgmatic constants or both!
|
|
||||||
|
|
||||||
After substitution, the logical result looks something like this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
source_directories:
|
|
||||||
- /home/foo/.config
|
|
||||||
- /home/foo/.ssh
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
archive_name_format: 'bar-{now}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that if you'd like to interpolate a constant into the beginning of a
|
|
||||||
value, you'll need to quote it. For instance, this won't work:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
source_directories:
|
|
||||||
- {my_home_directory}/.config # This will error!
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, do this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
source_directories:
|
|
||||||
- "{my_home_directory}/.config"
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.5</span> Constants
|
|
||||||
work across includes, meaning you can define a constant and then include a
|
|
||||||
separate configuration file that uses that constant.
|
|
||||||
|
|
||||||
An alternate to constants is passing in your values via [environment
|
|
||||||
variables](https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/).
|
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 6
|
order: 6
|
||||||
---
|
---
|
||||||
|
|
||||||
## Monitoring and alerting
|
|
||||||
|
|
||||||
Having backups is great, but they won't do you a lot of good unless you have
|
Having backups is great, but they won't do you a lot of good unless you have
|
||||||
confidence that they're running on a regular basis. That's where monitoring
|
confidence that they're running on a regular basis. That's where monitoring
|
||||||
and alerting comes in.
|
and alerting comes in.
|
||||||
@@ -18,7 +15,7 @@ your particular infrastructure:
|
|||||||
|
|
||||||
* **Job runner alerts**: The easiest place to start is with failure alerts from
|
* **Job runner alerts**: The easiest place to start is with failure alerts from
|
||||||
the [scheduled job
|
the [scheduled job
|
||||||
runner](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot)
|
runner](https://torsion.org/borgmatic/how-to/set-up-backups/#autopilot)
|
||||||
(cron, systemd, etc.) that's running borgmatic. But note that if the job
|
(cron, systemd, etc.) that's running borgmatic. But note that if the job
|
||||||
doesn't even get scheduled (e.g. due to the job runner not running), you
|
doesn't even get scheduled (e.g. due to the job runner not running), you
|
||||||
probably won't get an alert at all! Still, this is a decent first line of
|
probably won't get an alert at all! Still, this is a decent first line of
|
||||||
@@ -28,23 +25,13 @@ your particular infrastructure:
|
|||||||
you'll receive an alert when something goes wrong or when the service doesn't
|
you'll receive an alert when something goes wrong or when the service doesn't
|
||||||
hear from borgmatic for a configured interval (if supported). While these
|
hear from borgmatic for a configured interval (if supported). While these
|
||||||
services and libraries offer different features, you probably only need to
|
services and libraries offer different features, you probably only need to
|
||||||
use one of them at most. See these documentation links for configuration
|
use one of them at most. See the [monitoring configuration
|
||||||
information:
|
documentation](https://torsion.org/borgmatic/reference/configuration/monitoring/)
|
||||||
* [Apprise](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#apprise-hook)
|
for details.
|
||||||
* [Cronhub](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#cronhub-hook)
|
|
||||||
* [Cronitor](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#cronitor-hook)
|
|
||||||
* [Grafana Loki](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#loki-hook)
|
|
||||||
* [Healthchecks](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#healthchecks-hook)
|
|
||||||
* [ntfy](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#ntfy-hook)
|
|
||||||
* [PagerDuty](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pagerduty-hook)
|
|
||||||
* [Pushover](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pushover-hook)
|
|
||||||
* [Sentry](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#sentry-hook)
|
|
||||||
* [Uptime Kuma](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#uptime-kuma-hook)
|
|
||||||
* [Zabbix](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#zabbix-hook)
|
|
||||||
* **Third-party monitoring software:** You can use traditional monitoring
|
* **Third-party monitoring software:** You can use traditional monitoring
|
||||||
software to consume borgmatic JSON output and track when the last successful
|
software to consume borgmatic JSON output and track when the last successful
|
||||||
backup occurred. See [scripting
|
backup occurred. See [scripting
|
||||||
borgmatic](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#scripting-borgmatic)
|
borgmatic](https://torsion.org/borgmatic/how-to/monitor-your-backups/#scripting-borgmatic)
|
||||||
below for how to configure this.
|
below for how to configure this.
|
||||||
* **Borg hosting providers:** Some [Borg hosting
|
* **Borg hosting providers:** Some [Borg hosting
|
||||||
providers](https://torsion.org/borgmatic/#hosting-providers) include
|
providers](https://torsion.org/borgmatic/#hosting-providers) include
|
||||||
@@ -54,639 +41,18 @@ your particular infrastructure:
|
|||||||
* **Consistency checks:** While not strictly part of monitoring, if you want
|
* **Consistency checks:** While not strictly part of monitoring, if you want
|
||||||
confidence that your backups are not only running but are restorable as well,
|
confidence that your backups are not only running but are restorable as well,
|
||||||
you can configure particular [consistency
|
you can configure particular [consistency
|
||||||
checks](https://torsion.org/borgmatic/docs/how-to/deal-with-very-large-backups/#consistency-check-configuration)
|
checks](https://torsion.org/borgmatic/reference/configuration/consistency-checks/)
|
||||||
or even script full [extract
|
or even script full [extract
|
||||||
tests](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/).
|
tests](https://torsion.org/borgmatic/how-to/extract-a-backup/).
|
||||||
* **Commands run on error:** borgmatic's command hooks support running
|
* **Commands run on error:** borgmatic's command hooks support running
|
||||||
arbitrary commands or scripts when borgmatic itself encounters an error
|
arbitrary commands or scripts when borgmatic itself encounters an error
|
||||||
running your backups. So for instance, you can run a script to send yourself
|
running your backups. So for instance, you can run a script to send yourself
|
||||||
a text message alert. But note that if borgmatic doesn't actually run, this
|
a text message alert. But note that if borgmatic doesn't actually run, this
|
||||||
alert won't fire. See the [documentation on command hooks](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/)
|
alert won't fire. See the [documentation on preparation and cleanup
|
||||||
|
steps](https://torsion.org/borgmatic/how-to/add-preparation-and-cleanup-steps-to-backups/)
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
|
|
||||||
## Healthchecks hook
|
|
||||||
|
|
||||||
[Healthchecks](https://healthchecks.io/) is a service that provides "instant
|
|
||||||
alerts when your cron jobs fail silently," and borgmatic has built-in
|
|
||||||
integration with it. Once you create a Healthchecks account and project on
|
|
||||||
their site, all you need to do is configure borgmatic with the unique "Ping
|
|
||||||
URL" for your project. Here's an example:
|
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
healthchecks:
|
|
||||||
ping_url: https://hc-ping.com/addffa72-da17-40ae-be9c-ff591afb942a
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `hooks:` section of your configuration.
|
|
||||||
|
|
||||||
With this configuration, borgmatic pings your Healthchecks project when a
|
|
||||||
backup begins, ends, or errors, but only when any of the `create`, `prune`,
|
|
||||||
`compact`, or `check` actions are run.
|
|
||||||
|
|
||||||
Then, if the actions complete successfully, borgmatic notifies Healthchecks of
|
|
||||||
the success and includes borgmatic logs in the payload data sent to
|
|
||||||
Healthchecks. This means that borgmatic logs show up in the Healthchecks UI,
|
|
||||||
although be aware that Healthchecks currently has a 100-kilobyte limit for the
|
|
||||||
logs in each ping.
|
|
||||||
|
|
||||||
If an error occurs during any action or hook, borgmatic notifies Healthchecks,
|
|
||||||
also tacking on logs including the error itself. But the logs are only
|
|
||||||
included for errors that occur when a `create`, `prune`, `compact`, or `check`
|
|
||||||
action is run.
|
|
||||||
|
|
||||||
You can customize the verbosity of the logs that are sent to Healthchecks with
|
|
||||||
borgmatic's `--monitoring-verbosity` flag. The `--list` and `--stats` flags
|
|
||||||
may also be of use. See `borgmatic create --help` for more information.
|
|
||||||
Additionally, see the [borgmatic configuration
|
|
||||||
file](https://torsion.org/borgmatic/docs/reference/configuration/) for
|
|
||||||
additional Healthchecks options.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the
|
|
||||||
defaults for these flags in your borgmatic configuration via the
|
|
||||||
`monitoring_verbosity`, `list`, and `statistics` options.
|
|
||||||
|
|
||||||
You can configure Healthchecks to notify you by a [variety of
|
|
||||||
mechanisms](https://healthchecks.io/#welcome-integrations) when backups fail
|
|
||||||
or it doesn't hear from borgmatic for a certain period of time.
|
|
||||||
|
|
||||||
|
|
||||||
## Cronitor hook
|
|
||||||
|
|
||||||
[Cronitor](https://cronitor.io/) provides "Cron monitoring and uptime healthchecks
|
|
||||||
for websites, services and APIs," and borgmatic has built-in
|
|
||||||
integration with it. Once you create a Cronitor account and cron job monitor on
|
|
||||||
their site, all you need to do is configure borgmatic with the unique "Ping
|
|
||||||
API URL" for your monitor. Here's an example:
|
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
cronitor:
|
|
||||||
ping_url: https://cronitor.link/d3x0c1
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `hooks:` section of your configuration.
|
|
||||||
|
|
||||||
With this configuration, borgmatic pings your Cronitor monitor when a backup
|
|
||||||
begins, ends, or errors, but only when any of the `create`, `prune`,
|
|
||||||
`compact`, or `check` actions are run. Then, if the actions complete
|
|
||||||
successfully or errors, borgmatic notifies Cronitor accordingly.
|
|
||||||
|
|
||||||
You can configure Cronitor to notify you by a [variety of
|
|
||||||
mechanisms](https://cronitor.io/docs/cron-job-notifications) when backups fail
|
|
||||||
or it doesn't hear from borgmatic for a certain period of time.
|
|
||||||
|
|
||||||
|
|
||||||
## Cronhub hook
|
|
||||||
|
|
||||||
[Cronhub](https://cronhub.io/) provides "instant alerts when any of your
|
|
||||||
background jobs fail silently or run longer than expected," and borgmatic has
|
|
||||||
built-in integration with it. Once you create a Cronhub account and monitor on
|
|
||||||
their site, all you need to do is configure borgmatic with the unique "Ping
|
|
||||||
URL" for your monitor. Here's an example:
|
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
cronhub:
|
|
||||||
ping_url: https://cronhub.io/start/1f5e3410-254c-11e8-b61d-55875966d031
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `hooks:` section of your configuration.
|
|
||||||
|
|
||||||
With this configuration, borgmatic pings your Cronhub monitor when a backup
|
|
||||||
begins, ends, or errors, but only when any of the `create`, `prune`,
|
|
||||||
`compact`, or `check` actions are run. Then, if the actions complete
|
|
||||||
successfully or errors, borgmatic notifies Cronhub accordingly.
|
|
||||||
|
|
||||||
Note that even though you configure borgmatic with the "start" variant of the
|
|
||||||
ping URL, borgmatic substitutes the correct state into the URL when pinging
|
|
||||||
Cronhub ("start", "finish", or "fail").
|
|
||||||
|
|
||||||
You can configure Cronhub to notify you by a [variety of
|
|
||||||
mechanisms](https://docs.cronhub.io/integrations.html) when backups fail
|
|
||||||
or it doesn't hear from borgmatic for a certain period of time.
|
|
||||||
|
|
||||||
|
|
||||||
## PagerDuty hook
|
|
||||||
|
|
||||||
In case you're new here: [borgmatic](https://torsion.org/borgmatic/) is
|
|
||||||
simple, configuration-driven backup software for servers and workstations,
|
|
||||||
powered by [Borg Backup](https://www.borgbackup.org/).
|
|
||||||
|
|
||||||
[PagerDuty](https://www.pagerduty.com/) provides incident monitoring and
|
|
||||||
alerting. borgmatic has built-in integration that can notify you via PagerDuty
|
|
||||||
as soon as a backup fails, so you can make sure your backups keep working.
|
|
||||||
|
|
||||||
First, create a PagerDuty account and <a
|
|
||||||
href="https://support.pagerduty.com/docs/services-and-integrations">service</a>
|
|
||||||
on their site. On the service, add an integration and set the Integration Type
|
|
||||||
to "borgmatic".
|
|
||||||
|
|
||||||
Then, configure borgmatic with the unique "Integration Key" for your service.
|
|
||||||
Here's an example:
|
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
pagerduty:
|
|
||||||
integration_key: a177cad45bd374409f78906a810a3074
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `hooks:` section of your configuration.
|
|
||||||
|
|
||||||
With this configuration, borgmatic creates a PagerDuty event for your service
|
|
||||||
whenever backups fail, but only when any of the `create`, `prune`, `compact`,
|
|
||||||
or `check` actions are run. Note that borgmatic does not contact PagerDuty
|
|
||||||
when a backup starts or when it ends without error.
|
|
||||||
|
|
||||||
You can configure PagerDuty to notify you by a [variety of
|
|
||||||
mechanisms](https://support.pagerduty.com/docs/notifications) when backups
|
|
||||||
fail.
|
|
||||||
|
|
||||||
If you have any issues with the integration, [please contact
|
|
||||||
us](https://torsion.org/borgmatic/#support-and-contributing).
|
|
||||||
|
|
||||||
|
|
||||||
### Sending logs
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.14</span> borgmatic
|
|
||||||
logs are included in the payload data sent to PagerDuty. This means that
|
|
||||||
(truncated) borgmatic logs, including error messages, show up in the PagerDuty
|
|
||||||
incident UI and corresponding notification emails.
|
|
||||||
|
|
||||||
You can customize the verbosity of the logs that are sent with borgmatic's
|
|
||||||
`--monitoring-verbosity` flag. The `--list` and `--stats` flags may also be of
|
|
||||||
use. See `borgmatic create --help` for more information.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the
|
|
||||||
defaults for these flags in your borgmatic configuration via the
|
|
||||||
`monitoring_verbosity`, `list`, and `statistics` options.
|
|
||||||
|
|
||||||
If you don't want any logs sent, you can disable log sending by setting
|
|
||||||
`send_logs` to `false`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
pagerduty:
|
|
||||||
integration_key: a177cad45bd374409f78906a810a3074
|
|
||||||
send_logs: false
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Pushover hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.2</span>
|
|
||||||
[Pushover](https://pushover.net) makes it easy to get real-time notifications
|
|
||||||
on your Android, iPhone, iPad, and Desktop (Android Wear and Apple Watch,
|
|
||||||
too!).
|
|
||||||
|
|
||||||
First, create a Pushover account and login on your mobile device. Create an
|
|
||||||
Application in your Pushover dashboard.
|
|
||||||
|
|
||||||
Then, configure borgmatic with your user's unique "User Key" found in your
|
|
||||||
Pushover dashboard and the unique "API Token" from the created Application.
|
|
||||||
|
|
||||||
Here's a basic example:
|
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
pushover:
|
|
||||||
token: 7ms6TXHpTokTou2P6x4SodDeentHRa
|
|
||||||
user: hwRwoWsXMBWwgrSecfa9EfPey55WSN
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
With this configuration, borgmatic creates a Pushover event for your service
|
|
||||||
whenever borgmatic fails, but only when any of the `create`, `prune`, `compact`,
|
|
||||||
or `check` actions are run. Note that borgmatic does not contact Pushover
|
|
||||||
when a backup starts or when it ends without error by default.
|
|
||||||
|
|
||||||
You can configure Pushover to have custom parameters declared for borgmatic's
|
|
||||||
`start`, `fail` and `finish` hooks states.
|
|
||||||
|
|
||||||
Here's a more advanced example:
|
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
pushover:
|
|
||||||
token: 7ms6TXHpTokTou2P6x4SodDeentHRa
|
|
||||||
user: hwRwoWsXMBWwgrSecfa9EfPey55WSN
|
|
||||||
start:
|
|
||||||
message: "Backup <b>Started</b>"
|
|
||||||
priority: -2
|
|
||||||
title: "Backup Started"
|
|
||||||
html: True
|
|
||||||
ttl: 10 # Message will be deleted after 10 seconds.
|
|
||||||
fail:
|
|
||||||
message: "Backup <font color='#ff6961'>Failed</font>"
|
|
||||||
priority: 2 # Requests acknowledgement for messages.
|
|
||||||
expire: 600 # Used only for priority 2. Default is 600 seconds.
|
|
||||||
retry: 30 # Used only for priority 2. Default is 30 seconds.
|
|
||||||
device: "pixel8"
|
|
||||||
title: "Backup Failed"
|
|
||||||
html: True
|
|
||||||
sound: "siren"
|
|
||||||
url: "https://ticketing-system.example.com/login"
|
|
||||||
url_title: "Login to ticketing system"
|
|
||||||
finish:
|
|
||||||
message: "Backup <font color='#77dd77'>Finished</font>"
|
|
||||||
priority: 0
|
|
||||||
title: "Backup Finished"
|
|
||||||
html: True
|
|
||||||
ttl: 60
|
|
||||||
url: "https://ticketing-system.example.com/login"
|
|
||||||
url_title: "Login to ticketing system"
|
|
||||||
states:
|
|
||||||
- start
|
|
||||||
- finish
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Sentry hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.7</span>
|
|
||||||
[Sentry](https://sentry.io/) is an application monitoring service that
|
|
||||||
includes cron-style monitoring (either cloud-hosted or
|
|
||||||
[self-hosted](https://develop.sentry.dev/self-hosted/)).
|
|
||||||
|
|
||||||
To get started, create a [Sentry cron
|
|
||||||
monitor](https://docs.sentry.io/product/crons/) in the Sentry UI. Under
|
|
||||||
"Instrument your monitor," select "Sentry CLI" and copy the URL value for the
|
|
||||||
displayed
|
|
||||||
[`SENTRY_DSN`](https://docs.sentry.io/concepts/key-terms/dsn-explainer/)
|
|
||||||
environment variable into borgmatic's Sentry `data_source_name_url`
|
|
||||||
configuration option. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
sentry:
|
|
||||||
data_source_name_url: https://5f80ec@o294220.ingest.us.sentry.io/203069
|
|
||||||
monitor_slug: mymonitor
|
|
||||||
```
|
|
||||||
|
|
||||||
The `monitor_slug` value comes from the "Monitor Slug" under "Cron Details" on
|
|
||||||
the same Sentry monitor page.
|
|
||||||
|
|
||||||
With this configuration, borgmatic pings Sentry whenever borgmatic starts,
|
|
||||||
finishes, or fails, but only when any of the `create`, `prune`, `compact`, or
|
|
||||||
`check` actions are run. You can optionally override the start/finish/fail
|
|
||||||
behavior with the `states` configuration option. For instance, to only ping
|
|
||||||
Sentry on failure:
|
|
||||||
|
|
||||||
```
|
|
||||||
sentry:
|
|
||||||
data_source_name_url: https://5f80ec@o294220.ingest.us.sentry.io/203069
|
|
||||||
monitor_slug: mymonitor
|
|
||||||
states:
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## ntfy hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.6.3</span>
|
|
||||||
[ntfy](https://ntfy.sh) is a free, simple, service (either cloud-hosted or
|
|
||||||
self-hosted) which offers simple pub/sub push notifications to multiple
|
|
||||||
platforms including [web](https://ntfy.sh/stats),
|
|
||||||
[Android](https://play.google.com/store/apps/details?id=io.heckel.ntfy) and
|
|
||||||
[iOS](https://apps.apple.com/us/app/ntfy/id1625396347).
|
|
||||||
|
|
||||||
Since push notifications for regular events might soon become quite annoying,
|
|
||||||
this hook only fires on any errors by default in order to instantly alert you
|
|
||||||
to issues. The `states` list can override this. Each state can have its own
|
|
||||||
custom messages, priorities and tags or, if none are provided, will use the
|
|
||||||
default.
|
|
||||||
|
|
||||||
An example configuration is shown here with all the available options,
|
|
||||||
including [priorities](https://ntfy.sh/docs/publish/#message-priority) and
|
|
||||||
[tags](https://ntfy.sh/docs/publish/#tags-emojis):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
ntfy:
|
|
||||||
topic: my-unique-topic
|
|
||||||
server: https://ntfy.my-domain.com
|
|
||||||
username: myuser
|
|
||||||
password: secret
|
|
||||||
|
|
||||||
start:
|
|
||||||
title: A borgmatic backup started
|
|
||||||
message: Watch this space...
|
|
||||||
tags: borgmatic
|
|
||||||
priority: min
|
|
||||||
finish:
|
|
||||||
title: A borgmatic backup completed successfully
|
|
||||||
message: Nice!
|
|
||||||
tags: borgmatic,+1
|
|
||||||
priority: min
|
|
||||||
fail:
|
|
||||||
title: A borgmatic backup failed
|
|
||||||
message: You should probably fix it
|
|
||||||
tags: borgmatic,-1,skull
|
|
||||||
priority: max
|
|
||||||
states:
|
|
||||||
- start
|
|
||||||
- finish
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
the `ntfy:` option in the `hooks:` section of your configuration.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.9</span> Instead of
|
|
||||||
`username`/`password`, you can specify an [ntfy access
|
|
||||||
token](https://docs.ntfy.sh/config/#access-tokens):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
ntfy:
|
|
||||||
topic: my-unique-topic
|
|
||||||
server: https://ntfy.my-domain.com
|
|
||||||
access_token: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
|
|
||||||
````
|
|
||||||
|
|
||||||
## Loki hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.3</span> [Grafana
|
|
||||||
Loki](https://grafana.com/oss/loki/) is a "horizontally scalable, highly
|
|
||||||
available, multi-tenant log aggregation system inspired by Prometheus."
|
|
||||||
borgmatic has built-in integration with Loki, sending both backup status and
|
|
||||||
borgmatic logs.
|
|
||||||
|
|
||||||
You can configure borgmatic to use either a [self-hosted Loki
|
|
||||||
instance](https://grafana.com/docs/loki/latest/installation/) or [a Grafana
|
|
||||||
Cloud account](https://grafana.com/auth/sign-up/create-user). Start by setting
|
|
||||||
your Loki API push URL. Here's an example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
loki:
|
|
||||||
url: http://localhost:3100/loki/api/v1/push
|
|
||||||
|
|
||||||
labels:
|
|
||||||
app: borgmatic
|
|
||||||
hostname: example.org
|
|
||||||
```
|
|
||||||
|
|
||||||
With this configuration, borgmatic sends its logs to your Loki instance as any
|
|
||||||
of the `create`, `prune`, `compact`, or `check` actions are run. Then, after
|
|
||||||
the actions complete, borgmatic notifies Loki of success or failure.
|
|
||||||
|
|
||||||
This hook supports sending arbitrary labels to Loki. At least one label is
|
|
||||||
required.
|
|
||||||
|
|
||||||
There are also a few placeholders you can optionally use as label values:
|
|
||||||
|
|
||||||
* `__config`: name of the borgmatic configuration file
|
|
||||||
* `__config_path`: full path of the borgmatic configuration file
|
|
||||||
* `__hostname`: the local machine hostname
|
|
||||||
|
|
||||||
These placeholders are only substituted for the whole label value, not
|
|
||||||
interpolated into a larger string. For instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
loki:
|
|
||||||
url: http://localhost:3100/loki/api/v1/push
|
|
||||||
|
|
||||||
labels:
|
|
||||||
app: borgmatic
|
|
||||||
config: __config
|
|
||||||
hostname: __hostname
|
|
||||||
```
|
|
||||||
|
|
||||||
Also check out this [Loki dashboard for
|
|
||||||
borgmatic](https://grafana.com/grafana/dashboards/20736-borgmatic-logs/) if
|
|
||||||
you'd like to see your backup logs and statistics in one place.
|
|
||||||
|
|
||||||
|
|
||||||
## Apprise hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.4</span>
|
|
||||||
[Apprise](https://github.com/caronc/apprise/wiki) is a local notification library
|
|
||||||
that "allows you to send a notification to almost all of the most popular
|
|
||||||
[notification services](https://github.com/caronc/apprise/wiki) available to
|
|
||||||
us today such as: Telegram, Discord, Slack, Amazon SNS, Gotify, etc."
|
|
||||||
|
|
||||||
Depending on how you installed borgmatic, it may not have come with Apprise.
|
|
||||||
For instance, if you originally [installed borgmatic with
|
|
||||||
pipx](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#installation),
|
|
||||||
run the following to install Apprise so borgmatic can use it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo pipx uninstall borgmatic
|
|
||||||
sudo pipx install borgmatic[Apprise]
|
|
||||||
```
|
|
||||||
|
|
||||||
Omit `sudo` if borgmatic is installed as a non-root user.
|
|
||||||
|
|
||||||
Once Apprise is installed, configure borgmatic to notify one or more [Apprise
|
|
||||||
services](https://github.com/caronc/apprise/wiki). For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apprise:
|
|
||||||
services:
|
|
||||||
- url: gotify://hostname/token
|
|
||||||
label: gotify
|
|
||||||
- url: mastodons://access_key@hostname/@user
|
|
||||||
label: mastodon
|
|
||||||
states:
|
|
||||||
- start
|
|
||||||
- finish
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
With this configuration, borgmatic pings each of the configured Apprise
|
|
||||||
services when a backup begins, ends, or errors, but only when any of the
|
|
||||||
`create`, `prune`, `compact`, or `check` actions are run. (By default, if
|
|
||||||
`states` is not specified, Apprise services are only pinged on error.)
|
|
||||||
|
|
||||||
You can optionally customize the contents of the default messages sent to
|
|
||||||
these services:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apprise:
|
|
||||||
services:
|
|
||||||
- url: gotify://hostname/token
|
|
||||||
label: gotify
|
|
||||||
start:
|
|
||||||
title: Ping!
|
|
||||||
body: Starting backup process.
|
|
||||||
finish:
|
|
||||||
title: Ping!
|
|
||||||
body: Backups successfully made.
|
|
||||||
fail:
|
|
||||||
title: Ping!
|
|
||||||
body: Your backups have failed.
|
|
||||||
states:
|
|
||||||
- start
|
|
||||||
- finish
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.9</span> borgmatic
|
|
||||||
logs are automatically included in the body data sent to your Apprise services
|
|
||||||
when a backup finishes or fails.
|
|
||||||
|
|
||||||
You can customize the verbosity of the logs that are sent with borgmatic's
|
|
||||||
`--monitoring-verbosity` flag. The `--list` and `--stats` flags may also be of
|
|
||||||
use. See `borgmatic create --help` for more information.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>Set the
|
|
||||||
defaults for these flags in your borgmatic configuration via the
|
|
||||||
`monitoring_verbosity`, `list`, and `statistics` options.
|
|
||||||
|
|
||||||
If you don't want any logs sent, you can disable log sending by setting
|
|
||||||
`send_logs` to `false`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apprise:
|
|
||||||
services:
|
|
||||||
- url: gotify://hostname/token
|
|
||||||
label: gotify
|
|
||||||
send_logs: false
|
|
||||||
```
|
|
||||||
|
|
||||||
Or to limit the size of logs sent to Apprise services:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apprise:
|
|
||||||
services:
|
|
||||||
- url: gotify://hostname/token
|
|
||||||
label: gotify
|
|
||||||
logs_size_limit: 500
|
|
||||||
```
|
|
||||||
|
|
||||||
This may be necessary for some services that reject large requests.
|
|
||||||
|
|
||||||
See the [configuration
|
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/) for
|
|
||||||
details.
|
|
||||||
|
|
||||||
## Uptime Kuma hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.13</span> [Uptime
|
|
||||||
Kuma](https://uptime.kuma.pet) is a self-hosted monitoring tool and can
|
|
||||||
provide a Push monitor type to accept HTTP `GET` requests from a service
|
|
||||||
instead of contacting it directly.
|
|
||||||
|
|
||||||
Uptime Kuma allows you to see a history of monitor states and can in turn
|
|
||||||
alert via ntfy, Gotify, Matrix, Apprise, Email, and many more.
|
|
||||||
|
|
||||||
An example configuration is shown here with all the available options:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
uptime_kuma:
|
|
||||||
push_url: https://kuma.my-domain.com/api/push/abcd1234
|
|
||||||
states:
|
|
||||||
- start
|
|
||||||
- finish
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
The `push_url` is provided to your from your Uptime Kuma service and
|
|
||||||
originally includes a query string—the text including and after the question
|
|
||||||
mark (`?`). But please do not include the query string in the `push_url`
|
|
||||||
configuration; borgmatic will add this automatically depending on the state of
|
|
||||||
your backup.
|
|
||||||
|
|
||||||
Using `start`, `finish` and `fail` states means you will get two "up beats" in
|
|
||||||
Uptime Kuma for successful backups and the ability to see failures if and when
|
|
||||||
the backup started (was there a `start` beat?).
|
|
||||||
|
|
||||||
A reasonable base-level configuration for an Uptime Kuma Monitor for a backup
|
|
||||||
is below:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
# These are to be entered into Uptime Kuma and not into your borgmatic
|
|
||||||
# configuration.
|
|
||||||
|
|
||||||
# Push monitors wait for the client to contact Uptime Kuma instead of Uptime
|
|
||||||
# Kuma contacting the client. This is perfect for backup monitoring.
|
|
||||||
Monitor Type = Push
|
|
||||||
|
|
||||||
Heartbeat Interval = 90000 # = 25 hours = 1 day + 1 hour
|
|
||||||
|
|
||||||
# Wait 6 times the Heartbeat Retry (below) before logging a heartbeat missed.
|
|
||||||
Retries = 6
|
|
||||||
|
|
||||||
# Multiplied by Retries this gives a grace period within which the monitor
|
|
||||||
# goes into the "Pending" state.
|
|
||||||
Heartbeat Retry = 360 # = 10 minutes
|
|
||||||
|
|
||||||
# For each Heartbeat Interval if the backup fails repeatedly, a notification
|
|
||||||
# is sent each time.
|
|
||||||
Resend Notification every X times = 1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Zabbix hook
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.0</span>
|
|
||||||
[Zabbix](https://www.zabbix.com/) is an open-source monitoring tool used for
|
|
||||||
tracking and managing the performance and availability of networks, servers,
|
|
||||||
and applications in real-time.
|
|
||||||
|
|
||||||
This hook does not do any notifications on its own. Instead, it relies on your
|
|
||||||
Zabbix instance to notify and perform escalations based on the Zabbix
|
|
||||||
configuration. The `states` defined in the configuration determine which
|
|
||||||
states will trigger the hook. The value defined in the configuration of each
|
|
||||||
state is used to populate the data of the configured Zabbix item. If none are
|
|
||||||
provided, it defaults to a lower-case string of the state.
|
|
||||||
|
|
||||||
An example configuration is shown here with all the available options.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
zabbix:
|
|
||||||
server: http://cloud.zabbix.com/zabbix/api_jsonrpc.php
|
|
||||||
|
|
||||||
username: myuser
|
|
||||||
password: secret
|
|
||||||
api_key: b2ecba64d8beb47fc161ae48b164cfd7104a79e8e48e6074ef5b141d8a0aeeca
|
|
||||||
|
|
||||||
host: "borg-server"
|
|
||||||
key: borg.status
|
|
||||||
itemid: 55105
|
|
||||||
|
|
||||||
start:
|
|
||||||
value: "STARTED"
|
|
||||||
finish:
|
|
||||||
value: "OK"
|
|
||||||
fail:
|
|
||||||
value: "ERROR"
|
|
||||||
states:
|
|
||||||
- start
|
|
||||||
- finish
|
|
||||||
- fail
|
|
||||||
```
|
|
||||||
|
|
||||||
This hook requires the Zabbix server be running version 7.0.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.3</span> Zabbix 7.2+
|
|
||||||
is supported as well.
|
|
||||||
|
|
||||||
|
|
||||||
### Authentication methods
|
|
||||||
|
|
||||||
Authentication can be accomplished via `api_key` or both `username` and
|
|
||||||
`password`. If all three are declared, only `api_key` is used.
|
|
||||||
|
|
||||||
|
|
||||||
### Items
|
|
||||||
|
|
||||||
borgmatic writes its monitoring updates to a particular Zabbix item, which
|
|
||||||
you'll need to create in advance. In the Zabbix web UI, [make a new item with a
|
|
||||||
Type of "Zabbix
|
|
||||||
trapper"](https://www.zabbix.com/documentation/current/en/manual/config/items/itemtypes/trapper)
|
|
||||||
and a named Key. The "Type of information" for the item should be "Text", and
|
|
||||||
"History" designates how much data you want to retain.
|
|
||||||
|
|
||||||
When configuring borgmatic with this item to be updated, you can either declare
|
|
||||||
the `itemid` or both `host` and `key`. If all three are declared, only `itemid`
|
|
||||||
is used.
|
|
||||||
|
|
||||||
Keep in mind that `host` refers to the "Host name" on the Zabbix server and not
|
|
||||||
the "Visual name".
|
|
||||||
|
|
||||||
|
|
||||||
## Scripting borgmatic
|
## Scripting borgmatic
|
||||||
|
|
||||||
To consume the output of borgmatic in other software, you can include an
|
To consume the output of borgmatic in other software, you can include an
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 2
|
order: 2
|
||||||
---
|
---
|
||||||
## Providing passwords and secrets to borgmatic
|
|
||||||
|
|
||||||
If you want to use a Borg repository passphrase or database passwords with
|
If you want to use a Borg repository passphrase or database passwords with
|
||||||
borgmatic, you can set them directly in your borgmatic configuration file,
|
borgmatic, you can set them directly in your borgmatic configuration file,
|
||||||
treating those secrets like any other option value. For instance, you can
|
treating those secrets like any other option value. For instance, you can
|
||||||
@@ -40,352 +38,27 @@ should only ever get prompted for your password manager's passphrase at most
|
|||||||
once per borgmatic run.
|
once per borgmatic run.
|
||||||
|
|
||||||
|
|
||||||
### systemd service credentials
|
<a id="systemd-service-credentials"></a>
|
||||||
|
<a id="container-secrets"></a>
|
||||||
|
<a id="keepassxc-passwords"></a>
|
||||||
|
<a id="file-based-credentials"></a>
|
||||||
|
|
||||||
borgmatic supports reading encrypted [systemd
|
|
||||||
credentials](https://systemd.io/CREDENTIALS/). To use this feature, start by
|
|
||||||
saving your password as an encrypted credential to
|
|
||||||
`/etc/credstore.encrypted/borgmatic.pw`, e.g.,
|
|
||||||
|
|
||||||
```bash
|
### Using external credentials
|
||||||
systemd-ask-password -n | systemd-creds encrypt - /etc/credstore.encrypted/borgmatic.pw
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use the following in your configuration file:
|
<span class="minilink minilink-addedin">New in version 1.9.10</span> Several
|
||||||
|
borgmatic options support reading their values directly from an external
|
||||||
|
credential store or service. See the [credentials
|
||||||
|
documentation](https://torsion.org/borgmatic/reference/configuration/credentials/)
|
||||||
|
for details.
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: "{credential systemd borgmatic.pw}"
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.9.10</span> You can
|
<a id="environment-variable-interpolation"></a>
|
||||||
accomplish the same thing with this configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passcommand: cat ${CREDENTIALS_DIRECTORY}/borgmatic.pw
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the name `borgmatic.pw` is hardcoded in the systemd service file.
|
### Using environment variables
|
||||||
|
|
||||||
The `{credential ...}` syntax works for several different options in a borgmatic
|
Another way to get passwords into your configuration file is by [interpolating
|
||||||
configuration file besides just `encryption_passphrase`. For instance, the
|
arbitrary environment
|
||||||
username, password, and API token options within database and monitoring hooks
|
variables](https://torsion.org/borgmatic/reference/configuration/environment-variables/)
|
||||||
support `{credential ...}`:
|
directly into option values.
|
||||||
|
|
||||||
```yaml
|
|
||||||
postgresql_databases:
|
|
||||||
- name: invoices
|
|
||||||
username: postgres
|
|
||||||
password: "{credential systemd borgmatic_db1}"
|
|
||||||
```
|
|
||||||
|
|
||||||
For specifics about which options are supported, see the
|
|
||||||
[configuration
|
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/).
|
|
||||||
|
|
||||||
To use these credentials, you'll need to modify the borgmatic systemd service
|
|
||||||
file to support loading multiple credentials (assuming you need to load more
|
|
||||||
than one or anything not named `borgmatic.pw`).
|
|
||||||
|
|
||||||
Start by saving each encrypted credentials to
|
|
||||||
`/etc/credstore.encrypted/borgmatic/`. E.g.,
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir /etc/credstore.encrypted/borgmatic
|
|
||||||
systemd-ask-password -n | systemd-creds encrypt --name=borgmatic_backupserver1 - /etc/credstore.encrypted/borgmatic/backupserver1
|
|
||||||
systemd-ask-password -n | systemd-creds encrypt --name=borgmatic_pw2 - /etc/credstore.encrypted/borgmatic/pw2
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Ensure that the file names, (e.g. `backupserver1`) match the corresponding part
|
|
||||||
of the `--name` option *after* the underscore (_), and that the part *before*
|
|
||||||
the underscore matches the directory name (e.g. `borgmatic`).
|
|
||||||
|
|
||||||
Then, uncomment the appropriate line in the systemd service file:
|
|
||||||
|
|
||||||
```
|
|
||||||
systemctl edit borgmatic.service
|
|
||||||
...
|
|
||||||
# Load multiple encrypted credentials.
|
|
||||||
LoadCredentialEncrypted=borgmatic:/etc/credstore.encrypted/borgmatic/
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, use something like the following in your borgmatic configuration file
|
|
||||||
for each option value you'd like to load from systemd:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: "{credential systemd borgmatic_backupserver1}"
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.9.10</span> Use the
|
|
||||||
following instead, but only for the `encryption_passcommand` option and
|
|
||||||
not other options:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passcommand: cat ${CREDENTIALS_DIRECTORY}/borgmatic_backupserver1
|
|
||||||
```
|
|
||||||
|
|
||||||
Adjust `borgmatic_backupserver1` according to the name of the credential and the
|
|
||||||
directory set in the service file.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.9</span> When using
|
|
||||||
the systemd `{credential ...}` feature, borgmatic loads systemd credentials even
|
|
||||||
when run outside of a systemd service. This works by falling back to calling
|
|
||||||
`systemd-creds decrypt` instead of reading credentials directly. To customize
|
|
||||||
this behavior, you can override the `systemd-creds` command and/or the
|
|
||||||
credential store directory it uses:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
systemd:
|
|
||||||
systemd_creds_command: /usr/local/bin/systemd-creds
|
|
||||||
encrypted_credentials_directory: /path/to/credstore.encrypted
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 2.0.9</span> The
|
|
||||||
systemd `{credential ...}` feature did not work when run outside of a systemd
|
|
||||||
service. But depending on the borgmatic action invoked and the configuration
|
|
||||||
option where `{credential ...}` was used, you could sometimes get away without
|
|
||||||
working systemd credentials for certain actions. For instance, `borgmatic list`
|
|
||||||
doesn't connect to any databases or monitoring services, and `borgmatic config
|
|
||||||
validate` doesn't use credentials as all.
|
|
||||||
|
|
||||||
|
|
||||||
### Container secrets
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.11</span> When
|
|
||||||
running inside a container, borgmatic can read [Docker
|
|
||||||
secrets](https://docs.docker.com/compose/how-tos/use-secrets/) and [Podman
|
|
||||||
secrets](https://www.redhat.com/en/blog/new-podman-secrets-command). Creating
|
|
||||||
those secrets and passing them into your borgmatic container is outside the
|
|
||||||
scope of this documentation, but here's a simple example of that with [Docker
|
|
||||||
Compose](https://docs.docker.com/compose/):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
borgmatic:
|
|
||||||
# Use the actual image name of your borgmatic container here.
|
|
||||||
image: borgmatic:latest
|
|
||||||
secrets:
|
|
||||||
- borgmatic_passphrase
|
|
||||||
secrets:
|
|
||||||
borgmatic_passphrase:
|
|
||||||
file: /etc/borgmatic/passphrase.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
This assumes there's a file on the host at `/etc/borgmatic/passphrase.txt`
|
|
||||||
containing your passphrase. Docker or Podman mounts the contents of that file
|
|
||||||
into a secret named `borgmatic_passphrase` in the borgmatic container at
|
|
||||||
`/run/secrets/`.
|
|
||||||
|
|
||||||
Once your container secret is in place, you can consume it within your borgmatic
|
|
||||||
configuration file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: "{credential container borgmatic_passphrase}"
|
|
||||||
```
|
|
||||||
|
|
||||||
This reads the secret securely from a file mounted at
|
|
||||||
`/run/secrets/borgmatic_passphrase` within the borgmatic container.
|
|
||||||
|
|
||||||
The `{credential ...}` syntax works for several different options in a borgmatic
|
|
||||||
configuration file besides just `encryption_passphrase`. For instance, the
|
|
||||||
username, password, and API token options within database and monitoring hooks
|
|
||||||
support `{credential ...}`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
postgresql_databases:
|
|
||||||
- name: invoices
|
|
||||||
username: postgres
|
|
||||||
password: "{credential container borgmatic_db1}"
|
|
||||||
```
|
|
||||||
|
|
||||||
For specifics about which options are supported, see the
|
|
||||||
[configuration
|
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/).
|
|
||||||
|
|
||||||
You can also optionally override the `/run/secrets` directory that borgmatic reads secrets from
|
|
||||||
inside a container:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
container:
|
|
||||||
secrets_directory: /path/to/secrets
|
|
||||||
```
|
|
||||||
|
|
||||||
But you should only need to do this for development or testing purposes.
|
|
||||||
|
|
||||||
|
|
||||||
### KeePassXC passwords
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.11</span> borgmatic
|
|
||||||
supports reading passwords from the [KeePassXC](https://keepassxc.org/) password
|
|
||||||
manager. To use this feature, start by creating an entry in your KeePassXC
|
|
||||||
database, putting your password into the "Password" field of that entry and
|
|
||||||
making sure it's saved.
|
|
||||||
|
|
||||||
Then, you can consume that password in your borgmatic configuration file. For
|
|
||||||
instance, if the entry's title is "borgmatic" and your KeePassXC database is
|
|
||||||
located at `/etc/keys.kdbx`, do this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: "{credential keepassxc /etc/keys.kdbx borgmatic}"
|
|
||||||
```
|
|
||||||
|
|
||||||
But if the entry's title is multiple words like `borg pw`, you'll
|
|
||||||
need to quote it:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: "{credential keepassxc /etc/keys.kdbx 'borg pw'}"
|
|
||||||
```
|
|
||||||
|
|
||||||
With this in place, borgmatic runs the `keepassxc-cli` command to retrieve the
|
|
||||||
passphrase on demand. But note that `keepassxc-cli` will prompt for its own
|
|
||||||
passphrase in order to unlock its database, so be prepared to enter it when
|
|
||||||
running borgmatic.
|
|
||||||
|
|
||||||
The `{credential ...}` syntax works for several different options in a borgmatic
|
|
||||||
configuration file besides just `encryption_passphrase`. For instance, the
|
|
||||||
username, password, and API token options within database and monitoring hooks
|
|
||||||
support `{credential ...}`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
postgresql_databases:
|
|
||||||
- name: invoices
|
|
||||||
username: postgres
|
|
||||||
password: "{credential keepassxc /etc/keys.kdbx database}"
|
|
||||||
```
|
|
||||||
|
|
||||||
For specifics about which options are supported, see the
|
|
||||||
[configuration
|
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/).
|
|
||||||
|
|
||||||
You can also optionally override the `keepassxc-cli` command that borgmatic calls to load
|
|
||||||
passwords:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
keepassxc:
|
|
||||||
keepassxc_cli_command: /usr/local/bin/keepassxc-cli
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### File-based credentials
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.11</span> borgmatic
|
|
||||||
supports reading credentials from arbitrary file paths. To use this feature,
|
|
||||||
start by writing your credential into a file that borgmatic has permission to
|
|
||||||
read. Take care not to include anything in the file other than your credential.
|
|
||||||
(borgmatic is smart enough to strip off a trailing newline though.)
|
|
||||||
|
|
||||||
You can consume that credential file in your borgmatic configuration. For
|
|
||||||
instance, if your credential file is at `/credentials/borgmatic.txt`, do this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: "{credential file /credentials/borgmatic.txt}"
|
|
||||||
```
|
|
||||||
|
|
||||||
With this in place, borgmatic reads the credential from the file path.
|
|
||||||
|
|
||||||
The `{credential ...}` syntax works for several different options in a borgmatic
|
|
||||||
configuration file besides just `encryption_passphrase`. For instance, the
|
|
||||||
username, password, and API token options within database and monitoring hooks
|
|
||||||
support `{credential ...}`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
postgresql_databases:
|
|
||||||
- name: invoices
|
|
||||||
username: postgres
|
|
||||||
password: "{credential file /credentials/database.txt}"
|
|
||||||
```
|
|
||||||
|
|
||||||
For specifics about which options are supported, see the
|
|
||||||
[configuration
|
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/).
|
|
||||||
|
|
||||||
|
|
||||||
### Environment variable interpolation
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.6.4</span> borgmatic
|
|
||||||
supports interpolating arbitrary environment variables directly into option
|
|
||||||
values in your configuration file. That means you can instruct borgmatic to
|
|
||||||
pull your repository passphrase, your database passwords, or any other option
|
|
||||||
values from environment variables.
|
|
||||||
|
|
||||||
Be aware though that environment variables may be less secure than some of the
|
|
||||||
other approaches above for getting credentials into borgmatic. That's because
|
|
||||||
environment variables may be visible from within child processes and/or OS-level
|
|
||||||
process metadata.
|
|
||||||
|
|
||||||
Here's an example of using an environment variable from borgmatic's
|
|
||||||
configuration file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: ${YOUR_PASSPHRASE}
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `storage:` section of your configuration.
|
|
||||||
|
|
||||||
This uses the `YOUR_PASSPHRASE` environment variable as your encryption
|
|
||||||
passphrase. Note that the `{` `}` brackets are required. `$YOUR_PASSPHRASE` by
|
|
||||||
itself will not work.
|
|
||||||
|
|
||||||
In the case of `encryption_passphrase` in particular, an alternate approach
|
|
||||||
is to use Borg's `BORG_PASSPHRASE` environment variable, which doesn't even
|
|
||||||
require setting an explicit `encryption_passphrase` value in borgmatic's
|
|
||||||
configuration file.
|
|
||||||
|
|
||||||
For [database
|
|
||||||
configuration](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/),
|
|
||||||
the same approach applies. For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
postgresql_databases:
|
|
||||||
- name: users
|
|
||||||
password: ${YOUR_DATABASE_PASSWORD}
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
|
|
||||||
this option in the `hooks:` section of your configuration.
|
|
||||||
|
|
||||||
This uses the `YOUR_DATABASE_PASSWORD` environment variable as your database
|
|
||||||
password.
|
|
||||||
|
|
||||||
|
|
||||||
#### Interpolation defaults
|
|
||||||
|
|
||||||
If you'd like to set a default for your environment variables, you can do so
|
|
||||||
with the following syntax:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: ${YOUR_PASSPHRASE:-defaultpass}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, "`defaultpass`" is the default passphrase if the `YOUR_PASSPHRASE`
|
|
||||||
environment variable is not set. Without a default, if the environment
|
|
||||||
variable doesn't exist, borgmatic will error.
|
|
||||||
|
|
||||||
|
|
||||||
#### Disabling interpolation
|
|
||||||
|
|
||||||
To disable this environment variable interpolation feature entirely, you can
|
|
||||||
pass the `--no-environment-interpolation` flag on the command-line.
|
|
||||||
|
|
||||||
Or if you'd like to disable interpolation within a single option value, you
|
|
||||||
can escape it with a backslash. For instance, if your password is literally
|
|
||||||
`${A}@!`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
encryption_passphrase: \${A}@!
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Related features
|
|
||||||
|
|
||||||
Another way to override particular options within a borgmatic configuration
|
|
||||||
file is to use a [configuration
|
|
||||||
override](https://torsion.org/borgmatic/docs/how-to/make-per-application-backups/#configuration-overrides)
|
|
||||||
on the command-line. But please be aware of the security implications of
|
|
||||||
specifying secrets on the command-line.
|
|
||||||
|
|
||||||
Additionally, borgmatic action hooks support their own [variable
|
|
||||||
interpolation](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/#variable-interpolation),
|
|
||||||
although in that case it's for particular borgmatic runtime values rather than
|
|
||||||
(only) environment variables.
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta http-equiv='refresh' content='0; URL=https://torsion.org/borgmatic/docs/how-to/extract-a-backup/'>
|
<meta http-equiv='refresh' content='0; URL=https://torsion.org/borgmatic/how-to/extract-a-backup/'>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 12
|
order: 12
|
||||||
---
|
---
|
||||||
## Running Borg with borgmatic
|
|
||||||
|
|
||||||
Borg has several commands and options that borgmatic does not currently
|
Borg has several commands and options that borgmatic does not currently
|
||||||
support. Sometimes though, as a borgmatic user, you may find yourself wanting
|
support. Sometimes though, as a borgmatic user, you may find yourself wanting
|
||||||
to take advantage of these off-the-beaten-path Borg features. You could of
|
to take advantage of these off-the-beaten-path Borg features. You could of
|
||||||
@@ -28,7 +26,7 @@ arguments:
|
|||||||
* verbosity
|
* verbosity
|
||||||
|
|
||||||
|
|
||||||
### borg action
|
## borg action
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.5.15</span> The way
|
<span class="minilink minilink-addedin">New in version 1.5.15</span> The way
|
||||||
you run Borg with borgmatic is via the `borg` action. Here's a simple example:
|
you run Borg with borgmatic is via the `borg` action. Here's a simple example:
|
||||||
@@ -45,13 +43,10 @@ be preferred though for most uses.)
|
|||||||
You can also specify Borg options for relevant commands. For instance:
|
You can also specify Borg options for relevant commands. For instance:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
borgmatic borg repo-list --short
|
borgmatic borg list --short
|
||||||
```
|
```
|
||||||
|
|
||||||
(No borgmatic `repo-list` action? Try `rlist` or `list` instead or upgrade
|
This runs Borg's `list` command once on each configured borgmatic
|
||||||
borgmatic!)
|
|
||||||
|
|
||||||
This runs Borg's `repo-list` command once on each configured borgmatic
|
|
||||||
repository.
|
repository.
|
||||||
|
|
||||||
What if you only want to run Borg on a single configured borgmatic repository
|
What if you only want to run Borg on a single configured borgmatic repository
|
||||||
@@ -78,7 +73,7 @@ the right place (which didn't always work). So your command-line in these
|
|||||||
older versions didn't support the `::`
|
older versions didn't support the `::`
|
||||||
|
|
||||||
|
|
||||||
### Specifying an archive
|
## Specifying an archive
|
||||||
|
|
||||||
For borg commands that expect an archive name, you have a few approaches.
|
For borg commands that expect an archive name, you have a few approaches.
|
||||||
Here's one:
|
Here's one:
|
||||||
@@ -119,7 +114,7 @@ borgmatic borg --archive latest list '$ARCHIVE'
|
|||||||
borgmatic borg list your-actual-archive-name
|
borgmatic borg list your-actual-archive-name
|
||||||
```
|
```
|
||||||
|
|
||||||
### Limitations
|
## Limitations
|
||||||
|
|
||||||
borgmatic's `borg` action is not without limitations:
|
borgmatic's `borg` action is not without limitations:
|
||||||
|
|
||||||
@@ -139,6 +134,7 @@ borgmatic's `borg` action is not without limitations:
|
|||||||
not disable certain borgmatic logs to avoid interfering with JSON output.
|
not disable certain borgmatic logs to avoid interfering with JSON output.
|
||||||
* The `borg` action bypasses most of borgmatic's machinery, so for instance
|
* The `borg` action bypasses most of borgmatic's machinery, so for instance
|
||||||
monitoring hooks will not get triggered when running `borgmatic borg create`.
|
monitoring hooks will not get triggered when running `borgmatic borg create`.
|
||||||
|
* The `borg` action does not use `extra_borg_options`.
|
||||||
* <span class="minilink minilink-addedin">Prior to version 1.8.0</span>
|
* <span class="minilink minilink-addedin">Prior to version 1.8.0</span>
|
||||||
borgmatic implicitly injected the repository/archive arguments on the Borg
|
borgmatic implicitly injected the repository/archive arguments on the Borg
|
||||||
command-line for you (based on your borgmatic configuration or the
|
command-line for you (based on your borgmatic configuration or the
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta http-equiv='refresh' content='0; URL=https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/'>
|
<meta http-equiv='refresh' content='0; URL=https://torsion.org/borgmatic/how-to/add-preparation-and-cleanup-steps-to-backups/'>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 0
|
order: 0
|
||||||
---
|
---
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Prerequisites
|
To install borgmatic, first [install
|
||||||
|
|
||||||
First, [install
|
|
||||||
Borg](https://borgbackup.readthedocs.io/en/stable/installation.html), at least
|
Borg](https://borgbackup.readthedocs.io/en/stable/installation.html), at least
|
||||||
version 1.1. borgmatic does not install Borg automatically so as to avoid
|
version 1.1. (borgmatic does not install Borg automatically so as to avoid
|
||||||
conflicts with existing Borg installations.
|
conflicts with existing Borg installations.)
|
||||||
|
|
||||||
Then, [install pipx](https://pypa.github.io/pipx/installation/) as the root
|
Then, [install pipx](https://pypa.github.io/pipx/installation/) as the root
|
||||||
user (with `sudo`) to make installing borgmatic easier without impacting other
|
user (with `sudo`) to make installing borgmatic easier without impacting other
|
||||||
@@ -78,12 +75,16 @@ with `sudo`. If that doesn't work, you may need to update your [sudoers
|
|||||||
Besides the approaches described above, there are several other options for
|
Besides the approaches described above, there are several other options for
|
||||||
installing borgmatic:
|
installing borgmatic:
|
||||||
|
|
||||||
* [container image with scheduled backups](https://hub.docker.com/r/b3vis/borgmatic/) (+ Docker Compose files)
|
#### <span data-pagefind-weight="7.0">Docker / Podman</span>
|
||||||
* [container image with multi-arch and Docker CLI support](https://hub.docker.com/r/modem7/borgmatic-docker/)
|
|
||||||
|
* [container image with scheduled backups](https://github.com/borgmatic-collective/docker-borgmatic) (+ Docker Compose files)
|
||||||
|
* [container image with multi-arch and Docker CLI support](https://github.com/modem7/docker-borgmatic)
|
||||||
|
|
||||||
|
#### Operating system packages
|
||||||
|
|
||||||
* [Debian](https://tracker.debian.org/pkg/borgmatic)
|
* [Debian](https://tracker.debian.org/pkg/borgmatic)
|
||||||
* [Ubuntu](https://launchpad.net/ubuntu/+source/borgmatic)
|
* [Ubuntu](https://launchpad.net/ubuntu/+source/borgmatic)
|
||||||
* [Fedora official](https://bodhi.fedoraproject.org/updates/?search=borgmatic)
|
* [Fedora](https://bodhi.fedoraproject.org/updates/?search=borgmatic)
|
||||||
* [Fedora unofficial](https://copr.fedorainfracloud.org/coprs/heffer/borgmatic/)
|
|
||||||
* [Gentoo](https://packages.gentoo.org/packages/app-backup/borgmatic)
|
* [Gentoo](https://packages.gentoo.org/packages/app-backup/borgmatic)
|
||||||
* [Arch Linux](https://archlinux.org/packages/extra/any/borgmatic/)
|
* [Arch Linux](https://archlinux.org/packages/extra/any/borgmatic/)
|
||||||
* [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=borgmatic)
|
* [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=borgmatic)
|
||||||
@@ -91,9 +92,11 @@ installing borgmatic:
|
|||||||
* [openSUSE](https://software.opensuse.org/package/borgmatic)
|
* [openSUSE](https://software.opensuse.org/package/borgmatic)
|
||||||
* [macOS (via Homebrew)](https://formulae.brew.sh/formula/borgmatic)
|
* [macOS (via Homebrew)](https://formulae.brew.sh/formula/borgmatic)
|
||||||
* [macOS (via MacPorts)](https://ports.macports.org/port/borgmatic/)
|
* [macOS (via MacPorts)](https://ports.macports.org/port/borgmatic/)
|
||||||
* [NixOS](https://search.nixos.org/packages?show=borgmatic&sort=relevance&type=packages&query=borgmatic)
|
* [NixOS](https://search.nixos.org/packages?channel=unstable&show=borgmatic&query=borgmatic)
|
||||||
|
|
||||||
|
#### Etc.
|
||||||
|
|
||||||
* [Ansible role](https://github.com/borgbase/ansible-role-borgbackup)
|
* [Ansible role](https://github.com/borgbase/ansible-role-borgbackup)
|
||||||
* [Unraid](https://unraid.net/community/apps?q=borgmatic#r)
|
|
||||||
* [uv tool install](https://docs.astral.sh/uv/)
|
* [uv tool install](https://docs.astral.sh/uv/)
|
||||||
|
|
||||||
|
|
||||||
@@ -147,11 +150,14 @@ care that if you uncomment a particular option, also uncomment its containing
|
|||||||
section name—or else borgmatic won't recognize the option.
|
section name—or else borgmatic won't recognize the option.
|
||||||
|
|
||||||
You can get the same sample configuration file from the [configuration
|
You can get the same sample configuration file from the [configuration
|
||||||
reference](https://torsion.org/borgmatic/docs/reference/configuration/), the
|
reference](https://torsion.org/borgmatic/reference/configuration/), the
|
||||||
authoritative set of all configuration options. This is handy if borgmatic has
|
authoritative set of all configuration options. This is handy if borgmatic has
|
||||||
added new options since you originally created your configuration file. Also
|
added new options since you originally created your configuration file. Also
|
||||||
check out how to [upgrade your
|
check out how to [upgrade your
|
||||||
configuration](https://torsion.org/borgmatic/docs/how-to/upgrade/#upgrading-your-configuration).
|
configuration](https://torsion.org/borgmatic/how-to/upgrade/#upgrading-your-configuration).
|
||||||
|
|
||||||
|
And see the documentation on [patterns and
|
||||||
|
excludes](https://torsion.org/borgmatic/reference/configuration/patterns-and-excludes/).
|
||||||
|
|
||||||
|
|
||||||
### Encryption
|
### Encryption
|
||||||
@@ -174,7 +180,7 @@ for more info.
|
|||||||
|
|
||||||
If you'd like to configure your backups to go to multiple different
|
If you'd like to configure your backups to go to multiple different
|
||||||
repositories, see the documentation on how to [make backups
|
repositories, see the documentation on how to [make backups
|
||||||
redundant](https://torsion.org/borgmatic/docs/how-to/make-backups-redundant/).
|
redundant](https://torsion.org/borgmatic/how-to/make-backups-redundant/).
|
||||||
|
|
||||||
|
|
||||||
### Validation
|
### Validation
|
||||||
@@ -268,48 +274,20 @@ expectations based on your configuration.
|
|||||||
If you'd like to specify an alternate configuration file path, use the
|
If you'd like to specify an alternate configuration file path, use the
|
||||||
`--config` flag.
|
`--config` flag.
|
||||||
|
|
||||||
See `borgmatic --help` and `borgmatic create --help` for more information.
|
See [command-line
|
||||||
|
documentation](https://torsion.org/borgmatic/reference/command-line/) and the
|
||||||
|
[create action
|
||||||
|
documentation](https://torsion.org/borgmatic/reference/command-line/actions/create/)
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
|
||||||
## Default actions
|
<a id="default-actions"></a>
|
||||||
|
<a id="skipping-actions"></a>
|
||||||
|
<a id="disabling-default-actions"></a>
|
||||||
|
|
||||||
If you omit `create` and other actions, borgmatic runs through a set of
|
And check out the [actions
|
||||||
default actions: `prune` any old backups as per the configured retention
|
documentation](https://torsion.org/borgmatic/reference/command-line/actions/)
|
||||||
policy, `compact` segments to free up space (with Borg 1.2+, borgmatic
|
for details on customizing the actions that borgmatic runs.
|
||||||
1.5.23+), `create` a backup, *and* `check` backups for consistency problems
|
|
||||||
due to things like file damage. For instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo borgmatic --verbosity 1 --list --stats
|
|
||||||
```
|
|
||||||
|
|
||||||
### Skipping actions
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.8.5</span> You can
|
|
||||||
configure borgmatic to skip running certain actions (default or otherwise).
|
|
||||||
For instance, to always skip the `compact` action when using [Borg's
|
|
||||||
append-only
|
|
||||||
mode](https://borgbackup.readthedocs.io/en/stable/usage/notes.html#append-only-mode-forbid-compaction),
|
|
||||||
set the `skip_actions` option:
|
|
||||||
|
|
||||||
```
|
|
||||||
skip_actions:
|
|
||||||
- compact
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disabling default actions
|
|
||||||
|
|
||||||
By default, running `borgmatic` without any arguments will perform the default
|
|
||||||
backup actions (create, prune, compact and check). If you want to disable this
|
|
||||||
behavior and require explicit actions to be specified, add the following to
|
|
||||||
your configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
default_actions: false
|
|
||||||
```
|
|
||||||
|
|
||||||
With this setting, running `borgmatic` without arguments will show the help
|
|
||||||
message instead of performing any actions.
|
|
||||||
|
|
||||||
|
|
||||||
## Autopilot
|
## Autopilot
|
||||||
@@ -339,7 +317,7 @@ If you're using systemd instead of cron to run jobs, you can still configure
|
|||||||
borgmatic to run automatically.
|
borgmatic to run automatically.
|
||||||
|
|
||||||
(If you installed borgmatic from [Other ways to
|
(If you installed borgmatic from [Other ways to
|
||||||
install](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#other-ways-to-install),
|
install](https://torsion.org/borgmatic/how-to/set-up-backups/#other-ways-to-install),
|
||||||
you may already have borgmatic systemd service and timer files. If so, you may
|
you may already have borgmatic systemd service and timer files. If so, you may
|
||||||
be able to skip some of the steps below.)
|
be able to skip some of the steps below.)
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 9
|
order: 9
|
||||||
---
|
---
|
||||||
## Filesystem hooks
|
|
||||||
|
|
||||||
Many filesystems support taking snapshots—point-in-time, read-only "copies" of
|
Many filesystems support taking snapshots—point-in-time, read-only "copies" of
|
||||||
your data, ideal for backing up files that may change during the backup. These
|
your data, ideal for backing up files that may change during the backup. These
|
||||||
snapshots initially don't use any additional storage space and can be made
|
snapshots initially don't use any additional storage space and can be made
|
||||||
@@ -16,6 +14,10 @@ To help automate backup of these filesystems, borgmatic can use them to take
|
|||||||
snapshots.
|
snapshots.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="dataset-discovery"></a>
|
||||||
|
<a id="zfs-performance"></a>
|
||||||
|
|
||||||
|
|
||||||
### ZFS
|
### ZFS
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.3</span> <span
|
<span class="minilink minilink-addedin">New in version 1.9.3</span> <span
|
||||||
@@ -23,25 +25,17 @@ class="minilink minilink-addedin">Beta feature</span> borgmatic supports
|
|||||||
taking snapshots with the [ZFS filesystem](https://openzfs.org/) and sending
|
taking snapshots with the [ZFS filesystem](https://openzfs.org/) and sending
|
||||||
those snapshots to Borg for backup.
|
those snapshots to Borg for backup.
|
||||||
|
|
||||||
To use this feature, first you need one or more mounted ZFS datasets. Then,
|
To use this feature, add one or more ZFS dataset paths to your
|
||||||
enable ZFS within borgmatic by adding the following line to your configuration
|
`source_directories`. Then, enable borgmatic's ZFS snapshotting of those
|
||||||
file:
|
datasets by adding the following line to your configuration file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
zfs:
|
zfs:
|
||||||
```
|
```
|
||||||
|
|
||||||
No other options are necessary to enable ZFS support, but if desired you can
|
No other options are necessary to enable ZFS support. But if you're using
|
||||||
override some of the commands used by the ZFS hook. For instance:
|
systemd to run borgmatic, you'll likely need to modify the [sample systemd
|
||||||
|
service
|
||||||
```yaml
|
|
||||||
zfs:
|
|
||||||
zfs_command: /usr/local/bin/zfs
|
|
||||||
mount_command: /usr/local/bin/mount
|
|
||||||
umount_command: /usr/local/bin/umount
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're using systemd to run borgmatic, you will likely need to modify the [sample systemd service
|
|
||||||
file](https://projects.torsion.org/borgmatic-collective/borgmatic/raw/branch/main/sample/systemd/borgmatic.service)
|
file](https://projects.torsion.org/borgmatic-collective/borgmatic/raw/branch/main/sample/systemd/borgmatic.service)
|
||||||
to work with ZFS. See the comments in that file for details.
|
to work with ZFS. See the comments in that file for details.
|
||||||
|
|
||||||
@@ -50,101 +44,20 @@ and/or may not work well for your use cases. But feel free to use it in
|
|||||||
production if you're okay with these caveats, and please [provide any
|
production if you're okay with these caveats, and please [provide any
|
||||||
feedback](https://torsion.org/borgmatic/#issues) you have on this feature.
|
feedback](https://torsion.org/borgmatic/#issues) you have on this feature.
|
||||||
|
|
||||||
|
For additional details about ZFS support, see [borgmatic's ZFS
|
||||||
#### Dataset discovery
|
documentation](https://torsion.org/borgmatic/reference/configuration/data-sources/zfs/).
|
||||||
|
|
||||||
You have a couple of options for borgmatic to find and backup your ZFS datasets:
|
|
||||||
|
|
||||||
* For any dataset you'd like backed up, add its mount point to borgmatic's
|
|
||||||
`source_directories` option.
|
|
||||||
* <span class="minilink minilink-addedin">New in version 1.9.6</span> Or
|
|
||||||
include the mount point as a root pattern with borgmatic's `patterns` or
|
|
||||||
`patterns_from` options.
|
|
||||||
* Or set the borgmatic-specific user property
|
|
||||||
`org.torsion.borgmatic:backup=auto` onto your dataset, e.g. by running `zfs
|
|
||||||
set org.torsion.borgmatic:backup=auto datasetname`. Then borgmatic can find
|
|
||||||
and backup these datasets.
|
|
||||||
|
|
||||||
If you have multiple borgmatic configuration files with ZFS enabled, and you'd
|
|
||||||
like particular datasets to be backed up only for particular configuration
|
|
||||||
files, use the `source_directories` option instead of the user property.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.11</span> borgmatic
|
|
||||||
won't snapshot datasets with the `canmount=off` property, which is often set on
|
|
||||||
datasets that only serve as a container for other datasets. Use `zfs get
|
|
||||||
canmount datasetname` to see the `canmount` value for a dataset.
|
|
||||||
|
|
||||||
During a backup, borgmatic automatically snapshots these discovered datasets,
|
|
||||||
temporarily mounts the snapshots within its [runtime
|
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory),
|
|
||||||
and includes the snapshotted files in the paths sent to Borg. borgmatic is also
|
|
||||||
responsible for cleaning up (destroying) these snapshots after a backup
|
|
||||||
completes.
|
|
||||||
|
|
||||||
Additionally, borgmatic rewrites the snapshot file paths so that they appear
|
|
||||||
at their original dataset locations in a Borg archive. For instance, if your
|
|
||||||
dataset is mounted at `/var/dataset`, then the snapshotted files will appear
|
|
||||||
in an archive at `/var/dataset` as well—even if borgmatic has to mount the
|
|
||||||
snapshot somewhere in `/run/user/1000/borgmatic/zfs_snapshots/` to perform the
|
|
||||||
backup.
|
|
||||||
|
|
||||||
If a dataset has a separate filesystem mounted somewhere within it, that
|
|
||||||
filesystem won't get included in the snapshot. For instance, if `/` is a ZFS
|
|
||||||
dataset but `/boot` is a separate filesystem, borgmatic won't include `/boot` as
|
|
||||||
part of the dataset snapshot. You can however add `/boot` to
|
|
||||||
`source_directories` if you'd like it included in your backup.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.4</span> borgmatic
|
|
||||||
is smart enough to look at the parent (and grandparent, etc.) directories of
|
|
||||||
each of your `source_directories` to discover any datasets. For instance,
|
|
||||||
let's say you add `/var/log` and `/var/lib` to your source directories, but
|
|
||||||
`/var` is a dataset. borgmatic will discover that and snapshot `/var`
|
|
||||||
accordingly. This also works even with nested datasets; borgmatic selects
|
|
||||||
the dataset that's the "closest" parent to your source directories.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.6</span> When using
|
|
||||||
[patterns](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-patterns),
|
|
||||||
the initial portion of a pattern's path that you intend borgmatic to match
|
|
||||||
against a dataset can't have globs or other non-literal characters in it—or it
|
|
||||||
won't actually match. For instance, a mount point of `/var` would match a
|
|
||||||
pattern of `+ fm:/var/*/data`, but borgmatic isn't currently smart enough to
|
|
||||||
match `/var` to a pattern like `+ fm:/v*/lib/data`.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.2 and
|
|
||||||
earlier</span>Snapshotted files are instead stored at a path dependent on the
|
|
||||||
[runtime
|
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory)
|
|
||||||
in use at the time the archive was created, as Borg 1.2 and earlier do not
|
|
||||||
support path rewriting.
|
|
||||||
|
|
||||||
|
|
||||||
#### Extract a dataset
|
#### Extract a dataset
|
||||||
|
|
||||||
Filesystem snapshots are stored in a Borg archive as normal files, so
|
Filesystem snapshots are stored in a Borg archive as normal files, so
|
||||||
you can use the standard
|
you can use the standard
|
||||||
[extract action](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/) to
|
[extract action](https://torsion.org/borgmatic/how-to/extract-a-backup/) to
|
||||||
extract them.
|
extract them.
|
||||||
|
|
||||||
|
|
||||||
#### ZFS performance
|
<a id="subvolume-discovery"></a>
|
||||||
|
<a id="btrfs-performance"></a>
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.x</span> Because of
|
|
||||||
the way that ZFS snapshot paths can change from one borgmatic invocation to the
|
|
||||||
next, the [Borg file
|
|
||||||
cache](https://borgbackup.readthedocs.io/en/stable/internals/data-structures.html#cache)
|
|
||||||
may not get cache hits on snapshotted files. This makes backing up ZFS snapshots
|
|
||||||
a little slower than non-snapshotted files that have consistent paths. You can
|
|
||||||
mitigate this by setting a fixed [runtime
|
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory)
|
|
||||||
(that's not located in `/tmp`). This allows borgmatic to use a consistent
|
|
||||||
snapshot path from one run to the next, thereby resulting in Borg files cache
|
|
||||||
hits.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 2.x</span> Snapshotted
|
|
||||||
files should get cache hits regardless of whether their paths change, because
|
|
||||||
Borg 2.x is smarter about how it looks up file paths in its cache—it constructs
|
|
||||||
the cache key with the path *as it's seen in the archive* (which is consistent
|
|
||||||
across runs) rather than the full absolute source path (which can change).
|
|
||||||
|
|
||||||
|
|
||||||
### Btrfs
|
### Btrfs
|
||||||
@@ -154,23 +67,16 @@ class="minilink minilink-addedin">Beta feature</span> borgmatic supports taking
|
|||||||
snapshots with the [Btrfs filesystem](https://btrfs.readthedocs.io/) and sending
|
snapshots with the [Btrfs filesystem](https://btrfs.readthedocs.io/) and sending
|
||||||
those snapshots to Borg for backup.
|
those snapshots to Borg for backup.
|
||||||
|
|
||||||
To use this feature, first you need one or more Btrfs subvolumes. Then, enable
|
To use this feature, add one or more subvolume paths to your
|
||||||
Btrfs within borgmatic by adding the following line to your configuration file:
|
`source_directories`. Then, enable Btrfs within borgmatic by adding the
|
||||||
|
following line to your configuration file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
btrfs:
|
btrfs:
|
||||||
```
|
```
|
||||||
|
|
||||||
No other options are necessary to enable Btrfs support, but if desired you can
|
No other options are necessary to enable Btrfs support. But if you're using
|
||||||
override some of the commands used by the Btrfs hook. For instance:
|
systemd to run borgmatic, you may need to modify the [sample systemd service
|
||||||
|
|
||||||
```yaml
|
|
||||||
btrfs:
|
|
||||||
btrfs_command: /usr/local/bin/btrfs
|
|
||||||
findmnt_command: /usr/local/bin/findmnt
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're using systemd to run borgmatic, you may need to modify the [sample systemd service
|
|
||||||
file](https://projects.torsion.org/borgmatic-collective/borgmatic/raw/branch/main/sample/systemd/borgmatic.service)
|
file](https://projects.torsion.org/borgmatic-collective/borgmatic/raw/branch/main/sample/systemd/borgmatic.service)
|
||||||
to work with Btrfs. See the comments in that file for details.
|
to work with Btrfs. See the comments in that file for details.
|
||||||
|
|
||||||
@@ -179,82 +85,21 @@ and/or may not work well for your use cases. But feel free to use it in
|
|||||||
production if you're okay with these caveats, and please [provide any
|
production if you're okay with these caveats, and please [provide any
|
||||||
feedback](https://torsion.org/borgmatic/#issues) you have on this feature.
|
feedback](https://torsion.org/borgmatic/#issues) you have on this feature.
|
||||||
|
|
||||||
|
For additional details about Btrfs support, see [borgmatic's Btrfs
|
||||||
#### Subvolume discovery
|
documentation](https://torsion.org/borgmatic/reference/configuration/data-sources/btrfs/).
|
||||||
|
|
||||||
For any read-write subvolume you'd like backed up, add its subvolume path to
|
|
||||||
borgmatic's `source_directories` option. Btrfs does not support snapshotting
|
|
||||||
read-only subvolumes.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 2.0.7</span> The path can
|
|
||||||
be either the path of the subvolume itself or the mount point where the
|
|
||||||
subvolume is mounted. Prior to version 2.0.7, the subvolume path could not be
|
|
||||||
used if the subvolume was mounted elsewhere; only the mount point could be used.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.6</span> Instead of
|
|
||||||
using `source_directories`, you can include the subvolume path as a root pattern
|
|
||||||
with borgmatic's `patterns` or `patterns_from` options.
|
|
||||||
|
|
||||||
During a backup, borgmatic snapshots these subvolumes and includes the
|
|
||||||
snapshotted files in the paths sent to Borg. borgmatic is also responsible for
|
|
||||||
cleaning up (deleting) these snapshots after a backup completes.
|
|
||||||
|
|
||||||
borgmatic is smart enough to look at the parent (and grandparent, etc.)
|
|
||||||
directories of each of your `source_directories` to discover any subvolumes. For
|
|
||||||
instance, let's say you add `/var/log` and `/var/lib` to your source
|
|
||||||
directories, but `/var` is a subvolume path. borgmatic will discover that and
|
|
||||||
snapshot `/var` accordingly. This also works even with nested subvolumes;
|
|
||||||
borgmatic selects the subvolume that's the "closest" parent to your source
|
|
||||||
directories.
|
|
||||||
|
|
||||||
If a subvolume has a separate filesystem mounted somewhere within it, that
|
|
||||||
filesystem won't get included in the snapshot. For instance, if `/` is a Btrfs
|
|
||||||
subvolume but `/boot` is a separate filesystem, borgmatic won't include `/boot`
|
|
||||||
as part of the subvolume snapshot. You can however add `/boot` to
|
|
||||||
`source_directories` if you'd like it included in your backup.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.6</span> When using
|
|
||||||
[patterns](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-patterns),
|
|
||||||
the initial portion of a pattern's path that you intend borgmatic to match
|
|
||||||
against a subvolume path can't have globs or other non-literal characters in
|
|
||||||
it—or it won't actually match. For instance, a subvolume path of `/var` would
|
|
||||||
match a pattern of `+ fm:/var/*/data`, but borgmatic isn't currently smart
|
|
||||||
enough to match `/var` to a pattern like `+ fm:/v*/lib/data`.
|
|
||||||
|
|
||||||
Additionally, borgmatic rewrites the snapshot file paths so that they appear at
|
|
||||||
their original subvolume locations in a Borg archive. For instance, if your
|
|
||||||
subvolume path is `/var/subvolume`, then the snapshotted files will appear in an
|
|
||||||
archive at `/var/subvolume` as well—even if borgmatic has to mount the snapshot
|
|
||||||
somewhere in `/var/subvolume/.borgmatic-snapshot-1234/` to perform the backup.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.2 and
|
|
||||||
earlier</span>Snapshotted files are instead stored at a path dependent on the
|
|
||||||
temporary snapshot directory in use at the time the archive was created, as Borg
|
|
||||||
1.2 and earlier do not support path rewriting.
|
|
||||||
|
|
||||||
|
|
||||||
#### Extract a subvolume
|
#### Extract a subvolume
|
||||||
|
|
||||||
Subvolume snapshots are stored in a Borg archive as normal files, so you can use
|
Subvolume snapshots are stored in a Borg archive as normal files, so you can use
|
||||||
the standard [extract
|
the standard [extract
|
||||||
action](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/) to extract
|
action](https://torsion.org/borgmatic/how-to/extract-a-backup/) to extract
|
||||||
them.
|
them.
|
||||||
|
|
||||||
|
|
||||||
#### Btrfs performance
|
<a id="snapshot-size"></a>
|
||||||
|
<a id="logical-volume-discovery"></a>
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.x</span> Because of
|
<a id="lvm-performance"></a>
|
||||||
the way that Btrfs snapshot paths change from one borgmatic invocation to the
|
|
||||||
next, the [Borg file
|
|
||||||
cache](https://borgbackup.readthedocs.io/en/stable/internals/data-structures.html#cache)
|
|
||||||
will never get cache hits on snapshotted files. This makes backing up Btrfs
|
|
||||||
snapshots a little slower than non-snapshotted files that have consistent paths.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 2.x</span> Even
|
|
||||||
snapshotted files should get cache hits, because Borg 2.x is smarter about how
|
|
||||||
it looks up file paths in its cache—it constructs the cache key with the path
|
|
||||||
*as it's seen in the archive* (which is consistent across runs) rather than the
|
|
||||||
full absolute source path (which changes).
|
|
||||||
|
|
||||||
|
|
||||||
### LVM
|
### LVM
|
||||||
@@ -274,29 +119,16 @@ KVM that contains an MBR, partitions, etc.).
|
|||||||
In those cases, you can omit the `lvm:` option and use Borg's own support for
|
In those cases, you can omit the `lvm:` option and use Borg's own support for
|
||||||
[image backup](https://borgbackup.readthedocs.io/en/stable/deployment/image-backup.html).
|
[image backup](https://borgbackup.readthedocs.io/en/stable/deployment/image-backup.html).
|
||||||
|
|
||||||
To use the LVM snapshot feature, first you need one or more mounted LVM logical
|
To use the LVM snapshot feature, add one or more mounted LVM logical volumes to
|
||||||
volumes. Then, enable LVM within borgmatic by adding the following line to your
|
your `source_directories`. Then, enable LVM within borgmatic by adding the
|
||||||
configuration file:
|
following line to your configuration file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
lvm:
|
lvm:
|
||||||
```
|
```
|
||||||
|
|
||||||
No other options are necessary to enable LVM support, but if desired you can
|
No other options are necessary to enable LVM support. But if you're using
|
||||||
override some of the options used by the LVM hook. For instance:
|
systemd to run borgmatic, you may need to modify the [sample systemd service
|
||||||
|
|
||||||
```yaml
|
|
||||||
lvm:
|
|
||||||
snapshot_size: 5GB # See below for details.
|
|
||||||
lvcreate_command: /usr/local/bin/lvcreate
|
|
||||||
lvremove_command: /usr/local/bin/lvremove
|
|
||||||
lvs_command: /usr/local/bin/lvs
|
|
||||||
lsbrk_command: /usr/local/bin/lsbrk
|
|
||||||
mount_command: /usr/local/bin/mount
|
|
||||||
umount_command: /usr/local/bin/umount
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're using systemd to run borgmatic, you may need to modify the [sample systemd service
|
|
||||||
file](https://projects.torsion.org/borgmatic-collective/borgmatic/raw/branch/main/sample/systemd/borgmatic.service)
|
file](https://projects.torsion.org/borgmatic-collective/borgmatic/raw/branch/main/sample/systemd/borgmatic.service)
|
||||||
to work with LVM. See the comments in that file for details.
|
to work with LVM. See the comments in that file for details.
|
||||||
|
|
||||||
@@ -305,100 +137,13 @@ and/or may not work well for your use cases. But feel free to use it in
|
|||||||
production if you're okay with these caveats, and please [provide any
|
production if you're okay with these caveats, and please [provide any
|
||||||
feedback](https://torsion.org/borgmatic/#issues) you have on this feature.
|
feedback](https://torsion.org/borgmatic/#issues) you have on this feature.
|
||||||
|
|
||||||
|
For additional details about LVM support, see [borgmatic's LVM
|
||||||
#### Snapshot size
|
documentation](https://torsion.org/borgmatic/reference/configuration/data-sources/lvm/).
|
||||||
|
|
||||||
The `snapshot_size` option is the size to allocate for each snapshot taken,
|
|
||||||
including the units to use for that size. While borgmatic's snapshots
|
|
||||||
themselves are read-only and don't change during backups, the logical volume
|
|
||||||
being snapshotted *can* change—therefore requiring additional snapshot storage
|
|
||||||
since LVM snapshots are copy-on-write. And if the configured snapshot size is
|
|
||||||
too small (and LVM isn't configured to grow snapshots automatically), then the
|
|
||||||
snapshots will fail to allocate enough space, resulting in a broken backup.
|
|
||||||
|
|
||||||
If not specified, the `snapshot_size` option defaults to `10%ORIGIN`, which
|
|
||||||
means 10% of the size of the logical volume being snapshotted. See the
|
|
||||||
[`lvcreate --size` and `--extents`
|
|
||||||
documentation](https://www.man7.org/linux/man-pages/man8/lvcreate.8.html) for
|
|
||||||
more information about possible values here. (Under the hood, borgmatic uses
|
|
||||||
`lvcreate --extents` if the `snapshot_size` is a percentage value, and `lvcreate
|
|
||||||
--size` otherwise.)
|
|
||||||
|
|
||||||
|
|
||||||
#### Logical volume discovery
|
|
||||||
|
|
||||||
For any logical volume you'd like backed up, add its mount point to
|
|
||||||
borgmatic's `source_directories` option.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.6</span> Or include
|
|
||||||
the mount point as a root pattern with borgmatic's `patterns` or `patterns_from`
|
|
||||||
options.
|
|
||||||
|
|
||||||
During a backup, borgmatic automatically snapshots these discovered logical volumes
|
|
||||||
(non-recursively), temporarily mounts the snapshots within its [runtime
|
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory), and
|
|
||||||
includes the snapshotted files in the paths sent to Borg. borgmatic is also responsible for cleaning
|
|
||||||
up (deleting) these snapshots after a backup completes.
|
|
||||||
|
|
||||||
borgmatic is smart enough to look at the parent (and grandparent, etc.)
|
|
||||||
directories of each of your `source_directories` to discover any logical
|
|
||||||
volumes. For instance, let's say you add `/var/log` and `/var/lib` to your
|
|
||||||
source directories, but `/var` is a logical volume. borgmatic will discover
|
|
||||||
that and snapshot `/var` accordingly.
|
|
||||||
|
|
||||||
If a logical volume has a separate filesystem mounted somewhere within it, that
|
|
||||||
filesystem won't get included in the snapshot. For instance, if `/` is an LVM
|
|
||||||
logical volume but `/boot` is a separate filesystem, borgmatic won't include
|
|
||||||
`/boot` as part of the logical volume snapshot. You can however add `/boot` to
|
|
||||||
`source_directories` if you'd like it included in your backup.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.9.6</span> When using
|
|
||||||
[patterns](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-patterns),
|
|
||||||
the initial portion of a pattern's path that you intend borgmatic to match
|
|
||||||
against a logical volume can't have globs or other non-literal characters in
|
|
||||||
it—or it won't actually match. For instance, a logical volume of `/var` would
|
|
||||||
match a pattern of `+ fm:/var/*/data`, but borgmatic isn't currently smart
|
|
||||||
enough to match `/var` to a pattern like `+ fm:/v*/lib/data`.
|
|
||||||
|
|
||||||
Additionally, borgmatic rewrites the snapshot file paths so that they appear
|
|
||||||
at their original logical volume locations in a Borg archive. For instance, if
|
|
||||||
your logical volume is mounted at `/var/lvolume`, then the snapshotted files
|
|
||||||
will appear in an archive at `/var/lvolume` as well—even if borgmatic has to
|
|
||||||
mount the snapshot somewhere in `/run/user/1000/borgmatic/lvm_snapshots/` to
|
|
||||||
perform the backup.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.2 and
|
|
||||||
earlier</span>Snapshotted files are instead stored at a path dependent on the
|
|
||||||
[runtime
|
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory)
|
|
||||||
in use at the time the archive was created, as Borg 1.2 and earlier do not
|
|
||||||
support path rewriting.
|
|
||||||
|
|
||||||
|
|
||||||
#### Extract a logical volume
|
#### Extract a logical volume
|
||||||
|
|
||||||
Logical volume snapshots are stored in a Borg archive as normal files, so
|
Logical volume snapshots are stored in a Borg archive as normal files, so
|
||||||
you can use the standard
|
you can use the standard
|
||||||
[extract action](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/) to
|
[extract action](https://torsion.org/borgmatic/how-to/extract-a-backup/) to
|
||||||
extract them.
|
extract them.
|
||||||
|
|
||||||
|
|
||||||
#### LVM performance
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 1.x</span> Because of
|
|
||||||
the way that LVM snapshot paths can change from one borgmatic invocation to the
|
|
||||||
next, the [Borg file
|
|
||||||
cache](https://borgbackup.readthedocs.io/en/stable/internals/data-structures.html#cache)
|
|
||||||
may not get cache hits on snapshotted files. This makes backing up LVM snapshots
|
|
||||||
a little slower than non-snapshotted files that have consistent paths. You can
|
|
||||||
mitigate this by setting a fixed [runtime
|
|
||||||
directory](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#runtime-directory)
|
|
||||||
(that's not located in `/tmp`). This allows borgmatic to use a consistent
|
|
||||||
snapshot path from one run to the next, thereby resulting in Borg files cache
|
|
||||||
hits.
|
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">With Borg version 2.x</span> Snapshotted
|
|
||||||
files should get cache hits regardless of whether their paths change, because
|
|
||||||
Borg 2.x is smarter about how it looks up file paths in its cache—it constructs
|
|
||||||
the cache key with the path *as it's seen in the archive* (which is consistent
|
|
||||||
across runs) rather than the full absolute source path (which can change).
|
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ eleventyNavigation:
|
|||||||
parent: How-to guides
|
parent: How-to guides
|
||||||
order: 14
|
order: 14
|
||||||
---
|
---
|
||||||
## Upgrading borgmatic
|
|
||||||
|
|
||||||
In general, all you should need to do to upgrade borgmatic if you've
|
In general, all you should need to do to upgrade borgmatic if you've
|
||||||
[installed it with
|
[installed it with
|
||||||
pipx](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#installation)
|
pipx](https://torsion.org/borgmatic/how-to/set-up-backups/#installation)
|
||||||
is to run the following:
|
is to run the following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -23,11 +21,11 @@ upgrade each installation independently.
|
|||||||
If you originally installed borgmatic with `sudo pip3 install --user`, you can
|
If you originally installed borgmatic with `sudo pip3 install --user`, you can
|
||||||
uninstall it first with `sudo pip3 uninstall borgmatic` and then [install it
|
uninstall it first with `sudo pip3 uninstall borgmatic` and then [install it
|
||||||
again with
|
again with
|
||||||
pipx](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#installation),
|
pipx](https://torsion.org/borgmatic/how-to/set-up-backups/#installation),
|
||||||
which should better isolate borgmatic from your other Python applications.
|
which should better isolate borgmatic from your other Python applications.
|
||||||
|
|
||||||
But if you [installed borgmatic without pipx or
|
But if you [installed borgmatic without pipx or
|
||||||
pip3](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#other-ways-to-install),
|
pip3](https://torsion.org/borgmatic/how-to/set-up-backups/#other-ways-to-install),
|
||||||
then your upgrade method may be different.
|
then your upgrade method may be different.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: borg
|
||||||
|
eleventyNavigation:
|
||||||
|
key: borg
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/borg.txt %}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Related documentation
|
||||||
|
|
||||||
|
* [How to run arbitrary Borg commands](https://torsion.org/borgmatic/how-to/run-arbitrary-borg-commands/)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: break-lock
|
||||||
|
eleventyNavigation:
|
||||||
|
key: break-lock
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/break-lock.txt %}
|
||||||
|
```
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: check
|
||||||
|
eleventyNavigation:
|
||||||
|
key: check
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/check.txt %}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related documentation
|
||||||
|
|
||||||
|
* [Consistency checks](https://torsion.org/borgmatic/reference/configuration/consistency-checks/)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: compact
|
||||||
|
eleventyNavigation:
|
||||||
|
key: compact
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/compact.txt %}
|
||||||
|
```
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: config bootstrap
|
||||||
|
eleventyNavigation:
|
||||||
|
key: config bootstrap
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/config-bootstrap.txt %}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Related documentation
|
||||||
|
|
||||||
|
* [Extract the configuration files used to create an archive](https://torsion.org/borgmatic/how-to/extract-a-backup/#extract-the-configuration-files-used-to-create-an-archive)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: config generate
|
||||||
|
eleventyNavigation:
|
||||||
|
key: config generate
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/config-generate.txt %}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Related documentation
|
||||||
|
|
||||||
|
* [Configuration](https://torsion.org/borgmatic/how-to/set-up-backups/#configuration)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: config validate
|
||||||
|
eleventyNavigation:
|
||||||
|
key: config validate
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/config-validate.txt %}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Related documentation
|
||||||
|
|
||||||
|
* [Validation](https://torsion.org/borgmatic/how-to/set-up-backups/#validation)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: config
|
||||||
|
eleventyNavigation:
|
||||||
|
key: config
|
||||||
|
parent: 🎬 Actions
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
{% include borgmatic/command-line/config.txt %}
|
||||||
|
```
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user