Automated nightly backups to S3

I want to backup my HA config every night and upload it to S3. I’m surprised this isn’t a supported feature out of the box, and bewildered by how difficult it was to setup.

An automation starts the backup, waits a minute, then calls a shell command which runs a bash script inside the SSH addon to copy the latest backup to /share. That file copy fires a folder_watcher event, which triggers an automation to upload this file to S3 using minio addon.

The SSH workaround is because automations run inside the homeassistant Docker container, which does not have access to /backup. The SSH addon has access to both places, so it can copy the tar file to /share.

There is no way to know the backup filename directly, that’s why I have to use the folder_watcher event.

To setup SSH access you need to generate a key pair inside the homeassistant container and store it somewhere outside the home directory so that it persists across software updates. Then add the public key to the SSH addon.

ssh-keygen -t ecdsa -f /config/shell_scripts/id_ecdsa

This is my shell script at /config/shell_scripts/move_backup_to_share.sh

#!/bin/bash
mkdir -p /share/backups
cp /backup/$(ls -t /backup/ | head -1) /share/backups/

In configuration.yaml:

shell_command:
  copy_backup_to_share: >
    ssh -o UserKnownHostsFile=/config/shell_scripts/known_hosts 
    root@localhost 
    -i /config/shell_scripts/id_ecdsa 
    '/config/shell_scripts/copy_backup_to_share.sh'

folder_watcher:
  - folder: '/share/backups'
    patterns:
      - '*.tar'

And automations.yaml:

- alias: Utility - nightly backup
  trigger:
  - platform: time
    at: 03:00:00
  action:
  - service: hassio.backup_partial
    data:
      homeassistant: true
  - delay:
      minutes: 1
  - service: shell_command.copy_backup_to_share
  mode: single

- alias: Utility - upload backups to S3
  description: 'run when tar is finished copying to /share/backups'
  trigger:
  - platform: event
    event_type: folder_watcher
    event_data:
      event_type: closed
  action:
  - service: notify.mobile_app_pro_llama
    data:
      title: System backup at {{ now().strftime('%H:%M:%S') }}
      message: '{{ trigger.event.data.path }}'
  - service: minio.put
    data:
      bucket: my-bucket
      file_path: '{{ trigger.event.data.path }}'
      key: backups/{{ trigger.event.data.file }}
  mode: single

You can vote here to make this easier: