/
/
/
Ansible role that can run restic backups and push it to a remote storage server.
1---
2# Restore Workflow
3# Performs restore operation for a single service
4
5- name: Display restore start for {{ item.name }}
6 debug:
7 msg: |
8 Starting restore for {{ item.name }}
9 - Path: {{ item.path }}
10 - Tags: {{ item.tags | join(', ') }}
11 tags: [restore, info]
12
13- name: Stop compose project for restore
14 community.docker.docker_compose_v2:
15 project_src: "{{ item.path }}"
16 project_name: "{{ item.name }}"
17 state: stopped
18 register: _stop_result
19 failed_when: false
20 tags: [restore, stop]
21
22- name: Get latest snapshot for service
23 command: >
24 restic snapshots --tag {{ item.name }} --latest 1 --json
25 environment:
26 RESTIC_REPOSITORY: "{{ restic_repo }}"
27 RESTIC_PASSWORD: "{{ restic_password }}"
28 register: _latest_snapshot
29 changed_when: false
30 tags: [restore, snapshot]
31
32- name: Parse latest snapshot
33 set_fact:
34 _snapshot_id: "{{ (_latest_snapshot.stdout | from_json)[0].id if (_latest_snapshot.stdout | from_json | length > 0) else '' }}"
35 tags: [restore, snapshot]
36
37- name: Fail if no snapshot found
38 fail:
39 msg: "No backup snapshot found for service {{ item.name }} with tag {{ item.name }}"
40 when: _snapshot_id == ''
41 tags: [restore, validation]
42
43- name: Display restore information
44 debug:
45 msg: |
46 Restore Information for {{ item.name }}
47 - Snapshot ID: {{ _snapshot_id }}
48 - Target Path: {{ item.path }}
49 - Service: {{ item.name }}
50 tags: [restore, info]
51
52- name: Remove existing service directory for clean restore
53 file:
54 path: "{{ item.path }}"
55 state: absent
56 tags: [restore, cleanup]
57
58- name: Run restic restore
59 command: >
60 restic restore {{ _snapshot_id }} --target /
61 environment:
62 RESTIC_REPOSITORY: "{{ restic_repo }}"
63 RESTIC_PASSWORD: "{{ restic_password }}"
64 register: _restic_restore
65 become: yes
66 tags: [restore, restic]
67
68- name: Display restore result
69 debug:
70 msg: |
71 Restore completed for {{ item.name }}
72 - Snapshot: {{ _snapshot_id }}
73 - Target: {{ item.path }}
74 tags: [restore, info]
75
76- name: Fix directory permissions after restore
77 file:
78 path: "{{ item.path }}"
79 state: directory
80 owner: "{{ ansible_user_id | default('ansible') }}"
81 group: "{{ ansible_user_gid | default('users') }}"
82 mode: '0755'
83 tags: [restore, permissions]
84
85- name: Start compose project after restore
86 community.docker.docker_compose_v2:
87 project_src: "{{ item.path }}"
88 project_name: "{{ item.name }}"
89 state: present
90 register: _start_result
91 tags: [restore, start]
92
93- name: Wait for containers to be healthy after restore
94 vars:
95 _deadline: "{{ (backup_health_timeout_sec | int) // (backup_health_interval_sec | int) }}"
96 community.docker.docker_container_info:
97 filters:
98 label: "com.docker.compose.project={{ item.name }}"
99 register: _post_containers
100 until: >
101 (
102 (_post_containers.containers | default([])) | selectattr('Health','defined')
103 | map(attribute='Health') | map(attribute='Status') | list
104 ) | difference(['healthy']) | length == 0
105 or
106 (
107 (_post_containers.containers | default([])) | selectattr('Health','defined') | list | length == 0
108 )
109 retries: "{{ _deadline | int | max(1) }}"
110 delay: "{{ backup_health_interval_sec | int }}"
111 when: backup_health_check | bool
112 changed_when: false
113 tags: [restore, health-check]
114
115- name: Display restore completion for {{ item.name }}
116 debug:
117 msg: |
118 Restore workflow completed for {{ item.name }}
119 - Status: SUCCESS
120 - Service restarted: {{ _start_result.changed | default(false) }}
121 - Health check: {{ 'PASSED' if backup_health_check | bool else 'SKIPPED' }}
122 - Snapshot used: {{ _snapshot_id }}
123 tags: [restore, summary]
124