Secure remote backup with restic and Backblaze S3 backend

Secure remote backup with restic and Backblaze S3 backend

In this guide, we’ll explore the correct configuration of restic with Backblaze’s S3 backend, distinguishing it from the outdated and less recommended B2 backend prevalent in other online guides.

For those that have never heard of restic, it is a tool that allows secure cloud backup. This means there is no remote infrastructure to manage. All that is needed is an S3 bucket.

This guide primarily focuses on utilizing NixOS; however, its principles can be adapted to other Linux distributions. In such cases, setting up services will require manual intervention, involving the creation of files and direct use of systemctl to enable them and initializing the restic repo manually.

1. Setup

The setup covered in this article will suit the needs of most people. In my other article restic and filesystem snapshots, filesystem snapshots are considered.

1.1. Bucket creation

On Backblaze, create a new bucket for your restic repository. Select a zone that is physically close to you.

1.2. Prepare New Application Key

The Backblaze master application key only works with the B2 backend. You’ll need to generate a new Backblaze application key to use the S3 backend. This information does not seem to be documented on Backblaze’s website and it is what took me very long to get my setup working correctly.

New Application Key button

1.3. Service and timer config

On NixOS, it is very simple:

{ pkgs, userName, ... }: {

  services.restic.backups.the-world = {
    initialize = true;
    user = userName; # root is probably not needed
    environmentFile = "/etc/nixos/hosts/the-world/restic-environment-file";
    repositoryFile = "/etc/nixos/hosts/the-world/restic-repository-file";
    passwordFile = "/etc/nixos/hosts/the-world/restic-password-file";
    pruneOpts = [
      "--keep-daily 7"
      "--keep-weekly 4"
      "--keep-monthly 2"
      "--keep-yearly 0"
    ];
    paths = [
      "/home/${userName}"
      "/unsynced/${userName}/Workspace" # /unsynced and /home are on different zfs data sets since I don't want to include /unsynced in my zfs snapshots. This includes code, due to caches and artefacts taking a lot of space... However, restic can do this thanks to its exclusion list.
      "/etc/nixos" # uncommited changes should be backed up
    ];
    exclude = [
      "/unsynced/${userName}/Workspace/**/build" # Dart/Flutter
      "/unsynced/${userName}/Workspace/**/.dart_tool" # Dart/Flutter
      "/unsynced/${userName}/Workspace/**/target" # Rust (even though "--exclude-caches" should be enough)
      "/home/${userName}/.cache"
      "/home/${userName}/.local/share/waydroid" # permission issues with normal user
      "/home/${userName}/.local/share/Trash"
      "/home/${userName}/.var/app/*/cache"
      "/home/.zfs" # ZFS snapshots
    ];
    backupPrepareCommand = "${pkgs.restic}/bin/restic unlock"; # necessary to prevent locks from persisting indefinitely. See more:
    # https://forum.restic.net/t/restic-unlock-automation/5511
    extraBackupArgs = [
      "--exclude-caches"
    ];
    timerConfig = {
      OnCalendar = "hourly";  # Empty string to disable the timer
      Persistent = true;
      # NB: the option Persistent=true triggers the service
      # immediately if it missed the last start time
    };
  };

}

If you also want to use userName the same way I use it, I defined it in specialArgs (in my flake.nix):

specialArgs = {
  userName = "manuel"; # simplified version of my actual config
  # ...
}

On other Linux distributions you will have to initialize the restic repo manually.

1.4. Repo files

Make sure the following line is added to your .gitignore:

**/restic-*-file

Create these files:

  • /etc/nixos/hosts/YOUR_HOST_NAME/restic-environment-file

    AWS_ACCESS_KEY_ID="your_backblaze_keyID_goes_here"
    AWS_SECRET_ACCESS_KEY="your_backblaze_keyID_goes_here"
    

    NB: do not use B2_ACCOUNT_ID and B2_ACCOUNT_KEY, as they are designed to work with the B2 backend.

  • /etc/nixos/hosts/YOUR_HOST_NAME/restic-repository-file

    s3:https://s3.YOUR_LOCATION.backblazeb2.com/your_backblaze_bucket_name_goes_here
    

    Make sure to enter the right bucket location, for example:

    s3:https://s3.eu-central-003.backblazeb2.com/your_backblaze_bucket_name_goes_here
    

    Do not use the B2 backend that is found in most tutorials, such as:

    b2:your_backblaze_bucket_name_goes_here:/
    
  • /etc/nixos/hosts/YOUR_HOST_NAME/restic-password-file

    your_restic_repo_password_goes_here
    

And then run nixos-rebuild switch with superuser privileges. Now, you can enjoy your restic backups. Make sure to regularly check their status.

2. Status monitoring

2.1. Systemd service

services.restic.backups.the-world generates restic-backups-the-world.service and restic-backups-the-world.timer.

Useful commands:

  • systemctl status restic-backups-the-world.service
  • systemctl restart restic-backups-the-world.service

2.2. Locks

Locks can be listed with restic list locks --no-lock, inspected with restic cat lock lock-UUID and disentangled with restic unlock.

To prevent locks from persisting indefinitely and preventing a fully automated backup strategy, ensure the presence of backupPrepareCommand = "${pkgs.restic}/bin/restic unlock" in your configuration. A discussion about this can be found here: https://forum.restic.net/t/restic-unlock-automation/5511.

3. Restoring

To restore from your backup, run:

restic mount /some/path