Skip to content

Repository files navigation

Restor

Restic Backup Orchestrator - Back up services and files to a restic repository.

Features

  • Multiple projects from one config file
  • Parallel or sequential execution
  • Automatic service stop/start during backup (Docker/Podman Compose, systemd)
  • Per-project retention policies (forget) with a single global prune
  • Pre/post backup hooks
  • YAML, JSON, TOML config support

Installation

Download a package or archive from the releases. restor needs restic; the packages install it as a recommended dependency.

Package (.deb / .rpm)

sudo apt install ./restor_<version>_amd64.deb       # Debian, Ubuntu
sudo dnf install ./restor-<version>-1.x86_64.rpm    # Fedora, RHEL

Create the config and enable the daily timer:

sudo cp /usr/share/doc/restor/examples/config.yaml /etc/restor/config.yaml
sudoedit /etc/restor/config.yaml
sudo systemctl enable --now restor.timer

Archive

tar xzf restor_<version>_linux_amd64.tar.gz
cd restor_<version>_linux_amd64
sudo install -m 0755 restor /usr/local/bin/restor
sudo install -d -m 0700 /etc/restor
sudo cp example/config.yaml /etc/restor/config.yaml
sudoedit /etc/restor/config.yaml
sudo cp example/restor.service example/restor.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now restor.timer

As a normal user

The package also installs user units that read ~/.config/restor/config.yaml:

mkdir -p ~/.config/restor
cp /usr/share/doc/restor/examples/config.yaml ~/.config/restor/
systemctl --user enable --now restor.timer
sudo loginctl enable-linger "$USER"   # keep the timer running while logged out

To run a backup by hand: restor --config <file>. Logs: journalctl -u restor (or journalctl --user -u restor).

On SIGINT/SIGTERM restor stops the running backups, restarts the services it stopped and exits within its shutdown timeout (the largest start_timeout + 4 minutes, logged as shutdown_timeout). The shipped units allow 900 s (TimeoutStopSec); raise it if a start_timeout is above 11 minutes.

Configuration

See example/config.yaml for all options and example/restor.env for an env file template. The config file can be YAML, JSON or TOML (chosen by extension).

Global: mode (sequential, the default, runs projects sorted by name; parallel runs them all at once), restic_repo, auto_prune, prune_options, pre_backup_cmd, post_backup_cmd

Per project (required): service_manager (docker-compose, podman-compose, systemd or noop), sources, plus compose_file (compose) or systemd_units (systemd); stop_timeout and start_timeout (seconds) when services are stopped

Per project (optional): services, systemd_scope, stop_services, retention_policy, backup_options, forget_options, pre_backup_cmd, post_backup_cmd

Only the services that are running when a project's backup starts are stopped, and only those are started again afterwards.

Hooks: pre_backup_cmd runs before the services are stopped; if it fails, the project fails and nothing else runs. post_backup_cmd runs after the services are started again, also when the backup failed, so it can undo the pre hook. Hooks run without a shell (split on spaces); use a script for anything more.

Relative env_file, compose_file and sources paths are resolved against the directory of the config file. restor checks the whole file at startup and lists every problem with its field path; unknown keys (typos) are reported as warnings.

Environment: env_file and inline environments are passed to restic, the service managers and the hooks (inline wins). $VAR, ${VAR} and ${VAR:-default} are expanded from the process environment when the config is loaded; write $$ for a literal $. In .env files a value in single quotes is never expanded.

Secrets containing $: expansion also applies to values from the env file (YAML, JSON and unquoted or double-quoted .env values). An unset variable becomes empty, and $ followed by a digit or one of !*#@?- is replaced too, so RESTIC_PASSWORD: "pa$word" becomes pa and restic fails with a wrong password. Write pa$$word, use a single-quoted .env value (RESTIC_PASSWORD='pa$word'), or better, keep the password in a file (RESTIC_PASSWORD_FILE), which is never expanded.

Retention: with auto_prune, each project with a retention_policy runs forget after all backups, then one prune runs for the repository. A project that failed, or whose snapshot is incomplete (restic could not read some files), keeps its history, and prune waits for a run where every project has a complete snapshot.

Project names are case-insensitive; the lowercase name is the restic tag. A name must not start with - or contain , or whitespace.

List options (prune_options, backup_options, forget_options) are passed as one argument per item: write ["-o", "s3.connections=10"] or ["--option=s3.connections=10"]. A single string is one argument (forget_options: --group-by=host,tags keeps its comma).

License

MIT