2 Commits

Author SHA1 Message Date
Erik Westrup
3e540ef5e3 Modified: CHANGELOG.md 2022-07-27 18:16:15 +02:00
Mikhail Delport
2759964b13 Add support saving hourly snapshots 2022-07-23 21:07:28 -04:00
23 changed files with 136 additions and 374 deletions

View File

@@ -1,17 +0,0 @@
{
"features": {
"ghcr.io/devcontainers-extra/features/apt-get-packages:1": {
"packages": ["shellcheck"]
}
},
"postCreateCommand": "bash scripts/devcontainer_postCreateCommand.sh",
"customizations": {
"vscode": {
"extensions": [
"rogalmic.bash-debug",
"timonwong.shellcheck",
"mads-hartmann.bash-ide-vscode"
]
}
}
}

View File

@@ -1,47 +1,33 @@
name: Lint
name: Lint Code Base
on:
workflow_dispatch:
push:
branches: main
paths:
- "**.sh"
- ".github/workflows/linter.yml"
- "bin/**"
- '**.sh'
- '.github/workflows/linter.yml'
- 'bin/**'
pull_request:
branches: main
paths:
- "**.sh"
- ".github/workflows/linter.yml"
- "bin/**"
permissions: {}
- '**.sh'
- '.github/workflows/linter.yml'
- 'bin/**'
jobs:
build:
name: Lint
name: Lint Code Base
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
# To report GitHub Actions status checks
statuses: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@v2
with:
# super-linter needs the full git history to get the
# list of files that changed across commits
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Super-linter
uses: super-linter/super-linter@v7.3.0
- name: Lint Code Base
uses: github/super-linter@v4
env:
# To report GitHub Actions status checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BRANCH: main
VALIDATE_ALL_CODEBASE: true
VALIDATE_BASH: true
IGNORE_GENERATED_FILES: true
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,17 +0,0 @@
name: "Close stale issues and PRs"
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 0"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
with:
days-before-stale: 180
exempt-issue-labels: "NotStale"
exempt-pr-labels: "NotStale"
stale-issue-message: "Issue is stale; will soon close."
stale-pr-message: "PR is stale; will soon close."

View File

@@ -1,4 +0,0 @@
alpha: 0
beta: 0
rc: 0
release: v7.4.0

View File

