- name: Configure local backup for Headscale from remote hosts: lapy gather_facts: no vars_files: - ../../infra_vars.yml - ./headscale_vars.yml vars: remote_data_path: "{{ headscale_data_dir }}" remote_config_path: "/etc/headscale" tasks: - name: Debug remote backup vars debug: msg: - "remote_host={{ remote_host }}" - "remote_user={{ remote_user }}" - "remote_data_path='{{ remote_data_path }}'" - "remote_config_path='{{ remote_config_path }}'" - "local_backup_dir={{ local_backup_dir }}" - name: Ensure local backup directory exists file: path: "{{ local_backup_dir }}" state: directory mode: '0755' - name: Ensure ~/.local/bin exists file: path: "{{ lookup('env', 'HOME') }}/.local/bin" state: directory mode: '0755' - name: Create backup script copy: dest: "{{ backup_script_path }}" mode: '0750' content: | #!/bin/bash set -euo pipefail TIMESTAMP=$(date +'%Y-%m-%d') BACKUP_DIR="{{ local_backup_dir }}/$TIMESTAMP" mkdir -p "$BACKUP_DIR" {% if remote_key_file %} SSH_CMD="ssh -i {{ remote_key_file }} -p {{ hostvars[remote_host]['ansible_port'] | default(22) }}" {% else %} SSH_CMD="ssh -p {{ hostvars[remote_host]['ansible_port'] | default(22) }}" {% endif %} # Stop headscale service for consistent backup $SSH_CMD {{ remote_user }}@{{ remote_host }} "sudo systemctl stop headscale" # Backup data directory rsync -az -e "$SSH_CMD" --delete {{ remote_user }}@{{ remote_host }}:{{ remote_data_path }}/ "$BACKUP_DIR/data/" # Backup config directory rsync -az -e "$SSH_CMD" --delete {{ remote_user }}@{{ remote_host }}:{{ remote_config_path }}/ "$BACKUP_DIR/config/" # Start headscale service again $SSH_CMD {{ remote_user }}@{{ remote_host }} "sudo systemctl start headscale" # Rotate old backups (keep 14 days) find "{{ local_backup_dir }}" -maxdepth 1 -type d -name '20*' -mtime +13 -exec rm -rf {} \; - name: Ensure cronjob for backup exists cron: name: "Headscale backup" user: "{{ lookup('env', 'USER') }}" job: "{{ backup_script_path }}" minute: 5 hour: "9,12,15,18" - name: Run the backup script to make the first backup command: "{{ backup_script_path }}"