How to restore a backup

This guide has been updated to use the much easier method introduced in version 0.116, if you are running an older version of Home Assistant than this you can check the edit history of this post for the old instructions.

Note: this guide assumes you have copied the backup off your home assistant server before the problem occurred. So backup regularly and copy the backup eleswhere regularly. There are addons to do this automatically for you, see the list here:

https://www.home-assistant.io/common-tasks/os#copying-your-backups-to-another-location

If you have not done this, all is not lost. You may be able to recover the backup but it is beyond the scope of this guide.

1) Fix the issue that caused the crash.

There is little point restoring if it is just going to happen again.

For hardware this may be easy, e.g. replace a pi power supply or SD card.

For configuration issues, if you know what caused the crash you can make a copy of your backup and extract the relevant file using winrar, 7zip or some other compression software that allows decompressing .tar files. Edit the file and return it to the correct place in the backup. If you’ve done something incorrectly you still have the original backup to try again. Never edit this, only edit a copy. Alternatively, restore a backup that is older than when the issue was introduced.

Should you have protected your backup with a password, you may notice that you can’t extract individual files from it. With a little python script (kudos to @Taapie for writing the script → here) you can decrypt your snapshot and then extract individual files as explained above.

This requires Python 3 and the cryptography package.

Create a file called decrypt.py in the same directory as the .tar file you want to decrypt with the following content:

Click to expand!

#!/usr/bin/env python3

import sys

import getopt

import hashlib

import tarfile

import glob

import os

import shutil

from pathlib import Path

from cryptography.hazmat.backends import default_backend

from cryptography.hazmat.primitives.ciphers import (

  Cipher,

  algorithms,

  modes,

)

def _password_to_key(password):

  password = password.encode()

  for _ in range(100):

      password = hashlib.sha256(password).digest()

  return password[:16]

def _generate_iv(key, salt):

  temp_iv = key + salt

  for _ in range(100):

      temp_iv = hashlib.sha256(temp_iv).digest()

  return temp_iv[:16]

class SecureTarFile:

  def __init__(self, filename, password):

      self._file = None

      self._name = Path(filename)

      self._tar = None

      self._tar_mode = "r|gz"

      self._aes = None

      self._key = _password_to_key(password)

      self._decrypt = None

  def __enter__(self):

      self._file = self._name.open("rb")

      cbc_rand = self._file.read(16)

      self._aes = Cipher(

          algorithms.AES(self._key),

          modes.CBC(_generate_iv(self._key, cbc_rand)),

          backend=default_backend(),

      )

      self._decrypt = self._aes.decryptor()

      self._tar = tarfile.open(fileobj=self, mode=self._tar_mode)

      return self._tar

  def __exit__(self, exc_type, exc_value, traceback):

      if self._tar:

          self._tar.close()

      if self._file:

          self._file.close()

  def read(self, size = 0):

      return self._decrypt.update(self._file.read(size))

  @property

  def path(self):

      return self._name

  @property

  def size(self):

      if not self._name.is_file():

          return 0

      return round(self._name.stat().st_size / 1_048_576, 2)  # calc mbyte

def _extract_tar(filename):

  _dirname = '.'.join(filename.split('.')[:-1])

  try:

      shutil.rmtree('_dirname')

  except FileNotFoundError:

      pass

  print(f'Extracting {filename}...')

  _tar  = tarfile.open(name=filename, mode="r")

  _tar.extractall(path=_dirname)

  return _dirname

def _extract_secure_tar(filename, password):

  _dirname = '.'.join(filename.split('.')[:-2])

  print(f'Extracting secure tar {filename.split("/")[-1]}...')

  try:

      with SecureTarFile(filename, password) as _tar:

          _tar.extractall(path=_dirname)

  except tarfile.ReadError:

      print("Unable to extract SecureTar - maybe your password is wrong or the tar is not password encrypted?")

      sys.exit(5)

  return _dirname

def print_usage():

  print(f'{sys.argv[0]} -i <inputfile> -p <password>')

def main():

  _inputfile = None

  _password=None

  try:

      opts, args = getopt.getopt(sys.argv[1:],"hi:p:")

  except getopt.GetoptError:

      print_usage()

      sys.exit(2)

  for opt, arg in opts:

      if opt == '-h':

          print_usage()

          sys.exit()

      elif opt in ("-i"):

          _inputfile = arg

      elif opt in ("-p"):

          _password = arg

  if not _inputfile:

      print ("Missing inputfile")

      print_usage()

      sys.exit(3)

  if not _password:

      print ("Missing password")

      print_usage()

      sys.exit(4)

  _dirname = _extract_tar(_inputfile)

  for _secure_tar in glob.glob(f'{_dirname}/*.tar.gz'):

      _extract_secure_tar(_secure_tar, _password)

      os.remove(_secure_tar)

  print("Done")

if __name__ == "__main__":

  main()

Then execute the following command:


python3 decrypt.py -i name-of-your-file.tar -p yourpassword

You should now have a decrypted archive that you can extract as usual.

2) Flash a fresh copy of home assistant to an SD card, or install using your preferred method

If it has been a while since you have done this the install instructions are here: Installation - Home Assistant alternative installation instructions are available in this community guide category.

3) Boot up home assistant, browse for your backup from the login page and restore

After a bit of time and a restart you should now have home assittant looking exactly like it did when the backup was taken.

Common issues:

The home assistant database or MariaDB database sometimes does not restore correctly. You can chose to repair this (search the forum for instructions) or delete it and start fresh. Home Assistant now has the ability to archive the database and create a new one on start up for the default database if corruption is detected but it does not always work as intended.

9 Likes

Great instructions @tom_l !

Can I just add another option for backups which I use which is the backup to DropBox:

A great addition. Go for it.

Edit: I had some other minor changes to make so I have also added the DropBox backup link.

1 Like

Hi Tom, the snap-shipper add-on now also supports S3 storage.

May I add this to the guidelines on how to restore individual files from password protected snapshots?

1 Like

Please do.

Updated wording from “snapshot” to “backup”.

In my experience it is recommended to recycle power after a fresh install (step2). I had “internal server” errors and this helped me to get rid of these.