From 93876315ca461cda0924188abbfb22e7b254da65 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Thu, 17 Jul 2025 19:01:07 +0200 Subject: [PATCH 01/16] repo: Rename all yml extensions to yaml --- roles/arr/defaults/{main.yml => main.yaml} | 0 roles/arr/handlers/{main.yml => main.yaml} | 0 roles/arr/meta/{main.yml => main.yaml} | 0 roles/arr/tasks/{main.yml => main.yaml} | 0 roles/arr/templates/{arr.yml => arr.yaml} | 0 roles/arr/tests/{test.yml => test.yaml} | 0 roles/arr/vars/{main.yml => main.yaml} | 0 roles/grocy/defaults/{main.yml => main.yaml} | 0 roles/grocy/meta/{main.yml => main.yaml} | 0 roles/grocy/tasks/{main.yml => main.yaml} | 0 roles/grocy/tests/{test.yml => test.yaml} | 0 roles/paperless/defaults/{main.yml => main.yaml} | 0 roles/paperless/meta/{main.yml => main.yaml} | 0 roles/paperless/tasks/{main.yml => main.yaml} | 0 roles/paperless/tests/{test.yml => test.yaml} | 0 ...pose.simple_example.yml => docker-compose.simple_example.yaml} | 0 stacks/paperless/{docker-compose.yml => docker-compose.yaml} | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename roles/arr/defaults/{main.yml => main.yaml} (100%) rename roles/arr/handlers/{main.yml => main.yaml} (100%) rename roles/arr/meta/{main.yml => main.yaml} (100%) rename roles/arr/tasks/{main.yml => main.yaml} (100%) rename roles/arr/templates/{arr.yml => arr.yaml} (100%) rename roles/arr/tests/{test.yml => test.yaml} (100%) rename roles/arr/vars/{main.yml => main.yaml} (100%) rename roles/grocy/defaults/{main.yml => main.yaml} (100%) rename roles/grocy/meta/{main.yml => main.yaml} (100%) rename roles/grocy/tasks/{main.yml => main.yaml} (100%) rename roles/grocy/tests/{test.yml => test.yaml} (100%) rename roles/paperless/defaults/{main.yml => main.yaml} (100%) rename roles/paperless/meta/{main.yml => main.yaml} (100%) rename roles/paperless/tasks/{main.yml => main.yaml} (100%) rename roles/paperless/tests/{test.yml => test.yaml} (100%) rename stacks/paperless/{docker-compose.simple_example.yml => docker-compose.simple_example.yaml} (100%) rename stacks/paperless/{docker-compose.yml => docker-compose.yaml} (100%) diff --git a/roles/arr/defaults/main.yml b/roles/arr/defaults/main.yaml similarity index 100% rename from roles/arr/defaults/main.yml rename to roles/arr/defaults/main.yaml diff --git a/roles/arr/handlers/main.yml b/roles/arr/handlers/main.yaml similarity index 100% rename from roles/arr/handlers/main.yml rename to roles/arr/handlers/main.yaml diff --git a/roles/arr/meta/main.yml b/roles/arr/meta/main.yaml similarity index 100% rename from roles/arr/meta/main.yml rename to roles/arr/meta/main.yaml diff --git a/roles/arr/tasks/main.yml b/roles/arr/tasks/main.yaml similarity index 100% rename from roles/arr/tasks/main.yml rename to roles/arr/tasks/main.yaml diff --git a/roles/arr/templates/arr.yml b/roles/arr/templates/arr.yaml similarity index 100% rename from roles/arr/templates/arr.yml rename to roles/arr/templates/arr.yaml diff --git a/roles/arr/tests/test.yml b/roles/arr/tests/test.yaml similarity index 100% rename from roles/arr/tests/test.yml rename to roles/arr/tests/test.yaml diff --git a/roles/arr/vars/main.yml b/roles/arr/vars/main.yaml similarity index 100% rename from roles/arr/vars/main.yml rename to roles/arr/vars/main.yaml diff --git a/roles/grocy/defaults/main.yml b/roles/grocy/defaults/main.yaml similarity index 100% rename from roles/grocy/defaults/main.yml rename to roles/grocy/defaults/main.yaml diff --git a/roles/grocy/meta/main.yml b/roles/grocy/meta/main.yaml similarity index 100% rename from roles/grocy/meta/main.yml rename to roles/grocy/meta/main.yaml diff --git a/roles/grocy/tasks/main.yml b/roles/grocy/tasks/main.yaml similarity index 100% rename from roles/grocy/tasks/main.yml rename to roles/grocy/tasks/main.yaml diff --git a/roles/grocy/tests/test.yml b/roles/grocy/tests/test.yaml similarity index 100% rename from roles/grocy/tests/test.yml rename to roles/grocy/tests/test.yaml diff --git a/roles/paperless/defaults/main.yml b/roles/paperless/defaults/main.yaml similarity index 100% rename from roles/paperless/defaults/main.yml rename to roles/paperless/defaults/main.yaml diff --git a/roles/paperless/meta/main.yml b/roles/paperless/meta/main.yaml similarity index 100% rename from roles/paperless/meta/main.yml rename to roles/paperless/meta/main.yaml diff --git a/roles/paperless/tasks/main.yml b/roles/paperless/tasks/main.yaml similarity index 100% rename from roles/paperless/tasks/main.yml rename to roles/paperless/tasks/main.yaml diff --git a/roles/paperless/tests/test.yml b/roles/paperless/tests/test.yaml similarity index 100% rename from roles/paperless/tests/test.yml rename to roles/paperless/tests/test.yaml diff --git a/stacks/paperless/docker-compose.simple_example.yml b/stacks/paperless/docker-compose.simple_example.yaml similarity index 100% rename from stacks/paperless/docker-compose.simple_example.yml rename to stacks/paperless/docker-compose.simple_example.yaml diff --git a/stacks/paperless/docker-compose.yml b/stacks/paperless/docker-compose.yaml similarity index 100% rename from stacks/paperless/docker-compose.yml rename to stacks/paperless/docker-compose.yaml From 86d49a756b40ceee16d19b3a875c80104919e51d Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Thu, 17 Jul 2025 21:38:35 +0200 Subject: [PATCH 02/16] restic: Add stack Adapted from cloudserve-infrastructure, implements a backup stack using restic. The actual backups have to be implemented by individual other roles but this sets up initialization, pruning and checking of a repository. --- playbook.yaml | 5 ++ roles/restic/README.md | 49 +++++++++++++++++++ roles/restic/defaults/main.yml | 24 +++++++++ roles/restic/meta/main.yml | 7 +++ roles/restic/tasks/main.yml | 17 +++++++ roles/restic/templates/docker-compose.yaml.j2 | 36 ++++++++++++++ roles/restic/vars/main.yml | 8 +++ 7 files changed, 146 insertions(+) create mode 100644 roles/restic/README.md create mode 100644 roles/restic/defaults/main.yml create mode 100644 roles/restic/meta/main.yml create mode 100644 roles/restic/tasks/main.yml create mode 100644 roles/restic/templates/docker-compose.yaml.j2 create mode 100644 roles/restic/vars/main.yml 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 From 003cf64a7710fd5e04ac89eb18b5703f12ff237b Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 15:12:19 +0200 Subject: [PATCH 03/16] restic: Update docs --- roles/restic/README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/roles/restic/README.md b/roles/restic/README.md index 8849990..c4f31b3 100644 --- a/roles/restic/README.md +++ b/roles/restic/README.md @@ -3,15 +3,17 @@ Backup maintenance stack. Takes care of regularly pruning the backup repository and checking its integrity. -Currently only supports S3 as a backend. +Supports any restic backend. +Variables set here are used in other roles to individually configure their backups. ## Defaults ```yaml -restic_timezone: US/Chicago +restic_tz: US/Chicago ``` -The timezone to be used for the cronjob. +The timezone to be used for any cronjobs. +Inherits from the global `timezone` variable or can be set individually. ```yaml restic_version: latest @@ -25,6 +27,8 @@ restic_pass: ``` The repository url and the restic repository password. +Can be of any type restic supports +(e.g. `/path/to/backups` for local, `s3:address.to.s3.com` for S3, etc.) See the restic documentation for more information. ```yaml @@ -39,11 +43,15 @@ 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. +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. +Be aware that go-cron has an extra field in the front for seconds compared to many other implementations. ```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. +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. From fab6f5ff7c097518de5944a5c1b49416f45e7683 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 15:12:19 +0200 Subject: [PATCH 04/16] restic: Add notification Notifies double for each prune/check run which may need to be fixed. Also custom notification contents cannot currently be passed. Lastly, we should put identifying information into the notification body (such as the hostname/container name for which the notification is relevant). --- roles/restic/README.md | 12 +++ roles/restic/defaults/main.yml | 5 ++ roles/restic/templates/docker-compose.yaml.j2 | 76 ++++++++++++++++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/roles/restic/README.md b/roles/restic/README.md index c4f31b3..fcd739c 100644 --- a/roles/restic/README.md +++ b/roles/restic/README.md @@ -55,3 +55,15 @@ 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. + +```yaml +restic_notify_success: +restic_notify_failure: +restic_notify_exit: +``` + +If restic should notify the user on success/failure/exit (i.e. any outcome process finish). +Defaults to no notifications. +Uses Apprise and thus takes an [apprise URL](https://github.com/caronc/apprise/wiki) in the form of `ntfy://my-ntfy-channel`. +Setting one of these configures _both_ the prune and the check to notify the user if either is done, +so currently any success (failure/exit) notification would be doubled for each sucess (failure/exit). diff --git a/roles/restic/defaults/main.yml b/roles/restic/defaults/main.yml index b4602e7..5ffcab5 100644 --- a/roles/restic/defaults/main.yml +++ b/roles/restic/defaults/main.yml @@ -12,11 +12,16 @@ 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% +# backup target restic_repo: /opt/stack_restic_backup restic_pass: my-restic-pass restic_s3_key: restic_s3_secret: +restic_notify_success: +restic_notify_failure: +restic_notify_exit: + # S3 example # restic_repo: s3.eu-central-1.wasabisys.com/myrepo # restic_pass: diff --git a/roles/restic/templates/docker-compose.yaml.j2 b/roles/restic/templates/docker-compose.yaml.j2 index 644a8c9..e94aa80 100644 --- a/roles/restic/templates/docker-compose.yaml.j2 +++ b/roles/restic/templates/docker-compose.yaml.j2 @@ -2,16 +2,32 @@ services: prune: image: "mazzolino/restic:{{ restic_version }}" hostname: docker + networks: + - restic_notify + depends_on: + - notify_exit 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 }}" + 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_notify_success != None %} + POST_COMMANDS_SUCCESS: |- + curl -X POST --data "{\"title\": \"Restic Prune successful\", \"body\": \" \"}" http://notify_success:5000 +{% endif %} +{% if restic_notify_failure != None %} + POST_COMMANDS_FAILURE: |- + curl -X POST --data "{\"title\": \"Restic Prune failed\", \"body\": \" \"}" http://notify_failure:5000 +{% endif %} +{% if restic_notify_exit != None %} + POST_COMMANDS_EXIT: |- + curl -X POST --data "{\"title\": \"Restic Prune exited\", \"body\": \" \"}" http://notify_exit:5000 +{% endif %} {% if restic_repo is regex('^/.+') %} volumes: - "{{ restic_repo }}:{{ restic_repo }}" @@ -20,6 +36,8 @@ services: check: image: "mazzolino/restic:{{ restic_version }}" hostname: docker + networks: + - restic_notify environment: - "TZ={{ restic_tz }}" - "SKIP_INIT=true" # only run init on one container to avoid race cond @@ -30,7 +48,47 @@ services: - "RESTIC_PASSWORD={{ restic_pass }}" - "AWS_ACCESS_KEY_ID={{ restic_s3_key }}" - "AWS_SECRET_ACCESS_KEY={{ restic_s3_secret }}" +{% if restic_notify_success != None %} + POST_COMMANDS_SUCCESS: |- + curl -X POST --data "{\"title\": \"Restic Check successful\", \"body\": \" \"}" http://notify_success:5000 +{% endif %} +{% if restic_notify_failure != None %} + POST_COMMANDS_FAILURE: |- + curl -X POST --data "{\"title\": \"Restic Check failed\", \"body\": \" \"}" http://notify_failure:5000 +{% endif %} +{% if restic_notify_exit != None %} + POST_COMMANDS_EXIT: |- + curl -X POST --data "{\"title\": \"Restic Check exited\", \"body\": \" \"}" http://notify_exit:5000 +{% endif %} {% if restic_repo is regex('^/.+') %} volumes: - "{{ restic_repo }}:{{ restic_repo }}" {% endif %} + +{% if restic_notify_success != None %} + notify_success: + image: mazzolino/apprise-microservice:latest + networks: + - restic_notify + environment: + NOTIFICATION_URLS: {{ restic_notify_success }} +{% endif %} +{% if restic_notify_failure != None %} + notify_failure: + image: mazzolino/apprise-microservice:latest + networks: + - restic_notify + environment: + NOTIFICATION_URLS: {{ restic_notify_failure }} +{% endif %} +{% if restic_notify_exit != None %} + notify_exit: + image: mazzolino/apprise-microservice:latest + networks: + - restic_notify + environment: + NOTIFICATION_URLS: {{ restic_notify_exit }} +{% endif %} + +networks: + restic_notify: From 065fd4562beb34fadd2b9fc02c08c58b8cc864ea Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 15:12:19 +0200 Subject: [PATCH 05/16] vault: Add restic notification secrets --- group_vars/instance_system/vault.yaml | 66 ++++++++++++++------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/group_vars/instance_system/vault.yaml b/group_vars/instance_system/vault.yaml index d206e92..9552d2b 100644 --- a/group_vars/instance_system/vault.yaml +++ b/group_vars/instance_system/vault.yaml @@ -1,33 +1,35 @@ $ANSIBLE_VAULT;1.1;AES256 -61653738666234666537383737636631323931396632353236366639323833363134313066613962 -3730306266353834303135653939323561666131333539380a333039663466393061343736653935 -38613532346132303730363835373165326361373561666565316332623964316265306131663332 -3832373861666138330a646631346262666233383938366234373335383064353662326461656430 -35626334356566613731333639663130306133353439666234636137636265313662393730626232 -37633430663438666461353735656461623165316134346130663963356464656436313966613739 -35373763633936336565666261353631366531323430663461376338373265393235313932363733 -66323634656539303732303338376264353935316538373465633462386466383662626534623365 -38363362653535386130366462393365393731313034316431373039393331396231663365616435 -31373632633264383266623733613530333134643730396336373461633739383436623736613638 -39303163373234313534323562643836636337323266366538323932656563376163376162306165 -31356165343739373237363734393230356236363339653564326537313336663036313437646636 -35313336396361353365323763643861656561633235383732376236333232363565376365386232 -32353737373239396436316538323661303164373765343936643736663035323237663935643834 -39333238666162363366633938333231313966613430303236636634316136373433323536613833 -61386434636464633662383736323630326630313561356335306663333864643462366335643430 -34396331633935656632323062613433386234386535613337313561353037356633376664333634 -62303063636135343737323331623736303636643735613337633530666134333266323934376631 -62386535323834323435373362333662613637383065346335373465343765663836626434666463 -39623162313438346239393261613738376435636539623663646461396330326661343633353531 -66643362326464376437643732366466633332323837323433343165313735653331346431656136 -32376165666437373435646566373036313639316165353639626435333330626530373662333031 -37656463333231356465383039336237626235613331376639656665303431353131336436653839 -32383031376134643236333231323863633933376238613462623034663262316339353061343635 -36336338323331343866643836666530616639313933306536343631303635383862363633373562 -65663930306461316363376364663935663566393335396130626436316262643730393133363837 -63343334613966343466646366303137663965633639653262316630626436346461643533393135 -31303630616662623237313331356263613535323739616162393165336163346161303836653638 -30386636663230613731383165333462363033333734326662373961383236386631643139373934 -38353732393265656162633739623061333833353138303664633463643234396338643736353132 -32313763656165643963373236346364636534333964383131373839616435356631386463646437 -39336237356337383930 +36316161393635323361333537313262643766616137336334316330363661666530623039623165 +6561323966656238643532646431323064316230616632340a643961363064336239633661356532 +65333561336666386137326539346637663561633937346232646238316535303966656632353366 +6266356339303263360a626465353639653463313431653230636462353935326139386163643839 +39383261323730653237396538323038623633366264386265386233353439353437633366356234 +62616666373439353239616130323761656431626237653263636135356266373337313666646437 +62346535656237323761663834653061343731633332626662376136383530383638346338653837 +62613638373561373866626538663531353934396466333464373564656530616635616131653037 +37663435613565303364323635663362373862346630626631323866373661313839373962376333 +36326238663063343934353062643465366165393638376166306361616461353365623739663261 +65366639646461376639643138383232343234623831306136613765313938383665656631356464 +38643733646132386666663866363332636662636164666331343936393030643830386162353264 +62313264613366646131666530316164356562633838343463656238656435663661643730356262 +38633134633936306138633434356538346461383163636333633932623966303061643136336332 +64373737303662386335666630353966346363633264326432383262393238366431656337633634 +65653566663365346161346461643164303563393237663138383030326134326536313631313432 +62396532303463353630383663613666353638613735303733323464663339376630366236313432 +32343339396537653931653261363066623565363636626639663937613464623037343234313936 +31306533646661333539323161323934643331373238643730343162303461616530323530323662 +61326562326537343565313039326334316237363865646566653936323933653638623761333434 +38326639316165356137353434633831383434666430393434623132376636613531343333633561 +32333361663835396664656361373236393937353764323061646332643838396232633162306364 +37383464363737326661656139656561343139373535383666303337366534356234666433633162 +61343835313862643066366565323730346463393765333939343036666461323463613439666433 +65343266373934633330656331623932363039616461356432346431366565303663393563373763 +34396439383939633330383330653738616263393433656631623865376634643361363333616364 +36356437636435386363653934626637386364326162333331393733393261623362393434393637 +63333935633863333166313432646463306364626335303539616238343930366232343131393664 +31323636373161623335653033383436303837333562616162653833656363346638633438623031 +64373033653730316237666232653233323438396633623161363531383837353361666431393162 +30616162316631353630393136303239623666633730613962613366336337313739613266353463 +38393461326136343161363234393436623163643837333732613639643534653966636666356565 +31363364343136393438353534663633376531353633663361346232383766303436383066616536 +6463323939363964366632386336616463366531393261303235 From e13d85990c3d2c1c8ee37f15eb2a501bc38a73de Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 17:27:04 +0200 Subject: [PATCH 06/16] restic: Fix template env vars --- roles/restic/templates/docker-compose.yaml.j2 | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/roles/restic/templates/docker-compose.yaml.j2 b/roles/restic/templates/docker-compose.yaml.j2 index e94aa80..3bc4489 100644 --- a/roles/restic/templates/docker-compose.yaml.j2 +++ b/roles/restic/templates/docker-compose.yaml.j2 @@ -1,11 +1,10 @@ services: prune: image: "mazzolino/restic:{{ restic_version }}" + # TODO: get actual hostname/inventoryname? put it into notifications body below too hostname: docker networks: - restic_notify - depends_on: - - notify_exit environment: TZ: "{{ restic_tz }}" SKIP_INIT: "{{ not restic_auto_init }}" @@ -39,15 +38,15 @@ services: networks: - restic_notify 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 }}" + TZ: "{{ restic_tz }}" + SKIP_INIT: true + RUN_ON_STARTUP: true + 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_notify_success != None %} POST_COMMANDS_SUCCESS: |- curl -X POST --data "{\"title\": \"Restic Check successful\", \"body\": \" \"}" http://notify_success:5000 From b81328d400c10569a7708e333275f6d22a86acf1 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 16:56:08 +0200 Subject: [PATCH 07/16] paperless: Add restic backups --- roles/paperless/defaults/main.yaml | 3 +++ .../paperless/templates/docker-compose.yaml.j2 | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/roles/paperless/defaults/main.yaml b/roles/paperless/defaults/main.yaml index 70860bc..0f933eb 100644 --- a/roles/paperless/defaults/main.yaml +++ b/roles/paperless/defaults/main.yaml @@ -7,6 +7,9 @@ stack_paperless_tz: "{{ timezone | default('America/Chicago') }}" stack_paperless_puid: "{{ puid | default(1000) }}" stack_paperless_pgid: "{{ pgid | default(100) }}" +stack_paperless_restic_enable: true +stack_paperless_restic_cron: 0 0 2 * * * + stack_paperless_env_dir: /opt/stack_paperless stack_paperless_serve_dir: /srv stack_paperless_serve_dir_create: true diff --git a/roles/paperless/templates/docker-compose.yaml.j2 b/roles/paperless/templates/docker-compose.yaml.j2 index e2d739f..a7bf1d0 100644 --- a/roles/paperless/templates/docker-compose.yaml.j2 +++ b/roles/paperless/templates/docker-compose.yaml.j2 @@ -65,6 +65,24 @@ services: environment: REDIS_ARGS: "--save 60 10" +{% if restic_enable is not undefined and not false and stack_paperless_restic_enable is not undefined and not false %} + backup: + image: mazzolino/restic + environment: + TZ: "{{ restic_tz }}" + BACKUP_CRON: "{{ stack_paperless_restic_cron }}" + RESTIC_REPOSITORY: "{{ restic_repo }}" + RESTIC_PASSWORD: "{{ restic_pass }}" + AWS_ACCESS_KEY_ID: "{{ restic_s3_key }}" + AWS_SECRET_ACCESS_KEY: "{{ restic_s3_secret }}" + RESTIC_BACKUP_ARGS: >- + --tag paperless + RESTIC_BACKUP_SOURCES: "/backup" + volumes: + - "{{ stack_paperless_env_dir }}:/backup/{{ stack_paperless_env_dir }}" + - "{{ stack_paperless_serve_dir }}/documents:/backup/{{ stack_paperless_serve_dir }}/documents" +{% endif %} + networks: caddy: external: true From cd842ea4b5ef2184e7813d3b02a946ba3b6831b2 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 18:36:19 +0200 Subject: [PATCH 08/16] Add hostname to restic containers Will pass through the hostname to any snapshots set up. The hostname is _not_ derived from the random docker container string but instead takes the name of the _host_ on which docker is running (from ansible facts). The hostname in combination with the tag should point to the correct host -> stack which is being backed up. --- roles/paperless/templates/docker-compose.yaml.j2 | 1 + roles/restic/templates/docker-compose.yaml.j2 | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/roles/paperless/templates/docker-compose.yaml.j2 b/roles/paperless/templates/docker-compose.yaml.j2 index a7bf1d0..37d5361 100644 --- a/roles/paperless/templates/docker-compose.yaml.j2 +++ b/roles/paperless/templates/docker-compose.yaml.j2 @@ -68,6 +68,7 @@ services: {% if restic_enable is not undefined and not false and stack_paperless_restic_enable is not undefined and not false %} backup: image: mazzolino/restic + hostname: "{{ ansible_hostname }}" environment: TZ: "{{ restic_tz }}" BACKUP_CRON: "{{ stack_paperless_restic_cron }}" diff --git a/roles/restic/templates/docker-compose.yaml.j2 b/roles/restic/templates/docker-compose.yaml.j2 index 3bc4489..306c522 100644 --- a/roles/restic/templates/docker-compose.yaml.j2 +++ b/roles/restic/templates/docker-compose.yaml.j2 @@ -1,8 +1,7 @@ services: prune: image: "mazzolino/restic:{{ restic_version }}" - # TODO: get actual hostname/inventoryname? put it into notifications body below too - hostname: docker + hostname: "{{ ansible_hostname }}" networks: - restic_notify environment: @@ -17,15 +16,15 @@ services: AWS_SECRET_ACCESS_KEY: "{{ restic_s3_secret }}" {% if restic_notify_success != None %} POST_COMMANDS_SUCCESS: |- - curl -X POST --data "{\"title\": \"Restic Prune successful\", \"body\": \" \"}" http://notify_success:5000 + curl -X POST --data "{\"title\": \"Restic Prune successful\", \"body\": \"{{ ansible_hostname }}\"}" http://notify_success:5000 {% endif %} {% if restic_notify_failure != None %} POST_COMMANDS_FAILURE: |- - curl -X POST --data "{\"title\": \"Restic Prune failed\", \"body\": \" \"}" http://notify_failure:5000 + curl -X POST --data "{\"title\": \"Restic Prune failed\", \"body\": \"{{ ansible_hostname }}\"}" http://notify_failure:5000 {% endif %} {% if restic_notify_exit != None %} POST_COMMANDS_EXIT: |- - curl -X POST --data "{\"title\": \"Restic Prune exited\", \"body\": \" \"}" http://notify_exit:5000 + curl -X POST --data "{\"title\": \"Restic Prune exited\", \"body\": \"{{ ansible_hostname }}\"}" http://notify_exit:5000 {% endif %} {% if restic_repo is regex('^/.+') %} volumes: @@ -49,15 +48,15 @@ services: AWS_SECRET_ACCESS_KEY: "{{ restic_s3_secret }}" {% if restic_notify_success != None %} POST_COMMANDS_SUCCESS: |- - curl -X POST --data "{\"title\": \"Restic Check successful\", \"body\": \" \"}" http://notify_success:5000 + curl -X POST --data "{\"title\": \"Restic Check successful\", \"body\": \"{{ ansible_hostname }}\"}" http://notify_success:5000 {% endif %} {% if restic_notify_failure != None %} POST_COMMANDS_FAILURE: |- - curl -X POST --data "{\"title\": \"Restic Check failed\", \"body\": \" \"}" http://notify_failure:5000 + curl -X POST --data "{\"title\": \"Restic Check failed\", \"body\": \"{{ ansible_hostname }}\"}" http://notify_failure:5000 {% endif %} {% if restic_notify_exit != None %} POST_COMMANDS_EXIT: |- - curl -X POST --data "{\"title\": \"Restic Check exited\", \"body\": \" \"}" http://notify_exit:5000 + curl -X POST --data "{\"title\": \"Restic Check exited\", \"body\": \"{{ ansible_hostname }}\"}" http://notify_exit:5000 {% endif %} {% if restic_repo is regex('^/.+') %} volumes: From af5b647daf984cd750f63e0f6315856f8fa13777 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 18:40:18 +0200 Subject: [PATCH 09/16] grocy: Fix container service name --- roles/grocy/templates/docker-compose.yaml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/grocy/templates/docker-compose.yaml.j2 b/roles/grocy/templates/docker-compose.yaml.j2 index d291d49..3330659 100644 --- a/roles/grocy/templates/docker-compose.yaml.j2 +++ b/roles/grocy/templates/docker-compose.yaml.j2 @@ -1,5 +1,5 @@ services: - sonarr: + grocy: container_name: grocy image: lscr.io/linuxserver/grocy:latest networks: From 9cf1a5e571ee8b337d1fb857ce963610091cb9d2 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 20:02:19 +0200 Subject: [PATCH 10/16] grocy: Add restic backup --- roles/grocy/defaults/main.yaml | 3 +++ roles/grocy/templates/docker-compose.yaml.j2 | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/roles/grocy/defaults/main.yaml b/roles/grocy/defaults/main.yaml index 64ecba9..50a3b35 100644 --- a/roles/grocy/defaults/main.yaml +++ b/roles/grocy/defaults/main.yaml @@ -7,5 +7,8 @@ stack_grocy_puid: "{{ puid | default(1000) }}" stack_grocy_pgid: "{{ pgid | default(100) }}" stack_grocy_umask_set: "{{ umask_set | default('022') }}" +stack_grocy_restic_enable: true +stack_grocy_restic_cron: 0 15 2 * * * + stack_grocy_env_dir: /opt/stack_grocy diff --git a/roles/grocy/templates/docker-compose.yaml.j2 b/roles/grocy/templates/docker-compose.yaml.j2 index 3330659..dce0fdb 100644 --- a/roles/grocy/templates/docker-compose.yaml.j2 +++ b/roles/grocy/templates/docker-compose.yaml.j2 @@ -16,9 +16,23 @@ services: caddy: "{{ stack_grocy_subdomain }}" caddy.reverse_proxy: "{{ '{{' }}upstreams 80{{ '}}'}}" +{% if restic_enable is not undefined and not false and stack_grocy_restic_enable is not undefined and not false %} + backup: + image: mazzolino/restic + environment: + TZ: "{{ restic_tz }}" + BACKUP_CRON: "{{ stack_grocy_restic_cron }}" + RESTIC_REPOSITORY: "{{ restic_repo }}" + RESTIC_PASSWORD: "{{ restic_pass }}" + AWS_ACCESS_KEY_ID: "{{ restic_s3_key }}" + AWS_SECRET_ACCESS_KEY: "{{ restic_s3_secret }}" + RESTIC_BACKUP_ARGS: >- + --tag grocy + RESTIC_BACKUP_SOURCES: "/backup" + volumes: + - "{{ stack_grocy_env_dir }}:/backup/{{ stack_grocy_env_dir }}" +{% endif %} + networks: caddy: external: true - -volumes: - caddy_data: {} From f1be696479548bd2e19bddfd829002289138ecff Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 17:28:29 +0200 Subject: [PATCH 11/16] grocy: Add docker hostname to backup container --- roles/grocy/templates/docker-compose.yaml.j2 | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/grocy/templates/docker-compose.yaml.j2 b/roles/grocy/templates/docker-compose.yaml.j2 index dce0fdb..d2a3d46 100644 --- a/roles/grocy/templates/docker-compose.yaml.j2 +++ b/roles/grocy/templates/docker-compose.yaml.j2 @@ -19,6 +19,7 @@ services: {% if restic_enable is not undefined and not false and stack_grocy_restic_enable is not undefined and not false %} backup: image: mazzolino/restic + hostname: "{{ ansible_hostname }}" environment: TZ: "{{ restic_tz }}" BACKUP_CRON: "{{ stack_grocy_restic_cron }}" From 1fd72a05a62f212dc5728a090ffb9053b1abe80c Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 20:17:36 +0200 Subject: [PATCH 12/16] restic: Fix backup container local repo mounting If our chosen backup repo is a local one, each restic container needs to mount the local path as a volume, otherwise the data is stuck in the container itself. --- roles/grocy/templates/docker-compose.yaml.j2 | 3 +++ roles/paperless/templates/docker-compose.yaml.j2 | 3 +++ 2 files changed, 6 insertions(+) diff --git a/roles/grocy/templates/docker-compose.yaml.j2 b/roles/grocy/templates/docker-compose.yaml.j2 index d2a3d46..66012f7 100644 --- a/roles/grocy/templates/docker-compose.yaml.j2 +++ b/roles/grocy/templates/docker-compose.yaml.j2 @@ -31,6 +31,9 @@ services: --tag grocy RESTIC_BACKUP_SOURCES: "/backup" volumes: +{% if restic_repo is regex('^/.+') %} + - "{{ restic_repo }}:{{ restic_repo }}" +{% endif %} - "{{ stack_grocy_env_dir }}:/backup/{{ stack_grocy_env_dir }}" {% endif %} diff --git a/roles/paperless/templates/docker-compose.yaml.j2 b/roles/paperless/templates/docker-compose.yaml.j2 index 37d5361..170dd7e 100644 --- a/roles/paperless/templates/docker-compose.yaml.j2 +++ b/roles/paperless/templates/docker-compose.yaml.j2 @@ -80,6 +80,9 @@ services: --tag paperless RESTIC_BACKUP_SOURCES: "/backup" volumes: +{% if restic_repo is regex('^/.+') %} + - "{{ restic_repo }}:{{ restic_repo }}" +{% endif %} - "{{ stack_paperless_env_dir }}:/backup/{{ stack_paperless_env_dir }}" - "{{ stack_paperless_serve_dir }}/documents:/backup/{{ stack_paperless_serve_dir }}/documents" {% endif %} From 363ce9ae6f17986fd05272a140ff3609c59cffd8 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 21:05:16 +0200 Subject: [PATCH 13/16] arr: Remove leftover arr.yml file --- roles/arr/templates/arr.yaml | 171 ----------------------------------- 1 file changed, 171 deletions(-) delete mode 100644 roles/arr/templates/arr.yaml diff --git a/roles/arr/templates/arr.yaml b/roles/arr/templates/arr.yaml deleted file mode 100644 index 9d765e1..0000000 --- a/roles/arr/templates/arr.yaml +++ /dev/null @@ -1,171 +0,0 @@ -services: - sonarr: - container_name: sonarr - image: lscr.io/linuxserver/sonarr:latest - ports: - - 8989:8989 - environment: - - PUID=${PUID} - - PGID=${PGID} - - UMASK_SET=022 - - TZ=${TZ} - volumes: - - "./config/sonarr:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH:/data" - restart: unless-stopped - radarr: - container_name: radarr - image: lscr.io/linuxserver/radarr:latest - ports: - - 7878:7878 - environment: - - PUID=${PUID} - - PGID=${PGID} - - UMASK_SET=022 - - TZ=${TZ} - volumes: - - "./config/radarr:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH:/data" - restart: unless-stopped - lidarr: - container_name: lidarr - image: lscr.io/linuxserver/lidarr:latest - ports: - - 8686:8686 - environment: - - PUID=${PUID} - - PGID=${PGID} - - UMASK_SET=022 - - TZ=${TZ} - - DOCKER_MODS=linuxserver/mods:universal-docker - volumes: - - "./config/lidarr:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH:/data" - - "/var/run/docker.sock:/var/run/docker.sock:ro" - restart: unless-stopped - readarr: - container_name: readarr - image: lscr.io/linuxserver/readarr:develop - ports: - - 8787:8787 - environment: - - PUID=${PUID} - - PGID=${PGID} - - UMASK_SET=022 - - TZ=${TZ} - volumes: - - "./config/readarr:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH:/data" - restart: unless-stopped - prowlarr: - container_name: prowlarr - image: lscr.io/linuxserver/prowlarr:develop - environment: - - PUID=${PUID} - - PGID=${PGID} - - UMASK_SET=022 - - TZ=${TZ} - volumes: - - "./config/prowlarr:/config" - ports: - - 9696:9696 - restart: unless-stopped - sabnzbd: - container_name: sabnzbd - image: lscr.io/linuxserver/sabnzbd:latest - environment: - - PUID=${PUID} - - PGID=${PGID} - - TZ=${TZ} - volumes: - - "./config/sabnzbd:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH/usenet:/data/usenet:rw" - ports: - - 8080:8080 - restart: unless-stopped - pia-qbittorrent: - image: j4ym0/pia-qbittorrent - container_name: pia-qbittorrent - cap_add: - - NET_ADMIN - environment: - - UID=${PUID} - - GID=${PGID} - - TZ=${TZ} - - REGION=Netherlands - - USER=${PIA_USER} - - PASSWORD=${PIA_PASS} - volumes: - - "./config/piaqbit:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH/torrent:/downloads:rw" - ports: - - "8888:8888" - restart: unless-stopped - jellyfin: - image: lscr.io/linuxserver/jellyfin:latest - container_name: jellyfin - environment: - - PUID={$PUID} - - PGID={$PGID} - - TZ=${TZ} - #- JELLYFIN_PublishedServerUrl=192.168.0.5 #optional - volumes: - - ".config/jellyfin:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH/media:/data" - ports: - - 8096:8096 - - 7359:7359/udp #optional - network discovery - - 1900:1900/udp #optional - dlna discovery - restart: unless-stopped - audiobookshelf: - container_name: audiobookshelf - image: ghcr.io/advplyr/audiobookshelf:latest - environment: - - PUID=${PUID} - - PGID=${PGID} - - UMASK_SET=022 - - TZ=${TZ} - ports: - - 13378:80 - volumes: - - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/books:/audiobooks" - - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/podcasts:/podcasts" - - ".config/audiobookshelf:/config" - - ".metadata/audiobookshelf:/metadata" - restart: unless-stopped - jellyseerr: - image: fallenbagel/jellyseerr:latest - container_name: jellyseerr - environment: - - TZ=${TZ} - ports: - - 5055:5055 - volumes: - - "./config/jellyseerr:/app/config" - restart: unless-stopped - beets: - image: lscr.io/linuxserver/beets:latest - container_name: beets - environment: - - PUID=${PUID} - - PGID=${PGID} - - TZ=${TZ} - volumes: - - "./config/beets:/config" - - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/music:/music" - - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/music-unsorted:/downloads" - - "CHANGE_TO_COMPOSE_DATA_PATH:/data" - ports: - - 8337:8337 - restart: unless-stopped - homarr: - image: ghcr.io/ajnart/homarr:latest - container_name: homarr - volumes: - - /var/run/docker.sock:/var/run/docker.sock # Optional, only if you want docker integration - - ./config/homarr/configs:/app/data/configs - - ./config/homarr/icons:/app/public/icons - - ./config/homarr/data:/data - ports: - - '80:7575' - restart: unless-stopped From 64b85c0c4065c0302aa52e3592a572d3ba86e6e9 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 21:05:16 +0200 Subject: [PATCH 14/16] arr: Add restic backup --- roles/arr/defaults/main.yaml | 3 +++ roles/arr/templates/docker-compose.yaml.j2 | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/roles/arr/defaults/main.yaml b/roles/arr/defaults/main.yaml index e4a00e5..40af124 100644 --- a/roles/arr/defaults/main.yaml +++ b/roles/arr/defaults/main.yaml @@ -20,6 +20,9 @@ arrstack_puid: "{{ puid | default(1000) }}" arrstack_pgid: "{{ pgid | default(100) }}" arrstack_umask_set: "{{ umask_set | default('022') }}" +arrstack_restic_enable: true +arrstack_restic_cron: 0 30 2 * * * + arrstack_env_dir: /opt/arrstack arrstack_serve_dir: /srv arrstack_serve_dir_create: true diff --git a/roles/arr/templates/docker-compose.yaml.j2 b/roles/arr/templates/docker-compose.yaml.j2 index 0dce48d..df1d527 100644 --- a/roles/arr/templates/docker-compose.yaml.j2 +++ b/roles/arr/templates/docker-compose.yaml.j2 @@ -290,6 +290,26 @@ services: caddy: "{{ arrstack_gonic_subdomain }}" caddy.reverse_proxy: "{{ '{{' }}upstreams 80{{ '}}'}}" +{% if restic_enable is not undefined and not false and arrstack_restic_enable is not undefined and not false %} + backup: + image: mazzolino/restic + hostname: "{{ ansible_hostname }}" + environment: + TZ: "{{ restic_tz }}" + BACKUP_CRON: "{{ arrstack_restic_cron }}" + RESTIC_REPOSITORY: "{{ restic_repo }}" + RESTIC_PASSWORD: "{{ restic_pass }}" + AWS_ACCESS_KEY_ID: "{{ restic_s3_key }}" + AWS_SECRET_ACCESS_KEY: "{{ restic_s3_secret }}" + RESTIC_BACKUP_ARGS: >- + --tag arr + RESTIC_BACKUP_SOURCES: "/backup" + volumes: +{% if restic_repo is regex('^/.+') %} + - "{{ restic_repo }}:{{ restic_repo }}" +{% endif %} + - "{{ arrstack_env_dir }}:/backup/{{ arrstack_env_dir }}" +{% endif %} networks: caddy: From 945868feff90b423dc7acbfa3b972ba0029b77ac Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 21:33:17 +0200 Subject: [PATCH 15/16] vault: Add restic secrets --- group_vars/instance_system/vault.yaml | 79 +++++++++++++++------------ 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/group_vars/instance_system/vault.yaml b/group_vars/instance_system/vault.yaml index 9552d2b..4a12a4d 100644 --- a/group_vars/instance_system/vault.yaml +++ b/group_vars/instance_system/vault.yaml @@ -1,35 +1,46 @@ $ANSIBLE_VAULT;1.1;AES256 -36316161393635323361333537313262643766616137336334316330363661666530623039623165 -6561323966656238643532646431323064316230616632340a643961363064336239633661356532 -65333561336666386137326539346637663561633937346232646238316535303966656632353366 -6266356339303263360a626465353639653463313431653230636462353935326139386163643839 -39383261323730653237396538323038623633366264386265386233353439353437633366356234 -62616666373439353239616130323761656431626237653263636135356266373337313666646437 -62346535656237323761663834653061343731633332626662376136383530383638346338653837 -62613638373561373866626538663531353934396466333464373564656530616635616131653037 -37663435613565303364323635663362373862346630626631323866373661313839373962376333 -36326238663063343934353062643465366165393638376166306361616461353365623739663261 -65366639646461376639643138383232343234623831306136613765313938383665656631356464 -38643733646132386666663866363332636662636164666331343936393030643830386162353264 -62313264613366646131666530316164356562633838343463656238656435663661643730356262 -38633134633936306138633434356538346461383163636333633932623966303061643136336332 -64373737303662386335666630353966346363633264326432383262393238366431656337633634 -65653566663365346161346461643164303563393237663138383030326134326536313631313432 -62396532303463353630383663613666353638613735303733323464663339376630366236313432 -32343339396537653931653261363066623565363636626639663937613464623037343234313936 -31306533646661333539323161323934643331373238643730343162303461616530323530323662 -61326562326537343565313039326334316237363865646566653936323933653638623761333434 -38326639316165356137353434633831383434666430393434623132376636613531343333633561 -32333361663835396664656361373236393937353764323061646332643838396232633162306364 -37383464363737326661656139656561343139373535383666303337366534356234666433633162 -61343835313862643066366565323730346463393765333939343036666461323463613439666433 -65343266373934633330656331623932363039616461356432346431366565303663393563373763 -34396439383939633330383330653738616263393433656631623865376634643361363333616364 -36356437636435386363653934626637386364326162333331393733393261623362393434393637 -63333935633863333166313432646463306364626335303539616238343930366232343131393664 -31323636373161623335653033383436303837333562616162653833656363346638633438623031 -64373033653730316237666232653233323438396633623161363531383837353361666431393162 -30616162316631353630393136303239623666633730613962613366336337313739613266353463 -38393461326136343161363234393436623163643837333732613639643534653966636666356565 -31363364343136393438353534663633376531353633663361346232383766303436383066616536 -6463323939363964366632386336616463366531393261303235 +66623361613033616239323361396635633932346566663666346466613132303332663465383037 +3334373161356131653039326630666238303863663034360a633865303562663162303530656436 +36346162613333346133363533343731313965663731336130643333633832663161653437393663 +3936643262306639370a323431343661316564393165386634666635633337356134373862326431 +34633134643062643837316133616138623736306330316538623632633038393038316365316433 +30383363356633613833373738653338376335306361636265303232353838393866353830363434 +37353263353734623161333334316433303061643162363033333466326236373761366636343437 +35343765303462303231373531616362656634313837383631346263333962366634306530353036 +65363033346661323766613961343832323536313339306331626338643336643433333039633666 +63336633386531633632346337393435613338306338363636316138353233366334326435396466 +37363162636539313831323038383535613062373034346437623337393764663133623436356462 +33626361343136376136623964363638303138323330306366353963623962313936666261323334 +61393665313164646165316666333663613163386631643262663233666636386566363565626430 +35633664643438316263663035326636313532333735396537646266373539373063396532323532 +63653262333430663065653838336636616137346538393431306531323234376631616331613437 +65346332383438336137363965643438306564656538316331643566303932353336303931333936 +31306362343464316263316638613831626539323033313031656462373936303534366438383632 +33376562643836303466663437353731343833636630303636376266393866616532636537326130 +32633964396132656135376262316563396338336132313436313533616237656335386162363837 +61666134613830346361666266633538653861326263666565363738383032656536383637666363 +66323135336261376330346633373538383338613765356238323666333266333365646365343563 +33363162633333353234353838623864653862383431396237623065303232343034333735333064 +34636261333465666433363764373934313139306631616234626365306531303232336163363466 +36646666336466663034386634616564626366653963623436343630343263306164313231326166 +31326235346363373664353665666436653532333864303864336535653365623465333764653465 +66303037303839336164373062613836653732393964323634306438656531613364396132353965 +38353766386461663161636139326361663461346262626564326335626339346635356139336238 +64373161346363653661666661323732643236303561356164396136343235613762336264366136 +65333938633433303365646665346263636262303062626634336564616164626563353665396331 +32636164393863336237336331343836373564313935393661646538656539326333656439623363 +30653538346661383061363263326430323730636234346365653835346466666439393466303033 +64343133326664356432326631363234356235313035613134306436613364633836633032326332 +61323861386333653130386637323034336262396433653730323863353230393461316663613339 +37333334336630653037333366666561363332343533323966346536363237373337346439656533 +30633239346463373734666330303934316438663666366435613362336661623530333034616165 +33363336323539383565396337663232393835383734613064393331666135366266633861366333 +63653466356361343732303864373535366433313130303230323334376561363539336638363930 +61353666393366306636333437313736386165303535353534326433326136396637613236626634 +66376239376639393662616161623461643634633930633364333130366136343066376132633934 +66346165353837613837306231383361326236373132353239356164626264366437316639616263 +31623066356231323138303339356138303939343631636337353137663137356562353333376636 +38313066313431386633316339303835363135633434343663636338636365306430643436383430 +66623666663063353439303433386538623762626563323863396335336461646130353466626230 +62393063393936393661636235663730623834626432363563353536353339643135336464323936 +3733 From 412054e3cd041efdb9cc46ab390e559ec40b6b9c Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 18 Jul 2025 21:36:38 +0200 Subject: [PATCH 16/16] restic: Fix extensions to yaml --- roles/restic/defaults/{main.yml => main.yaml} | 0 roles/restic/meta/{main.yml => main.yaml} | 0 roles/restic/tasks/{main.yml => main.yaml} | 0 roles/restic/vars/{main.yml => main.yaml} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename roles/restic/defaults/{main.yml => main.yaml} (100%) rename roles/restic/meta/{main.yml => main.yaml} (100%) rename roles/restic/tasks/{main.yml => main.yaml} (100%) rename roles/restic/vars/{main.yml => main.yaml} (100%) diff --git a/roles/restic/defaults/main.yml b/roles/restic/defaults/main.yaml similarity index 100% rename from roles/restic/defaults/main.yml rename to roles/restic/defaults/main.yaml diff --git a/roles/restic/meta/main.yml b/roles/restic/meta/main.yaml similarity index 100% rename from roles/restic/meta/main.yml rename to roles/restic/meta/main.yaml diff --git a/roles/restic/tasks/main.yml b/roles/restic/tasks/main.yaml similarity index 100% rename from roles/restic/tasks/main.yml rename to roles/restic/tasks/main.yaml diff --git a/roles/restic/vars/main.yml b/roles/restic/vars/main.yaml similarity index 100% rename from roles/restic/vars/main.yml rename to roles/restic/vars/main.yaml