diff --git a/playbook.yaml b/playbook.yaml index 4c0c766..350e0da 100644 --- a/playbook.yaml +++ b/playbook.yaml @@ -54,3 +54,8 @@ ansible.builtin.import_role: name: grocy tags: grocy + + - name: Set up Restic stack + ansible.builtin.import_role: + name: restic + tags: restic diff --git a/roles/restic/README.md b/roles/restic/README.md new file mode 100644 index 0000000..8849990 --- /dev/null +++ b/roles/restic/README.md @@ -0,0 +1,49 @@ +# restic + +Backup maintenance stack. + +Takes care of regularly pruning the backup repository and checking its integrity. +Currently only supports S3 as a backend. + +## Defaults + +```yaml +restic_timezone: US/Chicago +``` + +The timezone to be used for the cronjob. + +```yaml +restic_version: latest +``` + +The docker image version to be used in stack creation. + +```yaml +restic_repo: s3.eu-central-1.wasabisys.com/myrepo +restic_pass: +``` + +The repository url and the restic repository password. +See the restic documentation for more information. + +```yaml +restic_s3_key: +restic_s3_secret: +``` + +The restic S3 credentials, i.e. the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. + +```yaml +restic_prune_cron: 0 0 4 * * * +restic_forget_args: --prune --keep-last 14 --keep-daily 2 --keep-weekly 2 +``` + +The default prune and forget cronjob schedule and arguments: Prune the repository every day at 4:00 AM and keep the last 14 snapshots, 2 daily snapshots and 2 weekly snapshots. + +```yaml +restic_check_cron: 0 15 5 * * * +restic_check_args: --read-data-subset=5% +``` + +The default check cronjob schedule and arguments: Check the repository integrity every day at 5:15 AM and in addition to structural checks, read 5 randomly chosen % for a data integrity check. diff --git a/roles/restic/defaults/main.yml b/roles/restic/defaults/main.yml new file mode 100644 index 0000000..b4602e7 --- /dev/null +++ b/roles/restic/defaults/main.yml @@ -0,0 +1,24 @@ +--- +# inherited from global +restic_enable: true +restic_tz: "{{ timezone | default('America/Chicago') }}" + +restic_version: latest +restic_auto_init: true + +restic_prune_cron: 0 0 4 * * * # go-cron starts with seconds in first pos +restic_forget_args: --prune --keep-last 14 --keep-daily 2 --keep-weekly 2 + +restic_check_cron: 0 30 4 * * SUN +restic_check_args: --read-data-subset=15% + +restic_repo: /opt/stack_restic_backup +restic_pass: my-restic-pass +restic_s3_key: +restic_s3_secret: + +# S3 example +# restic_repo: s3.eu-central-1.wasabisys.com/myrepo +# restic_pass: +# restic_s3_key: +# restic_s3_secret: diff --git a/roles/restic/meta/main.yml b/roles/restic/meta/main.yml new file mode 100644 index 0000000..37d0f6f --- /dev/null +++ b/roles/restic/meta/main.yml @@ -0,0 +1,7 @@ +--- +galaxy_info: + author: Marty Oehme + description: Installs a restic-based backup maintenance stack. + license: GPL-3.0-only + min_ansible_version: "2.9" + galaxy_tags: [] diff --git a/roles/restic/tasks/main.yml b/roles/restic/tasks/main.yml new file mode 100644 index 0000000..e87fdf1 --- /dev/null +++ b/roles/restic/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- name: Create local backup directory + ansible.builtin.file: + state: directory + path: "{{ restic_repo }}" + owner: root + group: root + mode: 0770 + when: restic_repo is regex('^/.+') + +- name: Deploy restic to compose + community.docker.docker_compose_v2: + project_name: restic + definition: "{{ lookup('template', 'docker-compose.yaml.j2') | from_yaml }}" + remove_orphans: true + wait: true + wait_timeout: 60 diff --git a/roles/restic/templates/docker-compose.yaml.j2 b/roles/restic/templates/docker-compose.yaml.j2 new file mode 100644 index 0000000..644a8c9 --- /dev/null +++ b/roles/restic/templates/docker-compose.yaml.j2 @@ -0,0 +1,36 @@ +services: + prune: + image: "mazzolino/restic:{{ restic_version }}" + hostname: docker + environment: + - "TZ={{ restic_tz }}" + - "SKIP_INIT={{ not restic_auto_init }}" + - "RUN_ON_STARTUP=true" + - "PRUNE_CRON={{ restic_prune_cron }}" + - "RESTIC_FORGET_ARGS={{ restic_forget_args }}" + - "RESTIC_REPOSITORY={{ restic_repo }}" + - "RESTIC_PASSWORD={{ restic_pass }}" + - "AWS_ACCESS_KEY_ID={{ restic_s3_key }}" + - "AWS_SECRET_ACCESS_KEY={{ restic_s3_secret }}" +{% if restic_repo is regex('^/.+') %} + volumes: + - "{{ restic_repo }}:{{ restic_repo }}" +{% endif %} + + check: + image: "mazzolino/restic:{{ restic_version }}" + hostname: docker + environment: + - "TZ={{ restic_tz }}" + - "SKIP_INIT=true" # only run init on one container to avoid race cond + - "RUN_ON_STARTUP=false" + - "CHECK_CRON={{ restic_check_cron }}" + - "RESTIC_CHECK_ARGS={{ restic_check_args }}" + - "RESTIC_REPOSITORY={{ restic_repo }}" + - "RESTIC_PASSWORD={{ restic_pass }}" + - "AWS_ACCESS_KEY_ID={{ restic_s3_key }}" + - "AWS_SECRET_ACCESS_KEY={{ restic_s3_secret }}" +{% if restic_repo is regex('^/.+') %} + volumes: + - "{{ restic_repo }}:{{ restic_repo }}" +{% endif %} diff --git a/roles/restic/vars/main.yml b/roles/restic/vars/main.yml new file mode 100644 index 0000000..8b3dcf5 --- /dev/null +++ b/roles/restic/vars/main.yml @@ -0,0 +1,8 @@ +--- +stack_name: restic + +stack_image: "mazzolino/restic" + +stack_compose: "{{ lookup('template', 'docker-stack.yml.j2') | from_yaml }}" + +backup_enable: true