From 9a0bf9bd9bdac08fe6b883a91c247d95e6292c78 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 10 Feb 2026 11:24:15 -0800 Subject: [PATCH 1/3] Add BorgBase offsite backup repository Adds a second borgmatic repository targeting BorgBase (US region) for offsite backups, completing a 3-2-1 backup strategy. SSH key is managed via 1Password and deployed by Ansible. Co-Authored-By: Claude Opus 4.6 --- ansible/playbooks/indri.yml | 16 ++++++++++++++++ ansible/roles/borgmatic/defaults/main.yml | 9 ++++++++- ansible/roles/borgmatic/tasks/main.yml | 14 ++++++++++++++ ansible/roles/borgmatic/templates/config.yaml.j2 | 2 ++ .../feature-borgbase-offsite-backup.feature.md | 1 + 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 docs/changelog.d/feature-borgbase-offsite-backup.feature.md diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index bde198e..3042366 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -22,6 +22,22 @@ no_log: true tags: [borgmatic] + - name: Fetch BorgBase SSH private key + ansible.builtin.command: + cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/noiobufntsxyzageu7mvlp2nbe/ssh-private-key" + delegate_to: localhost + register: _borgbase_ssh_key + changed_when: false + no_log: true + check_mode: false + tags: [borgmatic] + + - name: Set BorgBase SSH key fact + ansible.builtin.set_fact: + borgbase_ssh_private_key: "{{ _borgbase_ssh_key.stdout }}" + no_log: true + tags: [borgmatic] + # Forgejo secrets - name: Fetch forgejo LFS JWT secret ansible.builtin.command: diff --git a/ansible/roles/borgmatic/defaults/main.yml b/ansible/roles/borgmatic/defaults/main.yml index 147a621..293712c 100644 --- a/ansible/roles/borgmatic/defaults/main.yml +++ b/ansible/roles/borgmatic/defaults/main.yml @@ -17,12 +17,19 @@ borgmatic_source_directories: - /Users/erichblume/.config/borgmatic - /Users/erichblume/Documents -# Backup repository +# Backup repositories borgmatic_repositories: - path: /Volumes/backups/borg/ label: sifaka-borg-backups encryption: repokey append_only: true + - path: ssh://k04ljcd7@k04ljcd7.repo.borgbase.com/./repo + label: borgbase-offsite + encryption: repokey + append_only: true + +# BorgBase SSH key (fetched from 1Password in playbook pre_tasks) +borgmatic_borgbase_ssh_key_path: /Users/erichblume/.ssh/borgbase_ed25519 # Exclude patterns borgmatic_exclude_patterns: [] diff --git a/ansible/roles/borgmatic/tasks/main.yml b/ansible/roles/borgmatic/tasks/main.yml index 99eda45..9599622 100644 --- a/ansible/roles/borgmatic/tasks/main.yml +++ b/ansible/roles/borgmatic/tasks/main.yml @@ -19,6 +19,20 @@ mode: '0600' no_log: true +# BorgBase offsite backup - SSH key and host verification +- name: Deploy BorgBase SSH private key + ansible.builtin.copy: + content: "{{ borgbase_ssh_private_key }}\n" + dest: "{{ borgmatic_borgbase_ssh_key_path }}" + mode: '0600' + no_log: true + +- name: Add BorgBase host key to known_hosts + ansible.builtin.known_hosts: + name: k04ljcd7.repo.borgbase.com + key: "k04ljcd7.repo.borgbase.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGU0mISTyHBw9tBs6SuhSq8tvNM8m9eifQxM+88TowPO" + state: present + - name: Deploy borgmatic configuration ansible.builtin.template: src: config.yaml.j2 diff --git a/ansible/roles/borgmatic/templates/config.yaml.j2 b/ansible/roles/borgmatic/templates/config.yaml.j2 index 2e2bf0f..408f31f 100644 --- a/ansible/roles/borgmatic/templates/config.yaml.j2 +++ b/ansible/roles/borgmatic/templates/config.yaml.j2 @@ -31,6 +31,8 @@ exclude_patterns: encryption_passcommand: {{ borgmatic_encryption_passcommand }} +ssh_command: ssh -i {{ borgmatic_borgbase_ssh_key_path }} + # Retention policy keep_daily: {{ borgmatic_keep_daily }} keep_monthly: {{ borgmatic_keep_monthly }} diff --git a/docs/changelog.d/feature-borgbase-offsite-backup.feature.md b/docs/changelog.d/feature-borgbase-offsite-backup.feature.md new file mode 100644 index 0000000..1151e3f --- /dev/null +++ b/docs/changelog.d/feature-borgbase-offsite-backup.feature.md @@ -0,0 +1 @@ +Add BorgBase offsite backup repository for 3-2-1 backup strategy -- 2.50.1 (Apple Git-155) From 40285134ac2a336772482524cac0999a8144d007 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 10 Feb 2026 11:27:56 -0800 Subject: [PATCH 2/3] Fix SSH auth: use IdentitiesOnly to skip agent keys The 1Password SSH agent offers many keys, causing "Too many authentication failures" before the BorgBase key is tried. Co-Authored-By: Claude Opus 4.6 --- ansible/roles/borgmatic/templates/config.yaml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/borgmatic/templates/config.yaml.j2 b/ansible/roles/borgmatic/templates/config.yaml.j2 index 408f31f..9b8da14 100644 --- a/ansible/roles/borgmatic/templates/config.yaml.j2 +++ b/ansible/roles/borgmatic/templates/config.yaml.j2 @@ -31,7 +31,7 @@ exclude_patterns: encryption_passcommand: {{ borgmatic_encryption_passcommand }} -ssh_command: ssh -i {{ borgmatic_borgbase_ssh_key_path }} +ssh_command: ssh -o IdentitiesOnly=yes -i {{ borgmatic_borgbase_ssh_key_path }} # Retention policy keep_daily: {{ borgmatic_keep_daily }} -- 2.50.1 (Apple Git-155) From 4cc41cb9b3e82ba3731055ba2e7ba43cd34c9fcd Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 10 Feb 2026 12:31:40 -0800 Subject: [PATCH 3/3] Disable RunAtLoad for borgmatic LaunchAgent Prevents unintended backup runs when Ansible reloads the LaunchAgent. The StartCalendarInterval (2 AM daily) handles scheduling. Co-Authored-By: Claude Opus 4.6 --- ansible/roles/borgmatic/templates/borgmatic.plist.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/borgmatic/templates/borgmatic.plist.j2 b/ansible/roles/borgmatic/templates/borgmatic.plist.j2 index 75fb0c6..c7da8e8 100644 --- a/ansible/roles/borgmatic/templates/borgmatic.plist.j2 +++ b/ansible/roles/borgmatic/templates/borgmatic.plist.j2 @@ -23,7 +23,7 @@ create RunAtLoad - + StandardErrorPath {{ borgmatic_log_dir }}/mcquack.borgmatic.err.log StandardOutPath -- 2.50.1 (Apple Git-155)