Commit the accumulated infrastructure work that was living only in the working tree: monitoring stack, emergency access/bot, gyro allocator, grimmory, adguard, backup audit and the OpenCode agent definitions. Also ignore Python bytecode, local archives and Nix/direnv artifacts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
220 lines
6.9 KiB
YAML
220 lines
6.9 KiB
YAML
---
|
|
- name: Install offsite backup packages
|
|
ansible.builtin.apt:
|
|
name:
|
|
- restic
|
|
- rclone
|
|
- sqlite3
|
|
- ca-certificates
|
|
state: present
|
|
update_cache: true
|
|
|
|
- name: Ensure rclone config directory exists
|
|
ansible.builtin.file:
|
|
path: /root/.config/rclone
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0700"
|
|
|
|
- name: Install rclone config
|
|
ansible.builtin.copy:
|
|
src: "{{ offsite_rclone_config_local }}"
|
|
dest: /root/.config/rclone/rclone.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|
|
|
|
- name: Ensure restic config directory exists
|
|
ansible.builtin.file:
|
|
path: /etc/restic
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0700"
|
|
|
|
- name: Install restic repository password
|
|
ansible.builtin.copy:
|
|
src: "{{ offsite_restic_password_local }}"
|
|
dest: "/etc/restic/{{ offsite_profile }}.password"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|
|
|
|
- name: Ensure offsite backup directories exist
|
|
ansible.builtin.file:
|
|
path: "{{ item }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0700"
|
|
loop:
|
|
- /etc/homelab-restic
|
|
- /var/lib/homelab-restic
|
|
- /var/log/homelab-restic
|
|
|
|
- name: Install restic exclude file
|
|
ansible.builtin.copy:
|
|
dest: "/etc/homelab-restic/{{ offsite_profile }}.exclude"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
{% for exclude in offsite_excludes %}
|
|
{{ exclude }}
|
|
{% endfor %}
|
|
|
|
- name: Install restic environment file
|
|
ansible.builtin.copy:
|
|
dest: "/etc/homelab-restic/{{ offsite_profile }}.env"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
content: |
|
|
RESTIC_REPOSITORY={{ offsite_repository }}
|
|
RESTIC_PASSWORD_FILE=/etc/restic/{{ offsite_profile }}.password
|
|
RCLONE_CONFIG=/root/.config/rclone/rclone.conf
|
|
HOMELAB_RESTIC_PROFILE={{ offsite_profile }}
|
|
HOMELAB_RESTIC_SOURCE={{ offsite_source_path }}
|
|
HOMELAB_RESTIC_SQLITE_DB={{ offsite_sqlite_db | default('') }}
|
|
HOMELAB_RESTIC_MARIADB_CONTAINER={{ offsite_mariadb_container | default('') }}
|
|
HOMELAB_RESTIC_MARIADB_DATABASE={{ offsite_mariadb_database | default('') }}
|
|
HOMELAB_RESTIC_MARIADB_USER={{ offsite_mariadb_user | default('') }}
|
|
HOMELAB_RESTIC_MARIADB_ENV={{ offsite_mariadb_env | default('') }}
|
|
HOMELAB_RESTIC_MARIADB_DUMP_NAME={{ offsite_mariadb_dump_name | default('') }}
|
|
HOMELAB_RESTIC_EXCLUDE_FILE=/etc/homelab-restic/{{ offsite_profile }}.exclude
|
|
HOMELAB_RESTIC_STAGING=/var/lib/homelab-restic/{{ offsite_profile }}
|
|
HOMELAB_RESTIC_TAGS={{ offsite_backup_tag }}
|
|
HOMELAB_RESTIC_KEEP_DAILY=14
|
|
HOMELAB_RESTIC_KEEP_WEEKLY=8
|
|
HOMELAB_RESTIC_KEEP_MONTHLY=6
|
|
|
|
- name: Install restic offsite backup script
|
|
ansible.builtin.copy:
|
|
dest: /usr/local/sbin/homelab-restic-offsite-backup
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
content: |
|
|
#!/bin/sh
|
|
set -eu
|
|
|
|
if [ "$#" -ne 1 ]; then
|
|
echo "usage: $0 <profile>" >&2
|
|
exit 64
|
|
fi
|
|
|
|
env_file="/etc/homelab-restic/$1.env"
|
|
if [ ! -f "$env_file" ]; then
|
|
echo "missing env file: $env_file" >&2
|
|
exit 66
|
|
fi
|
|
|
|
set -a
|
|
. "$env_file"
|
|
set +a
|
|
|
|
export RESTIC_REPOSITORY RESTIC_PASSWORD_FILE RCLONE_CONFIG
|
|
|
|
mkdir -p "$HOMELAB_RESTIC_STAGING"
|
|
rm -rf "$HOMELAB_RESTIC_STAGING"/*
|
|
|
|
if [ -n "${HOMELAB_RESTIC_SQLITE_DB:-}" ] && [ -f "$HOMELAB_RESTIC_SQLITE_DB" ]; then
|
|
sqlite_backup="$HOMELAB_RESTIC_STAGING/$(basename "$HOMELAB_RESTIC_SQLITE_DB")"
|
|
sqlite3 -cmd 'PRAGMA busy_timeout=30000;' "$HOMELAB_RESTIC_SQLITE_DB" ".backup '$sqlite_backup'"
|
|
sqlite3 -cmd 'PRAGMA busy_timeout=30000;' "$sqlite_backup" "PRAGMA integrity_check;" | grep -qx ok
|
|
fi
|
|
|
|
if [ -n "${HOMELAB_RESTIC_MARIADB_CONTAINER:-}" ]; then
|
|
if [ ! -f "$HOMELAB_RESTIC_MARIADB_ENV" ]; then
|
|
echo "missing MariaDB env file: $HOMELAB_RESTIC_MARIADB_ENV" >&2
|
|
exit 66
|
|
fi
|
|
set -a
|
|
. "$HOMELAB_RESTIC_MARIADB_ENV"
|
|
set +a
|
|
dump_tmp="$HOMELAB_RESTIC_STAGING/${HOMELAB_RESTIC_MARIADB_DUMP_NAME}.tmp"
|
|
dump_final="$HOMELAB_RESTIC_STAGING/$HOMELAB_RESTIC_MARIADB_DUMP_NAME"
|
|
docker exec -e MYSQL_PWD="$DB_PASSWORD" "$HOMELAB_RESTIC_MARIADB_CONTAINER" \
|
|
mariadb-dump --user="$HOMELAB_RESTIC_MARIADB_USER" --single-transaction --routines --events \
|
|
--databases "$HOMELAB_RESTIC_MARIADB_DATABASE" > "$dump_tmp"
|
|
test -s "$dump_tmp"
|
|
mv "$dump_tmp" "$dump_final"
|
|
{
|
|
printf '%s\n' 'grimmory_image=grimmory/grimmory:v3.2.4@sha256:dfa7afdfcf25d649fd664497a62385dd00cd9678c37546e182c172e41c8e80cb'
|
|
printf '%s\n' 'mariadb_image=lscr.io/linuxserver/mariadb:11.4.8@sha256:91de7f701bc7fc3a424b81beafca7a7c6c4c5b7c8be6afd2ae148698695c0b0c'
|
|
printf 'created_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
} > "$HOMELAB_RESTIC_STAGING/manifest.txt"
|
|
fi
|
|
|
|
restic snapshots >/dev/null 2>&1 || restic init
|
|
|
|
tag_args=""
|
|
old_ifs="$IFS"
|
|
IFS=,
|
|
for tag in $HOMELAB_RESTIC_TAGS; do
|
|
tag_args="$tag_args --tag $tag"
|
|
done
|
|
IFS="$old_ifs"
|
|
|
|
# shellcheck disable=SC2086
|
|
restic backup "$HOMELAB_RESTIC_SOURCE" "$HOMELAB_RESTIC_STAGING" \
|
|
--exclude-file "$HOMELAB_RESTIC_EXCLUDE_FILE" \
|
|
--one-file-system \
|
|
$tag_args
|
|
|
|
restic forget --prune \
|
|
--keep-daily "$HOMELAB_RESTIC_KEEP_DAILY" \
|
|
--keep-weekly "$HOMELAB_RESTIC_KEEP_WEEKLY" \
|
|
--keep-monthly "$HOMELAB_RESTIC_KEEP_MONTHLY"
|
|
|
|
if [ -n "${HOMELAB_RESTIC_MARIADB_CONTAINER:-}" ]; then
|
|
rm -rf "$HOMELAB_RESTIC_STAGING"/*
|
|
fi
|
|
|
|
- name: Install restic offsite systemd service
|
|
ansible.builtin.copy:
|
|
dest: "/etc/systemd/system/homelab-restic-offsite-{{ offsite_profile }}.service"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
[Unit]
|
|
Description=HomeLab restic offsite backup ({{ offsite_profile }})
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/bin/flock -w 1800 /var/lock/homelab-restic-{{ offsite_profile }}.lock /usr/local/sbin/homelab-restic-offsite-backup {{ offsite_profile }}
|
|
|
|
- name: Install restic offsite systemd timer
|
|
ansible.builtin.copy:
|
|
dest: "/etc/systemd/system/homelab-restic-offsite-{{ offsite_profile }}.timer"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
[Unit]
|
|
Description=Run HomeLab restic offsite backup ({{ offsite_profile }})
|
|
|
|
[Timer]
|
|
OnCalendar={{ offsite_timer_oncalendar }}
|
|
Persistent=true
|
|
RandomizedDelaySec=15m
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
|
|
- name: Reload systemd
|
|
ansible.builtin.systemd:
|
|
daemon_reload: true
|
|
|
|
- name: Enable restic offsite timer
|
|
ansible.builtin.systemd:
|
|
name: "homelab-restic-offsite-{{ offsite_profile }}.timer"
|
|
enabled: true
|
|
state: started
|