Files
Clément Désiles ceca06ff2e feat(backup): add restic backup role with encrypted incremental snapshots
Restic-backed backups using a many-to-many repository/job model.

- repository = destination: restic repository URL verbatim (s3, local,
  removable device), backend credentials via env passthrough, retention
  policy, and an optional per-repository ntfy notify channel
- job = source set: paths, sqlite and postgres dumps (consistent .backup /
  pg_dump instead of torn live-file copies), pushed to one or more repos
- systemd timer per job, per-repo + per-job flock serialisation, restic
  check with read_data_subset for rotating integrity verification

Service roles declare what to back up (ntfy_backup_*, uptime_kuma_backup_*);
the inventory declares where.
2026-09-01 22:20:27 +02:00

52 lines
1.7 KiB
YAML

---
# Retention keys accepted in a repository's `retention` mapping. Each maps to
# the restic `forget` flag of the same name with dashes.
backup_retention_keys:
- keep_last
- keep_hourly
- keep_daily
- keep_weekly
- keep_monthly
- keep_yearly
# Environment variables a repository's `env` mapping is allowed to set, and
# which the generated env file clears between repositories. These are the
# credential/connection variables restic and its backends read; anything a
# previously-processed repository exported must not leak into the next one.
backup_env_keys:
- RESTIC_REPOSITORY
- RESTIC_PASSWORD
- RESTIC_PASSWORD_FILE
- RESTIC_PASSWORD_COMMAND
- RESTIC_CACHE_DIR
- RESTIC_REST_USERNAME
- RESTIC_REST_PASSWORD
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_SESSION_TOKEN
- AWS_DEFAULT_REGION
- AWS_PROFILE
- B2_ACCOUNT_ID
- B2_ACCOUNT_KEY
# Derived values. Lazily evaluated and only valid inside a loop that defines
# `repo`, i.e. tasks/repository.yml.
# RESTIC_REPOSITORY is written verbatim; the environment for the deploy-time
# init tasks is the same `env` mapping the backup script will use, so the two
# can never drift apart.
backup_repo_environment: >-
{{
{
'RESTIC_REPOSITORY': repo.repository,
'RESTIC_PASSWORD': repo.password,
'RESTIC_CACHE_DIR': backup_cache_dir,
}
| combine(repo.env | default({}))
}}
# A device repository is recognised by the presence of a mountpoint, not a
# `type` field. A path-style repository without one is a plain local directory.
backup_repo_is_device: "{{ repo.mountpoint is defined }}"
backup_repo_is_local: "{{ repo.mountpoint is not defined and repo.repository is match('^/') }}"