@@ -6,15 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Debug scripts by setting `TRACE=1`.
- Add semver-cli for git tagging.
### Changed
- Warn on certain unset envvars instead of error-exit.
## [7.4.0] - 2023-03-08
### Added
- Support saving hourly snapshots. [#98](https://github.com/erikw/restic-automatic-backup-scheduler/pull/98)
- Support for pre backup script at /etc/restic/pre_backup.sh [107](https://github.com/erikw/restic-automatic-backup-scheduler/pull/107)
### Fixed
- Full path to `/bin/bash` in sytemd services. [#96](https://github.com/erikw/restic-automatic-backup-scheduler/issues/96)

View File

@@ -12,12 +12,12 @@
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>source {{ INSTALL_PREFIX }}/etc/restic/default.env.sh &amp;&amp; {{ INSTALL_PREFIX }}/bin/restic_backup.sh >>$HOME/$LOG_OUT 2>>$HOME/$LOG_ERR</string>
<string>source $INSTALL_PREFIX/etc/restic/default.env.sh &amp;&amp; $INSTALL_PREFIX/bin/restic_backup.sh >>$HOME/$LOG_OUT 2>>$HOME/$LOG_ERR</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>{{ INSTALL_PREFIX }}/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<string>$INSTALL_PREFIX/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>LOG_OUT</key>
<string>/Library/Logs/restic/backup_stdout.log</string>
<key>LOG_ERR</key>

View File

@@ -8,12 +8,12 @@
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>source {{ INSTALL_PREFIX }}/etc/restic/default.env.sh &amp;&amp; {{ INSTALL_PREFIX }}/bin/restic_check.sh >>$HOME/$LOG_OUT 2>>$HOME/$LOG_ERR</string>
<string>source $INSTALL_PREFIX/etc/restic/default.env.sh &amp;&amp; $INSTALL_PREFIX/bin/restic_check.sh >>$HOME/$LOG_OUT 2>>$HOME/$LOG_ERR</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>{{ INSTALL_PREFIX }}/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<string>$INSTALL_PREFIX/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>LOG_OUT</key>
<string>/Library/Logs/restic/check_stdout.log</string>
<key>LOG_ERR</key>

View File

@@ -1,14 +1,14 @@
#### Notes ####################################################################
# This build process is done in three stages (out-of-source build):
# 1. copy source files to the local build directory.
# 2. build dir: replace the string "{{ INSTALL_PREFIX }}" with the value of $PREFIX
# 2. build dir: replace the string "$INSTALL_PREFIX" with the value of $PREFIX
# 3. install files from the build directory to the target directory.
#
# Why this dance?
# * To fully support that a user can install this project to a custom path e.g.
# $(PREFIX=/usr/local make install), we need to modify the files that refer
# to other files on disk. We do this by having a placeholder
# "{{ INSTALL_PREFIX }}" that is substituted with the value of $PREFIX when
# "$INSTALL_PREFIX" that is substituted with the value of $PREFIX when
# installed.
# * We don't want to modify the files that are controlled by git, thus let's
# copy them to a build directory and then modify.
@@ -57,7 +57,7 @@ LAUNCHAGENT_CHECK = com.github.erikw.restic-check
LAUNCHAGENT_TARGET_BACKUP = gui/$(UID)/$(LAUNCHAGENT_BACKUP)
LAUNCHAGENT_TARGET_CHECK = gui/$(UID)/$(LAUNCHAGENT_CHECK)
# What to substitute {{ INSTALL_PREFIX }} in sources to.
# What to substitute $INSTALL_PREFIX in sources to.
# This can be useful to set to empty on commandline when building e.g. an AUR
# package in a separate build directory (PREFIX).
INSTALL_PREFIX := $(PREFIX)
@@ -192,11 +192,11 @@ install-targets-schedtask: $(BUILD_DIR_SCHEDTASK)/$(SCHEDTASK_INSTALL)
uninstall-targets-schedtask: $(BUILD_DIR_SCHEDTASK)/$(SCHEDTASK_UNINSTALL)
test $(CUR_OS) != Windows || ./$<
# Copies sources to build directory & replace "{{ INSTALL_PREFIX }}".
# Copies sources to build directory & replace "$INSTALL_PREFIX".
$(BUILD_DIR)/% : %
@${MKDIR_PARENTS} $@
cp $< $@
sed -i.bak -e 's|{{ INSTALL_PREFIX }}|$(INSTALL_PREFIX)|g' $@; rm $@.bak
sed -i.bak -e 's|$$INSTALL_PREFIX|$(INSTALL_PREFIX)|g' $@; rm $@.bak
# Install destination script files.
$(DEST_DIR_SCRIPT)/%: $(BUILD_DIR_SCRIPT)/%

132
README.md
View File

@@ -1,10 +1,10 @@
# Automatic Restic Backups Using Native OS Task Schedulers
# Automatic restic Backups using Native OS Task Schedulers
*formerly named restic-systemd-automatic-backup*
[![GitHub Stars](https://img.shields.io/github/stars/erikw/restic-automatic-backup-scheduler?style=social)](#)
[![GitHub Forks](https://img.shields.io/github/forks/erikw/restic-automatic-backup-scheduler?style=social)](#)
<br>
[![Lint](https://github.com/erikw/restic-automatic-backup-scheduler/actions/workflows/linter.yml/badge.svg)](https://github.com/erikw/restic-automatic-backup-scheduler/actions/workflows/linter.yml)
[![Lint Code Base](https://github.com/erikw/restic-automatic-backup-scheduler/actions/workflows/linter.yml/badge.svg)](https://github.com/erikw/restic-automatic-backup-scheduler/actions/workflows/linter.yml)
[![Latest tag](https://img.shields.io/github/v/tag/erikw/restic-automatic-backup-scheduler)](https://github.com/erikw/restic-automatic-backup-scheduler/tags)
[![AUR version](https://img.shields.io/aur/version/restic-automatic-backup-scheduler)](https://aur.archlinux.org/packages/restic-automatic-backup-scheduler/)
[![AUR maintainer](https://img.shields.io/aur/maintainer/restic-automatic-backup-scheduler?label=AUR%20maintainer)](https://aur.archlinux.org/packages/restic-automatic-backup-scheduler/)
@@ -14,8 +14,7 @@
[![Closed PRs](https://img.shields.io/github/issues-pr-closed/erikw/restic-automatic-backup-scheduler?color=success)](https://github.com/erikw/restic-automatic-backup-scheduler/pulls?q=is%3Apr+is%3Aclosed)
[![License](https://img.shields.io/badge/license-BSD--3-blue)](LICENSE)
[![OSS Lifecycle](https://img.shields.io/osslifecycle/erikw/restic-automatic-backup-scheduler)](https://github.com/Netflix/osstracker)
[![SLOC](https://sloc.xyz/github/erikw/restic-automatic-backup-scheduler?lower=true)](#)
[![Top programming languages used](https://img.shields.io/github/languages/top/erikw/restic-automatic-backup-scheduler)](#)
[![SLOC](https://img.shields.io/tokei/lines/github/erikw/restic-automatic-backup-scheduler?logo=codefactor&logoColor=lightgrey)](#)
<br>
[![Contributors](https://img.shields.io/github/contributors/erikw/restic-automatic-backup-scheduler)](https://github.com/erikw/restic-automatic-backup-scheduler/graphs/contributors) including these top contributors:
@@ -23,37 +22,24 @@
<img src = "https://contrib.rocks/image?repo=erikw/restic-automatic-backup-scheduler&max=24"/>
</a>
<p align="center">
<!-- Ref: https://dev.to/azure/adding-a-github-codespace-button-to-your-readme-5f6l -->
<a href="https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=129436975" title="Open in GitHub Codespaces" ><img alt="Open in GitHub Codespaces" src="https://github.com/codespaces/badge.svg"></a>
</p>
# Intro
[restic](https://restic.net/) is a command-line tool for making backups, the right way. Check the official website for a feature explanation. As a storage backend, I recommend [Backblaze B2](https://www.backblaze.com/b2/cloud-storage.html) as restic works well with it, and it is (at the time of writing) very affordable for the hobbyist hacker! (anecdotal: I pay for my full-systems backups each month typically < 1 USD).
Unfortunately restic does not come pre-configured with a way to run automated backups, say every day. However, it's possible to set this up yourself using built-in tools in your OS and some wrappers. For Linux with systemd, it's convenient to use systemd timers. For macOS systems, we can use built-in LaunchAgents. For Windows we can use ScheduledTasks. Any OS having something cron-like will also work!
Unfortunately restic does not come pre-configured with a way to run automated backups, say every day. However it's possible to set this up yourself using built-in tools in your OS and some wrappers. For Linux with systemd, it's convenient to use systemd timers. For macOS systems, we can use built-in LaunchAgents. For Windows we can use ScheduledTasks. Any OS having something cron-like will also work!
Here follows a step-by step tutorial on how to set it up, with my sample script and configurations that you can modify to suit your needs.
Note, you can use any restic's supported [storage backends](https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html). The setup should be similar, but you will have to use other configuration variables to match your backend of choice.
Note, you can use any restic's supported [storage backends](https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html). The setup should be similar but you will have to use other configuration variables to match your backend of choice.
## Project Scope
> [!NOTE]
> **Update:** this project is feature complete (see reasoning below). Only bug fixes will be accepted. Feel free to fork if you want to add more features; being a forking base was the initial scope of this project!
The scope for this is not to be a full-fledged super solution that solves all the problems and all possible setups. The aim is to be a hackable code base for you to start sewing up the perfect backup solution that fits your requirements!
Nevertheless, the project should work out of the box, be minimal but still open the doors for configuration and extensions by users.
To use a different storage backend than B2, you should only need to tweak a few settings variables in the backup profile as well as some restic arguments inside `restic_backup.sh`.
Nevertheless the project should work out of the box, be minimal but still open the doors for configuration and extensions by users.
## Notes
> [!TIP]
> Navigate this document easily from the Section icon in the top left corner.
<img src="img/readme_sections.png" alt="README.md sections" width="30%" />
> [!NOTE]
> In the command listing in this document, `$` means a user shell and `#` means a root shell (or use `sudo`).
* Tip: Navigate this document easily from the Section icon in the top left corner.
![README Sections](img/readme_sections.png)
***Note**: in the command listing in this document, `$` means a user shell and `#` means a root shell (or use `sudo`).
# Requirements
@@ -74,8 +60,7 @@ Depending on your system, the setup will look different. Choose one of:
## Setup Linux Systemd
<img height="64" width="64" src="https://unpkg.com/simple-icons@v6/icons/linux.svg" />
> [!NOTE]
> The Linux setup here will assume an installation to `/`.
**Note** The Linux setup here will assume an installation to `/`.
Many Linux distributions nowadays use [Systemd](https://en.wikipedia.org/wiki/Systemd), which features good support for running services and scheduled jobs. If your distribution is no on Systemd, check out the [cron setup](#setup-cron) instead.
@@ -125,8 +110,7 @@ Many Linux distributions nowadays use [Systemd](https://en.wikipedia.org/wiki/Sy
## Setup macOS LaunchAgent
<img height="64" width="64" src="https://unpkg.com/simple-icons@v6/icons/apple.svg" />
> [!NOTE]
> The macOS setup here will assume a Homebrew installation to the [recommended default location](https://docs.brew.sh/FAQ#why-should-i-install-homebrew-in-the-default-location). This is [`$HOMEBREW_PREFIX` (`brew --prefix`)](https://docs.brew.sh/Formula-Cookbook#variables-for-directory-locations) , which is `/usr/local` on Intel Macs and `/opt/homebrew` on [Apple Silicon](https://docs.brew.sh/FAQ#why-is-the-default-installation-prefix-opthomebrew-on-apple-silicon).
☝ **Note** The macOS setup here will assume a Homebrew installation to the [recommended default location](https://docs.brew.sh/FAQ#why-should-i-install-homebrew-in-the-default-location). This is [`$HOMEBREW_PREFIX` (`brew --prefix`)](https://docs.brew.sh/Formula-Cookbook#variables-for-directory-locations) , which is `/usr/local` on Intel Macs and `/opt/homebrew` on [Apple Silicon](https://docs.brew.sh/FAQ#why-is-the-default-installation-prefix-opthomebrew-on-apple-silicon).
[Launchd](https://www.launchd.info/) is the modern built-in service scheduler in macOS. It has support for running services as root (Daemon) or as a normal user (Agent). Here we set up a LaunchAgent to be run as your normal user for starting regular backups.
@@ -150,7 +134,6 @@ Many Linux distributions nowadays use [Systemd](https://en.wikipedia.org/wiki/Sy
```
1. Configure [how often](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html#//apple_ref/doc/uid/10000172i-CH1-SW1) backups should be done. If needed, edit `OnCalendar` in
* Homebrew install: `~/Library/LaunchAgents/homebrew.mxcl.restic-automatic-backup-scheduler.plist`.
* Note that with Homebrew install, this file will only be available after running the `$ brew services start [...]` command in the next step. Run that command and come back here.
* `make` install: `~/Library/LaunchAgents/com.github.erikw.restic-backup.plist`.
1. Enable automated backup for starting with the system & make the first backup:
* Homebrew install:
@@ -221,9 +204,9 @@ I describe here one of may ways you can get restic and this backup script workin
**TL;DR setup**
1. Install [scoop](https://scoop.sh/)
1. Install dependencies from a PowerShell with *administrator privileges*. `pwsh` should be installed to be able to run powershell in shebang scripts.
1. Install dependencies from a PowerShell with *administrator privileges*:
```console
powershell> scoop install restic make git pwsh
powershell> scoop install restic make git
```
1. In a *non-privileged* PowerShell, start git-bash and clone this repo
```console
@@ -273,8 +256,7 @@ With `taskschd.msc` you can easily start, stop, delete and configure the schedul
## Setup Cron
<img height="64" width="64" src="https://unpkg.com/simple-icons@v6/icons/clockify.svg" />
> [!NOTE]
> There are many different cron [implementations](https://wiki.archlinux.org/title/Cron) out there and they all work slightly different.
☝ **Note** There are many different cron [implementations](https://wiki.archlinux.org/title/Cron) out there and they all work slightly different.
Any system that has a cron-like system can easily setup restic backups as well. However if you system supports any of the previous setups, those are recommended over cron as they provide more features and reliability for your backups.
@@ -321,17 +303,17 @@ $ git clone https://github.com/erikw/restic-automatic-backup-scheduler.git && cd
Make a quick search-and-replace in the source files:
```console
$ find bin etc usr Library ScheduledTask -type f -exec sed -i.bak -e 's|{{ INSTALL_PREFIX }}||g' {} \; -exec rm {}.bak \;
$ find etc bin -type f -exec sed -i.bak -e 's|$INSTALL_PREFIX||g' {} \; -exec rm {}.bak \;
```
and you should now see that all files have been changed like e.g.
```diff
-export RESTIC_PASSWORD_FILE="{{ INSTALL_PREFIX }}/etc/restic/pw.txt"
-export RESTIC_PASSWORD_FILE="$INSTALL_PREFIX/etc/restic/pw.txt"
+export RESTIC_PASSWORD_FILE="/etc/restic/pw.txt"
```
Why? The OS specific TL;DR setups above all use the [Makefile](Makefile) or a package manager to install these files. The placeholder string `{{ INSTALL_PREFIX }}` is in the source files for portability reasons, so that the Makefile can support all different operating systems. `make` users can set a different `$PREFIX` when installing like `PREFIX=/usr/local make install-systemd`.
Why? The OS specific TL;DR setups above all use the [Makefile](Makefile) or a package manager to install these files. The placeholder string `$INSTALL_PREFIX` is in the source files for portability reasons, so that the Makefile can support all different operating systems. `make` users can set a different `$PREFIX` when installing like `PREFIX=/usr/local make install-systemd`.
In this detailed manual setup we will copy all files manually to `/etc`and `/bin`. Thus, we need to remove the placeholder string `{{ INSTALL_PREFIX }}` in the source files as a first step.
In this detailed manual setup we will copy all files manually to `/etc`and `/bin`. Thus we need to remove the placeholder string `$INSTALL_PREFIX` in the source files as a first step.
#### 1. Create Backblaze B2 Account, Bucket and Keys
@@ -457,18 +439,12 @@ To create a different backup and use you can do:
# restic_backup.sh
```
### Optional: Summary stats log
When enabled, it will write to a CSV log file the stats after each backup. Can be enabled by uncommenting its env variable (`RESTIC_BACKUP_STATS_DIR`) on the global environment file or defining it on a specific profile.
The stats log (as well as) the desktop notifications incur in an additional run of `restic snapshots` and `restic diff`. This execution is shared with the notifications (no extra run).
### Optional: Desktop Notifications
<img src="img/macos_notification.png" align="right" />
It's a good idea to be on top of your backups to make sure that they don't increase a lot in size and incur high costs. However, it's notoriously tricky to make GUI notifications correctly from a non-user process (e.g. root).
It's a good idea to be on top of your backups to make sure that they don't increase a lot in size and incur high costs. However it's notoriously tricky to make GUI notifications correctly from a non-user process (e.g. root).
Therefore, this project provides a lightweight solution for desktop notifications that works like this: Basically `restic_backup.sh` will append a summary line of the last backup to a user-owned file (the user running your OS's desktop environment) in a fire-and-forget fashion. Then the user has a process that reads this and forward each line as a new message to the desktop environment in use.
Therefore this project provides a lightweight solution for desktop notifications that works like this: Basically `restic_backup.sh` will append a summary line of the last backup to a user-owned file (the user running your OS's desktop environment) in a fire-and-forget fashion. Then the user has a process that reads this and forward each line as a new message to the desktop environment in use.
To set desktop notifications up:
1. Create a special FIFO file as your desktop user:
@@ -477,11 +453,12 @@ To set desktop notifications up:
```
1. In your profile, e.g. `/etc/restic/default.sh`, set:
```bash
RESTIC_NOTIFY_BACKUP_STATS=true
RESTIC_BACKUP_NOTIFICATION_FILE=/home/user/.cache/notification-queue
```
1. Create a listener on the notification queue file that forwards to desktop notifications
* Linux auto start + cross-platform notifier / notify-send
* [notification-queue-notifier](https://github.com/gerardbosch/dotfiles/blob/2130d54daa827e7f885abac0d4f10b6f67d28ad3/home/bin/notification-queue-notifier)
* [notification-queue-notifier](https://github.com/gerardbosch/dotfiles/blob/ddc1491056822eab45dedd131f1946308ef62135/home/bin/notification-queue-notifier)
* [notification-queue.desktop](https://github.com/gerardbosch/dotfiles-linux/blob/ea0f75bfd7a356945544ecaa42a2fc35c9fab3a1/home/.config/autostart/notification-queue.desktop)
* macOS auto start + [terminal-notifier](https://github.com/julienXX/terminal-notifier)
* [notification-queue-notifier.sh](https://github.com/erikw/dotfiles/blob/8a942defe268292200b614951cdf433ddccf7170/bin/notification-queue-notifier.sh)
@@ -496,10 +473,10 @@ We want to be aware when the automatic backup fails, so we can fix it. Since my
Put this file in `/bin`:
* `systemd-email`: Sends email using sendmail(1). This script also features time-out for not spamming Gmail servers and getting my account blocked.
Put this file in `/etc/systemd/system/`:
* `status-email-user@.service`: A service that can notify you via email when a systemd service fails. Edit the target email address in this file, and replace or remove `{{ INSTALL_PREFIX }}` according to your installation.
Put this files in `/etc/systemd/system/`:
* `status-email-user@.service`: A service that can notify you via email when a systemd service fails. Edit the target email address in this file.
Now edit `/usr/lib/systemd/system/restic-backup@.service` and `/usr/lib/systemd/system/restic-check@.service` to call this service failure.
Now edit `restic-backup@.service` and `status-email-user@.service` to call this service failure.
```
OnFailure=status-email-user@%n.service
```
@@ -519,7 +496,6 @@ For a laptop, it can make sense to not do heavy backups when your on a metered c
1. Edit `restic-backup@.service` and `restic-check@.service` to require the new service to be in success state:
```
Requires=nm-unmetered-connection.service
After=nm-unmetered-connection.service
```
1. Copy and paste the command below, it will install the following files and refresh systemd daemon:
1. Put this file in `/etc/systemd/system/`:
@@ -530,10 +506,8 @@ For a laptop, it can make sense to not do heavy backups when your on a metered c
```console
# systemctl daemon-reload
```
> [!TIP]
> All steps but the first can be done in one go if you use the Makefile. Set `$PREFIX` as needed or leave empty for install to `/`.
☝ **Tip**: All steps but the first can be done in one go if you use the Makefile. Set `$PREFIX` as needed or leave empty for install to `/`.
```bash
sudo bash -c 'export PREFIX=
make build/usr/lib/systemd/system/nm-unmetered-connection.service
@@ -549,16 +523,14 @@ straightforward (it needs to run with sudo to read environment). Just run:
* `sudo resticw WHATEVER` (e.g. `sudo resticw snapshots`) to use the default profile.
* You can run the wrapper by passing a specific profile: `resticw -p anotherprofile snapshots`.
* The wrapper has extras on top of `restic` like `--diff-latest` option.
Useful commands:
| Command | Description |
|---------------------------------------------------|---------------------------------------------------------------------------------------|
| `resticw snapshots` | List backup snapshots |
| `resticw diff <snapshotId-1> <snapshotId-2>` | Show the changes between backup snapshots |
| `resticw stats` / `resticw stats snapshotId ...` | Show the statistics for the whole repo or the specified snapshots |
| `resticw mount /mnt/restic` | Mount your remote repository |
| `resticw --diff-latest` | Show latest snapshot changes: Runs `restic diff` after finding the latest 2 snapshots |
| Command | Description |
|---------------------------------------------------|-------------------------------------------------------------------|
| `resticw snapshots` | List backup snapshots |
| `resticw diff <snapshotId-1> <snapshotId-2>` | Show the changes between backup snapshots |
| `resticw stats` / `resticw stats snapshotId ...` | Show the statistics for the whole repo or the specified snapshots |
| `resticw mount /mnt/restic` | Mount your remote repository |
@@ -569,32 +541,6 @@ There is a make target to remove all files (scripts and **configs)** that were i
$ sudo make uninstall
```
# Debugging
The best way to debug what's going on is to run the `restic_backup.sh` script with bash's trace function. You can activate it by running the script with `bash -x`:
```consle
$ source /etc/restic/default.env.sh
$ bash -x /bin/restic_backup.sh
```
To debug smaller portions of the backup script, insert these lines at the top and bottom of the relevant code portions e.g.:
```bash
set -x
exec 2>/tmp/restic-automatic-backup-scheduler.log
<code to debug>
set +x
```
and then inspect the outputs like
```shell
$ less /tmp/restic-automatic-backup-scheduler.log
$ tail -f /tmp/restic-automatic-backup-scheduler.log # or follow output like this.
```
# Development
* To not mess up your real installation when changing the `Makefile` simply install to a `$PREFIX` like
```console
@@ -607,13 +553,13 @@ $ tail -f /tmp/restic-automatic-backup-scheduler.log # or follow output like thi
```
# Releasing
1. Create a new version of this project by using [semver-cli](https://github.com/maykonlsf/semver-cli).
```shell
vi CHANGELOG.md
semver up minor
ver=$(semver get release)
git commit -am "Bump version to $ver" && git tag $ver && git push --atomic origin main $ver
```
To make a new release:
1. Create a new tag:
```console
$ vi CHANGELOG.md && git commit -am "Update CHANGELOG.md"
$ git tag vX.Y.Z
$ git push && git push --tags
```
1. Update version in the AUR [PKGBUILD](https://aur.archlinux.org/packages/restic-automatic-backup-scheduler/)
1. Update version in the Homebrew Formulas (see the repo README):
* [restic-automatic-backup-scheduler](https://github.com/erikw/homebrew-tap/blob/main/Formula/restic-automatic-backup-scheduler.rb)

View File

@@ -9,11 +9,11 @@
# Install restic_backup.sh
$action = New-ScheduledTaskAction -Execute "$(scoop prefix git)\git-bash.exe" -Argument '-l -c "source {{ INSTALL_PREFIX }}/etc/restic/default.env.sh && {{ INSTALL_PREFIX }}/bin/restic_backup.sh"'
$action = New-ScheduledTaskAction -Execute "$(scoop prefix git)\git-bash.exe" -Argument '-l -c "source $INSTALL_PREFIX/etc/restic/default.env.sh && $INSTALL_PREFIX/bin/restic_backup.sh"'
$trigger = New-ScheduledTaskTrigger -Daily -At 7pm
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "restic_backup" -Description "Daily backup to B2 with restic."
# Install restic_check.sh
$action = New-ScheduledTaskAction -Execute "$(scoop prefix git)\git-bash.exe" -Argument '-l -c "source {{ INSTALL_PREFIX }}/etc/restic/default.env.sh && {{ INSTALL_PREFIX }}/bin/restic_check.sh"'
$action = New-ScheduledTaskAction -Execute "$(scoop prefix git)\git-bash.exe" -Argument '-l -c "source $INSTALL_PREFIX/etc/restic/default.env.sh && /INSTALL_PREFIX/bin/restic_check.sh"'
$trigger = New-ScheduledTaskTrigger -Weekly -WeeksInterval 4 -DaysOfWeek Sunday -At 8pm -RandomDelay 128
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "restic_check" -Description "Check B2 backups with restic."

View File

@@ -5,7 +5,7 @@
# Why? Because of FreeBSD the system cron uses sendmail, and I want to use ssmtp.
# Make your crontab files like:
#SHELL=/bin/sh
#PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:{{ INSTALL_PREFIX }}/bin
#PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:$INSTALL_PREFIX/bin
#@daily root cron_mail freebsd-update cron
mail_target=root

114
bin/restic_backup.sh Executable file → Normal file
View File

@@ -8,9 +8,8 @@
# $ source $PREFIX/etc/default.env.sh
# $ restic_backup.sh
set -o errexit
set -o pipefail
[[ "${TRACE-0}" =~ ^1|t|y|true|yes$ ]] && set -o xtrace
# Exit on error, unset var, pipe failure
set -euo pipefail
# Clean up lock if we are killed.
# If killed by systemd, like $(systemctl stop restic), then it kills the whole cgroup and all it's subprocesses.
@@ -29,76 +28,22 @@ assert_envvars() {
local varnames=("$@")
for varname in "${varnames[@]}"; do
if [ -z ${!varname+x} ]; then
printf "%s must be set for this script to work.\n\nDid you forget to source a {{ INSTALL_PREFIX }}/etc/restic/*.env.sh profile in the current shell before executing this script?\n" "$varname" >&2
printf "%s must be set for this script to work.\n\nDid you forget to source a $INSTALL_PREFIX/etc/restic/*.env.sh profile in the current shell before executing this script?\n" "$varname" >&2
exit 1
fi
done
}
warn_on_missing_envvars() {
local unset_envs=()
local varnames=("$@")
for varname in "${varnames[@]}"; do
if [ -z "${!varname-}" ]; then
unset_envs=("${unset_envs[@]}" "$varname")
fi
done
if [ ${#unset_envs[@]} -gt 0 ]; then
printf "The following env variables are recommended, but have not been set. This script may not work as expected: %s\n" "${unset_envs[*]}" >&2
fi
}
# Log the backup summary stats to a CSV file
logBackupStatsCsv() {
local snapId="$1" added="$2" removed="$3" snapSize="$4"
local logFile
logFile="${RESTIC_BACKUP_STATS_DIR}/$(date '+%Y')-stats.log.csv"
test -e "$logFile" || install -D -m 0644 <(echo "Date, Snapshot ID, Added, Removed, Snapshot size") "$logFile"
# DEV-NOTE: using `ex` due `sed` inconsistencies (GNU vs. BSD) and `awk` cannot edit in-place. `ex` does a good job
printf '1a\n%s\n.\nwq\n' "$(date '+%F %H:%M:%S'), ${snapId}, ${added}, ${removed}, ${snapSize}" | ex "$logFile"
}
# Notify the backup summary stats to the user
notifyBackupStats() {
local statsMsg="$1"
if [ -w "$RESTIC_BACKUP_NOTIFICATION_FILE" ]; then
echo "$statsMsg" >> "$RESTIC_BACKUP_NOTIFICATION_FILE"
else
echo "[WARN] Couldn't write to the backup notification file. File not found or not writable: ${RESTIC_BACKUP_NOTIFICATION_FILE}"
fi
}
# ------------
# === Main ===
# ------------
assert_envvars \
RESTIC_BACKUP_PATHS RESTIC_BACKUP_TAG \
RESTIC_BACKUP_EXCLUDE_FILE RESTIC_BACKUP_EXTRA_ARGS RESTIC_REPOSITORY RESTIC_VERBOSITY_LEVEL \
RESTIC_RETENTION_HOURS RESTIC_RETENTION_DAYS RESTIC_RETENTION_MONTHS RESTIC_RETENTION_WEEKS RESTIC_RETENTION_YEARS
warn_on_missing_envvars \
B2_ACCOUNT_ID B2_ACCOUNT_KEY B2_CONNECTIONS \
RESTIC_PASSWORD_FILE
RESTIC_BACKUP_PATHS RESTIC_BACKUP_TAG \
RESTIC_BACKUP_EXCLUDE_FILE RESTIC_BACKUP_EXTRA_ARGS RESTIC_PASSWORD_FILE RESTIC_REPOSITORY RESTIC_VERBOSITY_LEVEL \
RESTIC_RETENTION_DAYS RESTIC_RETENTION_MONTHS RESTIC_RETENTION_WEEKS RESTIC_RETENTION_YEARS
# Convert to arrays, as arrays should be used to build command lines. See https://github.com/koalaman/shellcheck/wiki/SC2086
IFS=':' read -ra backup_paths <<< "$RESTIC_BACKUP_PATHS"
IFS=' ' read -ra extra_args <<< "$RESTIC_BACKUP_EXTRA_ARGS"
# Convert to array, an preserve spaces. See #111
backup_extra_args=( )
if [ -n "$RESTIC_BACKUP_EXTRA_ARGS" ]; then
while IFS= read -r -d ''; do
backup_extra_args+=( "$REPLY" )
done < <(xargs printf '%s\0' <<<"$RESTIC_BACKUP_EXTRA_ARGS")
fi
B2_ARG=
[ -z "${B2_CONNECTIONS+x}" ] || B2_ARG=(--option b2.connections="$B2_CONNECTIONS")
# If you need to run some commands before performing the backup; create this file, put them there and make the file executable.
PRE_SCRIPT="{{ INSTALL_PREFIX }}/etc/restic/pre_backup.sh"
test -x "$PRE_SCRIPT" && "$PRE_SCRIPT"
# Set up exclude files: global + path-specific ones
# NOTE that restic will fail the backup if not all listed --exclude-files exist. Thus we should only list them if they are really all available.
@@ -133,9 +78,9 @@ restic backup \
--verbose="$RESTIC_VERBOSITY_LEVEL" \
$FS_ARG \
--tag "$RESTIC_BACKUP_TAG" \
"${B2_ARG[@]}" \
--option b2.connections="$B2_CONNECTIONS" \
"${exclusion_args[@]}" \
"${backup_extra_args[@]}" \
"${extra_args[@]}" \
"${backup_paths[@]}" &
wait $!
@@ -145,10 +90,10 @@ wait $!
restic forget \
--verbose="$RESTIC_VERBOSITY_LEVEL" \
--tag "$RESTIC_BACKUP_TAG" \
"${B2_ARG[@]}" \
--option b2.connections="$B2_CONNECTIONS" \
--prune \
--group-by "paths,tags" \
--keep-hourly "$RESTIC_RETENTION_HOURS" \
--keep-hourly "$RESTIC_RETENTION_HOURS" \
--keep-daily "$RESTIC_RETENTION_DAYS" \
--keep-weekly "$RESTIC_RETENTION_WEEKS" \
--keep-monthly "$RESTIC_RETENTION_MONTHS" \
@@ -162,22 +107,23 @@ wait $!
echo "Backup & cleaning is done."
# (optional) Compute backup summary stats
if [[ -n "$RESTIC_BACKUP_STATS_DIR" || -n "$RESTIC_BACKUP_NOTIFICATION_FILE" ]]; then
echo 'Silently computing backup summary stats...'
latest_snapshots=$(restic snapshots --tag "$RESTIC_BACKUP_TAG" --latest 2 --compact \
| grep -Ei "^[abcdef0-9]{8} " \
| awk '{print $1}' \
| tail -2 \
| tr '\n' ' ')
latest_snapshot_diff=$(echo "$latest_snapshots" | xargs restic diff)
added=$(echo "$latest_snapshot_diff" | grep -i 'added:' | awk '{print $2 " " $3}')
removed=$(echo "$latest_snapshot_diff" | grep -i 'removed:' | awk '{print $2 " " $3}')
snapshot_size=$(restic stats latest --tag "$RESTIC_BACKUP_TAG" | grep -i 'total size:' | cut -d ':' -f2 | xargs) # xargs acts as trim
snapshotId=$(echo "$latest_snapshots" | cut -d ' ' -f2)
statsMsg="Added: ${added}. Removed: ${removed}. Snap size: ${snapshot_size}"
# (optionally) Notify about backup summary stats.
if [ "$RESTIC_NOTIFY_BACKUP_STATS" = true ]; then
if [ -w "$RESTIC_BACKUP_NOTIFICATION_FILE" ]; then
echo 'Notifications are enabled: Silently computing backup summary stats...'
echo "$statsMsg"
test -n "$RESTIC_BACKUP_STATS_DIR" && logBackupStatsCsv "$snapshotId" "$added" "$removed" "$snapshot_size"
test -n "$RESTIC_BACKUP_NOTIFICATION_FILE" && notifyBackupStats "$statsMsg"
snapshot_size=$(restic stats latest --tag "$RESTIC_BACKUP_TAG" | grep -i 'total size:' | cut -d ':' -f2 | xargs) # xargs acts as trim
latest_snapshot_diff=$(restic snapshots --tag "$RESTIC_BACKUP_TAG" --latest 2 --compact \
| grep -Ei "^[abcdef0-9]{8} " \
| awk '{print $1}' \
| tail -2 \
| tr '\n' ' ' \
| xargs restic diff)
added=$(echo "$latest_snapshot_diff" | grep -i 'added:' | awk '{print $2 " " $3}')
removed=$(echo "$latest_snapshot_diff" | grep -i 'removed:' | awk '{print $2 " " $3}')
echo "Added: ${added}. Removed: ${removed}. Snap size: ${snapshot_size}" >> "$RESTIC_BACKUP_NOTIFICATION_FILE"
else
echo "[WARN] Couldn't write the backup summary stats. File not found or not writable: ${RESTIC_BACKUP_NOTIFICATION_FILE}"
fi
fi

32
bin/restic_check.sh Executable file → Normal file
View File

@@ -2,9 +2,8 @@
# Check the backups made with restic to Backblaze B2 for errors.
# See restic_backup.sh on how this script is run (as it's analogous for this script).
set -o errexit
set -o pipefail
[[ "${TRACE-0}" =~ ^1|t|y|true|yes$ ]] && set -o xtrace
# Exit on error, unset var, pipe failure
set -euo pipefail
# Clean up lock if we are killed.
# If killed by systemd, like $(systemctl stop restic), then it kills the whole cgroup and all it's subprocesses.
@@ -21,34 +20,15 @@ assert_envvars() {
local varnames=("$@")
for varname in "${varnames[@]}"; do
if [ -z ${!varname+x} ]; then
printf "%s must be set for this script to work.\n\nDid you forget to source a {{ INSTALL_PREFIX }}/etc/restic/*.env.sh profile in the current shell before executing this script?\n" "$varname" >&2
printf "%s must be set for this script to work.\n\nDid you forget to source a $INSTALL_PREFIX/etc/restic/*.env.sh profile in the current shell before executing this script?\n" "$varname" >&2
exit 1
fi
done
}
warn_on_missing_envvars() {
local unset_envs=()
local varnames=("$@")
for varname in "${varnames[@]}"; do
if [ -z "${!varname}" ]; then
unset_envs=("${unset_envs[@]}" "$varname")
fi
done
if [ ${#unset_envs[@]} -gt 0 ]; then
printf "The following env variables are recommended, but have not been set. This script may not work as expected: %s\n" "${unset_envs[*]}" >&2
fi
}
assert_envvars\
assert_envvars \
B2_ACCOUNT_ID B2_ACCOUNT_KEY B2_CONNECTIONS \
RESTIC_PASSWORD_FILE RESTIC_REPOSITORY RESTIC_VERBOSITY_LEVEL
warn_on_missing_envvars \
B2_ACCOUNT_ID B2_ACCOUNT_KEY B2_CONNECTIONS
B2_ARG=
[ -z "${B2_CONNECTIONS+x}" ] || B2_ARG=(--option b2.connections="$B2_CONNECTIONS")
# Remove locks from other stale processes to keep the automated backup running.
# NOTE nope, don't unlock like restic_backup.sh. restic_backup.sh should take precedence over this script.
@@ -57,6 +37,6 @@ B2_ARG=
# Check repository for errors.
restic check \
"${B2_ARG[@]}" \
--option b2.connections="$B2_CONNECTIONS" \
--verbose="$RESTIC_VERBOSITY_LEVEL" &
wait $!

View File

@@ -1,24 +1,18 @@
#!/usr/bin/env bash
# @generated
DOC="A little wrapper over restic just to handle profiles and environment loading, with small extensions.
DOC="A little wrapper over restic just to handle profiles and environment loading.
Usage:
resticw [--profile <name>] <restic_arguments_line>...
resticw [--profile <name>] --diff-latest
resticw [options] <restic_arguments_line>...
The <restic_arguments_line> is just the regular unwrapped restic command arguments, e.g. \`stats latest\`.
The <restic_arguments_line> is just the regular unwrapped restic command arguments, e.g. stats latest
Options:
-p --profile=<name> Specify the profile to load or use default [default: default].
--diff-latest Show latest snapshot changes: Runs \`restic diff\` after finding the latest 2 snapshots.
Examples:
resticw --profile profileA snapshots
resticw stats latest # this will use the profile: default
resticw -p profileB --diff-latest
💡 You may need to run it with sudo to source the profile environment.
"
# The following argument parser is generated with docopt.sh from the above docstring.
@@ -91,51 +85,35 @@ local node_idx; ((testdepth++)) || true; for node_idx in "$@"; do
if ! "node_$node_idx"; then left=("${initial_left[@]}"); ((testdepth--)) || true
return 1; fi; done; if [[ $((--testdepth)) -eq 0 ]]; then
left=("${initial_left[@]}"); for node_idx in "$@"; do "node_$node_idx"; done; fi
return 0; }; either() { local initial_left=("${left[@]}"); local best_match_idx
local match_count; local node_idx; ((testdepth++)) || true
for node_idx in "$@"; do if "node_$node_idx"; then
if [[ -z $match_count || ${#left[@]} -lt $match_count ]]; then
best_match_idx=$node_idx; match_count=${#left[@]}; fi; fi
left=("${initial_left[@]}"); done; ((testdepth--)) || true
if [[ -n $best_match_idx ]]; then "node_$best_match_idx"; return 0; fi
left=("${initial_left[@]}"); return 1; }; optional() { local node_idx
for node_idx in "$@"; do "node_$node_idx"; done; return 0; }; oneormore() {
local i=0; local prev=${#left[@]}; while "node_$1"; do ((i++)) || true
return 0; }; optional() { local node_idx; for node_idx in "$@"; do
"node_$node_idx"; done; return 0; }; oneormore() { local i=0
local prev=${#left[@]}; while "node_$1"; do ((i++)) || true
[[ $prev -eq ${#left[@]} ]] && break; prev=${#left[@]}; done
if [[ $i -ge 1 ]]; then return 0; fi; return 1; }; switch() { local i
if [[ $i -ge 1 ]]; then return 0; fi; return 1; }; value() { local i
for i in "${!left[@]}"; do local l=${left[$i]}
if [[ ${parsed_params[$l]} = "$2" ]]; then
left=("${left[@]:0:$i}" "${left[@]:((i+1))}")
[[ $testdepth -gt 0 ]] && return 0; if [[ $3 = true ]]; then
eval "((var_$1++))" || true; else eval "var_$1=true"; fi; return 0; fi; done
return 1; }; value() { local i; for i in "${!left[@]}"; do local l=${left[$i]}
if [[ ${parsed_params[$l]} = "$2" ]]; then
left=("${left[@]:0:$i}" "${left[@]:((i+1))}")
[[ $testdepth -gt 0 ]] && return 0; local value
value=$(printf -- "%q" "${parsed_values[$l]}"); if [[ $3 = true ]]; then
eval "var_$1+=($value)"; else eval "var_$1=$value"; fi; return 0; fi; done
return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() {
printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() {
[[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() {
printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:0:751}
usage=${DOC:102:105}; digest=a9466; shorts=(-p '')
longs=(--profile --diff-latest); argcounts=(1 0); node_0(){ value __profile 0; }
node_1(){ switch __diff_latest 1; }; node_2(){
value _restic_arguments_line_ a true; }; node_3(){ optional 0; }; node_4(){
oneormore 2; }; node_5(){ required 3 4; }; node_6(){ required 3 1; }; node_7(){
either 5 6; }; node_8(){ required 7; }; cat <<<' docopt_exit() {
[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:102:105}" >&2
exit 1; }'; unset var___profile var___diff_latest var__restic_arguments_line_
parse 8 "$@"; local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__profile" \
"${prefix}__diff_latest" "${prefix}_restic_arguments_line_"
printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:0:450}
usage=${DOC:79:53}; digest=dc31d; shorts=(-p); longs=(--profile); argcounts=(1)
node_0(){ value __profile 0; }; node_1(){ value _restic_arguments_line_ a true
}; node_2(){ optional 0; }; node_3(){ optional 2; }; node_4(){ oneormore 1; }
node_5(){ required 3 4; }; node_6(){ required 5; }; cat <<<' docopt_exit() {
[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:79:53}" >&2; exit 1
}'; unset var___profile var__restic_arguments_line_; parse 6 "$@"
local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__profile" \
"${prefix}_restic_arguments_line_"
eval "${prefix}"'__profile=${var___profile:-default}'
eval "${prefix}"'__diff_latest=${var___diff_latest:-false}'
if declare -p var__restic_arguments_line_ >/dev/null 2>&1; then
eval "${prefix}"'_restic_arguments_line_=("${var__restic_arguments_line_[@]}")'
else eval "${prefix}"'_restic_arguments_line_=()'; fi; local docopt_i=1
[[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do
declare -p "${prefix}__profile" "${prefix}__diff_latest" \
"${prefix}_restic_arguments_line_"; done; }
declare -p "${prefix}__profile" "${prefix}_restic_arguments_line_"; done; }
# docopt parser above, complete command for generating this parser is `docopt.sh resticw`
# Parse arguments - See https://github.com/andsens/docopt.sh for the magic :)
@@ -148,21 +126,11 @@ eval "$(docopt "$@")"
# Exit on error, unbound variable, pipe error
set -euo pipefail
ENV_DIR="{{ INSTALL_PREFIX }}/etc/restic"
ENV_DIR=$INSTALL_PREFIX/etc/restic
ERR_NO_SUCH_PROFILE=2
ERR_PROFILE_NO_READ_PERM=3
# Compute the latest 2 snapshots and run the diff
latestSnapshotDiff() {
restic snapshots --tag "$RESTIC_BACKUP_TAG" --latest 2 --compact \
| grep -Ei "^[abcdef0-9]{8} " \
| awk '{print $1}' \
| tail -2 \
| tr '\n' ' ' \
| xargs restic diff
}
# shellcheck disable=SC2154
profile_file="${ENV_DIR}/${__profile}.env.sh"
@@ -174,12 +142,6 @@ if [[ ! -r "$profile_file" ]]; then
fi
echo -e "‣ Using profile: ${__profile} -- (${profile_file})\n"
# shellcheck disable=SC1090
source "$profile_file"
# shellcheck disable=SC2154
if [[ "${__diff_latest}" == true ]]; then
latestSnapshotDiff
else
restic "${_restic_arguments_line_[@]}"
fi
# shellcheck disable=SC2154,SC1090
source "$profile_file" && restic "${_restic_arguments_line_[@]}"

View File

@@ -1,13 +1,13 @@
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin/:{{ INSTALL_PREFIX }}/bin/
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin/:$INSTALL_PREFIX/bin/
# Order of crontab fields
# minute hour mday month wday command
# Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html
# Reference: crontab(5).
@midnight root . {{ INSTALL_PREFIX }}/etc/restic/default.env.sh && restic_backup.sh
@monthly root . {{ INSTALL_PREFIX }}/etc/restic/default.env.sh && restic_check.sh
@midnight root . $INSTALL_PREFIX/etc/restic/default.env.sh && restic_backup.sh
@monthly root . $INSTALL_PREFIX/etc/restic/default.env.sh && restic_check.sh
# Email notification version. Make sure bin/cron_mail is in the above $PATH
#@midnight root . {{ INSTALL_PREFIX }}/etc/restic/default.env.sh && cron_mail restic_backup.sh
#@monthly root . {{ INSTALL_PREFIX }}/etc/restic/default.env.sh && cron_mail restic_check.sh
#@midnight root . $INSTALL_PREFIX/etc/restic/default.env.sh && cron_mail restic_backup.sh
#@monthly root . $INSTALL_PREFIX/etc/restic/default.env.sh && cron_mail restic_check.sh

View File

@@ -1,6 +1,6 @@
# shellcheck shell=sh
# Global environment variables
# Global envionment variables
# These variables are sourced FIRST, and any values inside of *.env.sh files for
# specific configurations will override if also defined there.
@@ -10,9 +10,9 @@
# The restic repository encryption key
export RESTIC_PASSWORD_FILE="{{ INSTALL_PREFIX }}/etc/restic/pw.txt"
export RESTIC_PASSWORD_FILE="$INSTALL_PREFIX/etc/restic/pw.txt"
# The global restic exclude file
export RESTIC_BACKUP_EXCLUDE_FILE="{{ INSTALL_PREFIX }}/etc/restic/backup_exclude.txt"
export RESTIC_BACKUP_EXCLUDE_FILE="$INSTALL_PREFIX/etc/restic/backup_exclude.txt"
# Backblaze B2 credentials keyID & applicationKey pair.
# Restic environment variables are documented at https://restic.readthedocs.io/en/latest/040_backup.html#environment-variables
@@ -30,8 +30,6 @@ export RESTIC_BACKUP_EXTRA_ARGS=
# Override this value in a profile if needed.
export RESTIC_VERBOSITY_LEVEL=0
# (optional, uncomment to enable) Backup summary stats log: snapshot size, etc. (empty/unset won't log)
#export RESTIC_BACKUP_STATS_DIR="{{ INSTALL_PREFIX }}/var/log/restic-automatic-backup-scheduler"
# (optional) Desktop notifications. See README and restic_backup.sh for details on how to set this up (empty/unset means disabled)
# (optional) Desktop notifications. See restic_backup.sh for details on how to set this up.
export RESTIC_NOTIFY_BACKUP_STATS=false
export RESTIC_BACKUP_NOTIFICATION_FILE=

View File

@@ -12,7 +12,7 @@
# $ restic --repo ... --password-file ...
# shellcheck source=etc/restic/_global.env.sh
. "{{ INSTALL_PREFIX }}/etc/restic/_global.env.sh"
. "$INSTALL_PREFIX/etc/restic/_global.env.sh"
# Envvars below will override those in _global.env.sh if present.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
# Devcontainer postCreateCommand.
# Install dependencies for running this project in GitHub Codespaces.
set -eux
# For git version tagging:
go install github.com/maykonlsf/semver-cli/cmd/semver@latest

View File

@@ -3,4 +3,4 @@ Description=Check if the current NetworkManager connection is metered
[Service]
Type=oneshot
ExecStart={{ INSTALL_PREFIX }}/bin/nm-unmetered-connection.sh
ExecStart=$INSTALL_PREFIX/bin/nm-unmetered-connection.sh

View File

@@ -4,7 +4,6 @@ Description=Backup with restic to Backblaze B2
#OnFailure=status-email-user@%n.service
# Prevent backup on unmetered connection. Needs special setup. See README.md.
#Requires=nm-unmetered-connection.service
#After=nm-unmetered-connection.service
[Service]
Type=simple
@@ -14,4 +13,4 @@ Environment="HOME=/root"
# pipefail: so that redirecting stderr from the script to systemd-cat does not hide the failed command from OnFailure above.
# Random sleep (in seconds): in the case of multiple backup profiles. Many restic instances started at the same time could case high load or network bandwith usage.
# `systemd-cat` allows showing the restic output to the systemd journal
ExecStart=/bin/bash -c 'set -o pipefail; ps cax | grep -q restic && sleep $(shuf -i 0-300 -n 1); source {{ INSTALL_PREFIX }}/etc/restic/%I.env.sh && {{ INSTALL_PREFIX }}/bin/restic_backup.sh 2>&1 | systemd-cat'
ExecStart=/bin/bash -c 'set -o pipefail; ps cax | grep -q restic && sleep $(shuf -i 0-300 -n 1); source $INSTALL_PREFIX/etc/restic/%I.env.sh && $INSTALL_PREFIX/bin/restic_backup.sh 2>&1 | systemd-cat'

View File

@@ -5,11 +5,10 @@ Description=Check restic backup Backblaze B2 for errors
Conflicts=restic-backup.service
# Prevent backup on unmetered connection. Needs special setup. See README.md.
#Requires=nm-unmetered-connection.service
#After=nm-unmetered-connection.service
[Service]
Type=simple
Nice=10
# pipefail: so that redirecting stderr from the script to systemd-cat does not hide the failed command from OnFailure above.
# `systemd-cat`: allows showing the restic output to the systemd journal
ExecStart=/bin/bash -c 'set -o pipefail; source {{ INSTALL_PREFIX }}/etc/restic/%I.env.sh && {{ INSTALL_PREFIX }}/bin/restic_check.sh 2>&1 | systemd-cat'
ExecStart=/bin/bash -c 'set -o pipefail; source $INSTALL_PREFIX/etc/restic/%I.env.sh && $INSTALL_PREFIX/bin/restic_check.sh 2>&1 | systemd-cat'

View File

@@ -6,6 +6,6 @@ Description=Send status email for %i to user
[Service]
Type=oneshot
ExecStart={{ INSTALL_PREFIX }}/bin/systemd-email abc@gmail.com %i
ExecStart=$INSTALL_PREFIX/bin/systemd-email abc@gmail.com %i
User=root
Group=systemd-journal