feat: move restic role to dedicated docker_restic role with S3 support and update documentation

This commit is contained in:
Janik von Rotz committed 2026-10-10 17:50:00 +02:00
1 parent b6c8215a95
commit 326038114a
25 files changed
+199 -95

No files matched your search

+2 -1
View File
@@ -28,6 +28,7 @@
| ![docker_hosts logo](/logos/docker_hosts.png) | [docker_hosts](roles/docker_hosts/README.md) | system | Docker hostname resolver. |
| ![docker_network logo](/logos/docker_network.png) | [docker_network](roles/docker_network/README.md) | system | Configures a Docker network. |
| ![docker logo](/logos/docker.png) | [docker](roles/docker/README.md) | system | Install Docker. |
| ![docker_restic logo](/logos/docker_restic.png) | [docker_restic](roles/docker_restic/README.md) | environment | Runs Restic in Docker container. |
| ![docker_swarm logo](/logos/docker_swarm.png) | [docker_swarm](roles/docker_swarm/README.md) | system | Configures Docker Swarm. |
| ![docker_volume logo](/logos/docker_volume.png) | [docker_volume](roles/docker_volume/README.md) | system | Configures a Docker volume. |
| ![dozzle logo](/logos/dozzle.png) | [dozzle](roles/dozzle/README.md) | application | Deploy Dozzle container. |
@@ -99,7 +100,7 @@
| ![remark42 logo](/logos/remark42.png) | [remark42](roles/remark42/README.md) | application | Deploy Remark42 container |
| ![resolv logo](/logos/resolv.png) | [resolv](roles/resolv/README.md) | system | Manage DNS configuration. |
| ![restic_exporter logo](/logos/restic_exporter.png) | [restic_exporter](roles/restic_exporter/README.md) | service | Add nginx config for Restic exporter path. |
| ![restic logo](/logos/restic.png) | [restic](roles/restic/README.md) | environment | Configures Restic backup jobs. |
| ![restic logo](/logos/restic.png) | [restic](roles/restic/README.md) | environment | Configure restic and backup jobs. |
| ![restic_server logo](/logos/restic_server.png) | [restic_server](roles/restic_server/README.md) | service | Deploy Restic server container. |
| ![s3cmd logo](/logos/s3cmd.png) | [s3cmd](roles/s3cmd/README.md) | environment | Install and configure s3cmd. |
| ![simple_mail_forwarder logo](/logos/simple_mail_forwarder.png) | [simple_mail_forwarder](roles/simple_mail_forwarder/README.md) | service | Deploy Simple Mail Forwarder container. |
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+39
View File
@@ -0,0 +1,39 @@
---
title: "Create task commands for logo scripts."
author: "Janik von Rotz <login@janikvonrotz.ch>"
state: draft
date_completed: YYYY-MM-DD
model:
input_tokens:
output_tokens:
---
# Create task commands for logo scripts.
Note: @Clanker refers to the "ai agent" (you) who is working on this prompt file.
@Clanker when working on this prompt file, make sure to:
- Read context and task section first
- Prepare a list of todos
- Update the todo list while working on task
## Context
@Clanker Read the `AGENTS.md` and `README.md` to get an understanding of the project.
## Task
There are two scripts to update the logos `logos/resize-logos` and `logos/convert-logos`.
I want you to create two new task commands from these scripts.
Then remove the scripts.
I will test the new commands by running `./task resize-logos` and `task convert-logos`.
## Worklog
@Clanker Add a summary here once the task has been completed.
@Clanker Set frontmatter state to completed and update date and model. If you have access to session info also add token count.
+42
View File
@@ -0,0 +1,42 @@
---
kind: environment
---
<img src="/logos/docker_restic.png" alt="docker_restic logo" width="100" height="100">
# Docker Restic role
Runs Restic in Docker container.
## Usage
Configure the role.
```yml
# https://hub.docker.com/r/restic/restic
restic_image: restic/restic:0.19.1
restic_owner: admin # default: root
restic_group: wheel # default: backup
restic_repo_type: s3 # default: s3
restic_s3_endpoint: https://s3.swiss-backup02.infomaniak.com
restic_s3_bucket: my-restic-bucket
restic_s3_region: us-east-1 # default: us-east-1
restic_s3_access_key: # default: "{{ vault_restic_s3_access_key }}"
restic_s3_secret_key: # default: "{{ vault_restic_s3_secret_key }}"
restic_backup_set:
restic_backup_rotation:
daily: 7 # default: 7
weekly: 4 # default: 4
monthly: 1 # default: 1
```
And include it in your playbook.
```yml
- hosts: restic
roles:
- role: restic
```
+9
View File
@@ -0,0 +1,9 @@
---
restic_repo_type: s3
restic_s3_access_key: "{{ vault_restic_s3_access_key }}"
restic_s3_secret_key: "{{ vault_restic_s3_secret_key }}"
restic_backup_rotation:
last: 14
daily: 14
weekly: 4
monthly: 3
+2
View File
@@ -0,0 +1,2 @@
---
dependencies: []
+2
View File
@@ -0,0 +1,2 @@
---
kind: environment
File renamed without changes.
+12
View File
@@ -0,0 +1,12 @@
---
- name: Include {{ role_name }} tasks
ansible.builtin.include_tasks: "{{ role_name }}.yml"
when: restic_repo_type is defined and restic_repo_type == 's3'
tags:
- restic
- name: Include {{ role_name }} backup tasks
ansible.builtin.include_tasks: "{{ role_name }}_backup.yml"
when: restic_backup_set is defined
tags:
- restic
File renamed without changes.
+21 -51
View File
@@ -4,9 +4,10 @@ kind: environment
<img src="/logos/restic.png" alt="restic logo" width="100" height="100">
# Restic role
Configures Restic backup jobs.
Configure restic and backup jobs.
## Usage
@@ -16,10 +17,10 @@ Configure the role.
restic_backup_dir: /srv/backup # default: /var/backup
restic_owner: admin # default: root
restic_group: wheel # default: backup
restic_repo: "restic.example.com/{{ inventory_hostname }}"
restic_repo_password: # default: "{{ vault_restic_repo_password }}"
restic_rest_user: rest-user # default: restic
restic_rest_password: # default "{{ vault_restic_rest_password }}"
restic_repo: "restic.example.com/{{ inventory_hostname }}"
restic_repo_password: # default: "{{ vault_restic_repo_password }}"
restic_backup_set:
- id: "Backup Jenkins volume"
@@ -42,7 +43,7 @@ restic_backup_set:
hour: "1"
- id: "Backup Bookstack files"
type: file
type: file-backup
path: /usr/share/bookstack01
tags:
- bookstack
@@ -60,7 +61,7 @@ restic_backup_set:
hour: "2"
- id: "Backup Postgres database"
type: postgres-dump
type: docker-postgres-dump
container: postgres01
databases: odoo
tags:
@@ -70,7 +71,7 @@ restic_backup_set:
disabled: true
- id: "Backup Postgres databases"
type: postgres-dump
type: docker-postgres-dump
container: postgres01
tags:
- postgres
@@ -96,7 +97,7 @@ restic_backup_set:
hour: "1"
- id: "Backup MariaDB database"
type: mariadb-dump
type: docker-mariadb-dump
container: mariadb01
databases: frappe
tags:
@@ -120,6 +121,18 @@ And include it in your playbook.
## Docs
### Backup types
These backup types are available:
* docker-mariadb-dump
* dokcer-mysql-dump
* docker-odoo-backup
* docker-postgres-dump
* docker-volume
* file-backup
* odoo-backup
### Show restic version
Show restic version for alls hosts.
@@ -137,34 +150,6 @@ restic_repo_type: local # default: rest
restic_repo: /home/backup
```
### Setup an S3 repository
Restic runs in a Docker container for S3 repositories. The credentials are
stored in `/etc/restic/restic.env` (`0600`, root only) and passed to the
container with `--env-file`. The wrapper script `/usr/local/bin/restic-docker`
contains no secrets.
Set these vars:
```yaml
# https://hub.docker.com/r/restic/restic
restic_image: restic/restic:0.19.1
restic_repo_type: s3 # default: rest
restic_s3_endpoint: https://s3.swiss-backup02.infomaniak.com
restic_s3_bucket: my-restic-bucket
restic_s3_region: us-east-1 # default: us-east-1
restic_s3_access_key: # default: "{{ vault_restic_s3_access_key }}"
restic_s3_secret_key: # default: "{{ vault_restic_s3_secret_key }}"
```
Store the secrets in the vault:
```yaml
vault_restic_repo_password: "..."
vault_restic_s3_access_key: "..."
vault_restic_s3_secret_key: "..."
```
### Delete all snaphots
Run these commands to remove all snapshots:
@@ -174,21 +159,6 @@ restic forget --keep-last 1 --prune
restic forget --prune latest
```
### Backup types
These backup types are available:
* mariadb-dump
* mysql-dump
* postges-dump
* docker-volume
* docker-odoo-backup
* odoo-backup
* file
* docker-restic-backup
## Docs
### Change backup path
The backup paths can be changed manually. Ensure that you have set `restic_backup_dir: /mnt/db/backup`.
@@ -252,4 +222,4 @@ If your confident and did a test with a single host, run this command to replace
```bash
task run all -i inventories/setup -b -m shell -a 'if [ -f /var/spool/cron/root ] && grep -q " restic " /var/spool/cron/root; then sed -i "s/ restic / restic-main /g" /var/spool/cron/root; fi; if [ -f /var/spool/cron/crontabs/root ] && grep -q " restic " /var/spool/cron/crontabs/root; then sed -i "s/ restic / restic-main /g" /var/spool/cron/crontabs/root; fi'
```
```
-2
View File
@@ -6,8 +6,6 @@ restic_repo_type: rest
restic_rest_user: restic
restic_rest_password: "{{ vault_restic_rest_password }}"
restic_repo_password: "{{ vault_restic_repo_password }}"
restic_s3_access_key: "{{ vault_restic_s3_access_key }}"
restic_s3_secret_key: "{{ vault_restic_s3_secret_key }}"
restic_backup_rotation:
last: 14
daily: 14
+6
View File
@@ -29,6 +29,12 @@
tags:
- restic
- name: Include {{ role_name }} docker odoo backup tasks
ansible.builtin.include_tasks: "{{ role_name }}_odoo_backup.yml"
when: restic_backup_set is defined
tags:
- restic
- name: Include {{ role_name }} docker postgres backup tasks
ansible.builtin.include_tasks: "{{ role_name }}_docker_postgres_backup.yml"
when: restic_backup_set is defined
@@ -1,4 +1,10 @@
---
- name: Verify docker-mysql-backup command exists
ansible.builtin.command: command -v docker-mysql-backup
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "docker-mysql-backup"
- name: Register selective docker mariadb backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
@@ -9,7 +15,7 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "mariadb-dump" and item.databases is defined
when: item.type == "docker-mariadb-dump" and item.databases is defined
- name: Register docker mariadb backup jobs
ansible.builtin.cron:
@@ -21,4 +27,4 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "mariadb-dump" and item.databases is undefined
when: item.type == "docker-mariadb-dump" and item.databases is undefined
@@ -1,4 +1,10 @@
---
- name: Verify docker-mysql-backup command exists
ansible.builtin.command: command -v docker-mysql-backup
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "docker-mysql-backup"
- name: Register selective docker mysql backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
@@ -9,7 +15,7 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "mysql-dump" and item.databases is defined
when: item.type == "docker-mysql-dump" and item.databases is defined
- name: Register docker mysql backup jobs
ansible.builtin.cron:
@@ -21,4 +27,4 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "mysql-dump" and item.databases is undefined
when: item.type == "docker-mysql-dump" and item.databases is undefined
@@ -0,0 +1,19 @@
---
- name: Verify docker-odoo-backup command exists
ansible.builtin.command: command -v docker-odoo-backup
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "docker-odoo-backup"
- name: Register odoo backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
hour: "{{ item.hour }}"
minute: "{{ 59 | random(seed=item.id) }}"
job: docker-odoo-backup -d {{ item.database }} -c {{ item.container }} -o {{ restic_backup_dir }} -f tar {{ '&& restic-main backup ' + restic_backup_dir +
'/' + item.database + '.tar --tag ' + (item.tags | join(' --tag ')) if item.upload | default(true) else '' }}; write-node-exporter-metric -c '{{ item.id
}}' -v $?
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "docker-odoo-backup"
@@ -1,4 +1,10 @@
---
- name: Verify docker-postgres-backup command exists
ansible.builtin.command: command -v docker-postgres-backup
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "docker-postgres-dump"
- name: Register selective docker postgres backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
@@ -10,7 +16,7 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "postgres-dump" and item.databases is defined
when: item.type == "docker-postgres-dump" and item.databases is defined
- name: Register docker postgres backup jobs
ansible.builtin.cron:
@@ -22,4 +28,4 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "postgres-dump" and item.databases is undefined
when: item.type == "docker-postgres-dump" and item.databases is undefined
@@ -1,4 +1,10 @@
---
- name: Verify docker-volume-backup command exists
ansible.builtin.command: command -v docker-volume-backup
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "docker-volume"
- name: Register docker multi volume backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
+1 -7
View File
@@ -1,10 +1,4 @@
---
- name: Verify file path exists
ansible.builtin.command: ls {{ item.path }}
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "file"
- name: Register file backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
@@ -15,4 +9,4 @@
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "file"
when: item.type == "file-backup"
+2 -15
View File
@@ -1,6 +1,6 @@
---
- name: Verify odoo backup command exists
ansible.builtin.shell: ls $(which odoo-backup)
- name: Verify odoo-backup command exists
ansible.builtin.command: command -v odoo-backup
changed_when: false
loop: "{{ restic_backup_set }}"
when: item.type == "odoo-backup"
@@ -17,16 +17,3 @@
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "odoo-backup"
- name: Register odoo backup jobs
ansible.builtin.cron:
name: "{{ item.id }}"
hour: "{{ item.hour }}"
minute: "{{ 59 | random(seed=item.id) }}"
job: docker-odoo-backup -d {{ item.database }} -c {{ item.container }} -o {{ restic_backup_dir }} -f tar {{ '&& restic-main backup ' + restic_backup_dir +
'/' + item.database + '.tar --tag ' + (item.tags | join(' --tag ')) if item.upload | default(true) else '' }}; write-node-exporter-metric -c '{{ item.id
}}' -v $?
disabled: "{{ item.disabled | default(false) }}"
state: "{{ item.state | default('present') }}"
loop: "{{ restic_backup_set }}"
when: item.type == "docker-odoo-backup"
+12 -13
View File
@@ -25,13 +25,13 @@ help-table() {
rows+=("===DOCS===| | ")
rows+=("build-vuepress||Create vuepress build.")
rows+=("dev-vuepress||Start vuepress development server.")
rows+=("docs||Update docs.")
rows+=("update-docs||Update docs.")
rows+=("install-vuepress||Install build dependencies.")
rows+=("serve-vuepress||Serve vuepress build.")
rows+=("update-roles-doc||Update readme files in roles folder.")
rows+=("format-role-readmes||Update readme files in roles folder.")
rows+=("===LLM===| | ")
rows+=("commit-with-llm||Commit with llm generated commit message.")
rows+=("update-with-llm|[glob][prompt]|Feed files matching glob with prompt to LLM and apply file changes.")
rows+=("update-with-llm|[glob][prompt]|Feed files matching and prompt to LLM and apply file changes.")
rows+=("===DOCKER===| | ")
rows+=("login-docker|[user][token]|Run Docker login and create docker config.")
rows+=("===VAULT===| | ")
@@ -41,7 +41,7 @@ help-table() {
rows+=("encrypt-string|[vault_id] [key] [value] -q|Generate encrypted string with Ansible Vault.")
rows+=("generate-encrypted-string|[vault_id] [key] -q|Generate encrypted string for key with Ansible Vault.")
rows+=("generate-vault-password|[vault_id] [password]|Generate a password file for Ansible Vault.")
rows+=("show-vault-file|[vault_id] [path]|Show content of encrypted strings or fully encrypted vault file.")
rows+=("show-vault-file|[vault_id] [path]|Show content of encrypted strings or encrypted vault file.")
rows+=("===SETUP===| | ")
rows+=("install||Install Ansible and dependencies with uv.")
rows+=("install-ansible-build-scripts|[role]|Install scripts of the specified Ansbile role.")
@@ -67,7 +67,7 @@ help-table() {
rows+=("upgrade-odoo|[env][step]|Run the Odoo upgrade for a target environment.")
rows+=("===TEST===| | ")
rows+=("test-project|[cleanup]|Deploy to localhost. Pass option 'cleanup' to reset test env.")
rows+=("test-scripts|[cleanup]|Install and test scripts. Pass option 'cleanup' to reset test env.")
rows+=("test-scripts|[cleanup]|Install and test scripts. Pass option 'cleanup' to reset env.")
for row in "${rows[@]}"; do
IFS='|' read -r cmd opt desc <<< "$row"
@@ -747,12 +747,12 @@ test-scripts() {
# Docs
docs() {
update-docs() {
echo "Update roles.md"
echo "# Roles" > "roles.md"
echo "" >> "roles.md"
echo "| Logo | Role | Kind | Description |" >> "roles.md"
echo "| --- | --- | --- | --- |" >> "roles.md"
echo "# Roles" > "docs/roles.md"
echo "" >> "docs/roles.md"
echo "| Logo | Role | Kind | Description |" >> "docs/roles.md"
echo "| --- | --- | --- | --- |" >> "docs/roles.md"
local md_files=$(find roles -name 'README.md' | sort)
for readme in $md_files; do
@@ -762,11 +762,11 @@ docs() {
kind=$(grep "^kind:" "$readme" | sed 's/kind: //')
local description
description=$(sed -n '5,$p' "$readme" | grep -v '^$' | grep -v '<img' | grep -v '^#' | head -1)
echo "| ![$role logo](/logos/$role.png) | [$role]($readme) | $kind | $description |" >> "roles.md"
echo "| ![$role logo](/logos/$role.png) | [$role]($readme) | $kind | $description |" >> "docs/roles.md"
done
}
update-roles-doc() {
format-role-readmes() {
local md_files=$(find roles -name 'README.md' | sort)
for readme in $md_files; do
local role
@@ -850,7 +850,6 @@ build-vuepress() {
cp -r roles/nginx/files/* .vuepress/dist/nginx
cp -r roles/odoo/files/* .vuepress/dist/odoo
cp -r roles/postgres/files/* .vuepress/dist/postgres
}
serve-vuepress() {