Skip to main content
This example demonstrates how to restore files and directories from a backup snapshot. You can restore entire snapshots or specific files and directories.

Complete Example

use rustic_backend::BackendOptions;
use rustic_core::{
    Credentials, LocalDestination, LsOptions, Repository, RepositoryOptions, RestoreOptions,
};
use simplelog::{Config, LevelFilter, SimpleLogger};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Display info logs
    let _ = SimpleLogger::init(LevelFilter::Info, Config::default());

    // Initialize Backends
    let backends = BackendOptions::default()
        .repository("/tmp/repo")
        .to_backends()?;

    // Open repository
    let repo_opts = RepositoryOptions::default();
    let repo = Repository::new(&repo_opts, &backends)?
        .open(&Credentials::password("test"))?
        .to_indexed()?;

    // use latest snapshot without filtering snapshots
    let node = repo.node_from_snapshot_path("latest", |_| true)?;

    // use list of the snapshot contents using no additional filtering
    let streamer_opts = LsOptions::default();
    let ls = repo.ls(&node, &streamer_opts)?;

    let destination = "./restore/"; // restore to this destination dir
    let create = true; // create destination dir, if it doesn't exist
    let dest = LocalDestination::new(destination, create, !node.is_dir())?;

    let opts = RestoreOptions::default();
    let dry_run = false;
    // create restore infos. Note: this also already creates needed dirs in the destination
    let restore_infos = repo.prepare_restore(&opts, ls.clone(), &dest, dry_run)?;

    repo.restore(restore_infos, &opts, ls, &dest)?;
    Ok(())
}

Step-by-Step Explanation

1. Set up logging

let _ = SimpleLogger::init(LevelFilter::Info, Config::default());
Enable logging to track restore progress, including files being restored and any errors encountered.

2. Configure the backend

let backends = BackendOptions::default()
    .repository("/tmp/repo")
    .to_backends()?;
Connect to the repository backend where your snapshots are stored.

3. Open and index the repository

let repo_opts = RepositoryOptions::default();
let repo = Repository::new(&repo_opts, &backends)?
    .open(&Credentials::password("test"))?
    .to_indexed()?;
Open the repository with authentication and build an index of all data blobs. The to_indexed() method creates an index for efficient data retrieval during restore.

4. Select the snapshot to restore

let node = repo.node_from_snapshot_path("latest", |_| true)?;
Choose which snapshot and path to restore:
  • “latest”: Most recent snapshot (can also use snapshot ID or timestamp)
  • Filter function: |_| true accepts all snapshots; customize to filter by tags, hostname, etc.
You can also specify a path within the snapshot:
let node = repo.node_from_snapshot_path("latest:/path/to/file", |_| true)?;

5. List snapshot contents

let streamer_opts = LsOptions::default();
let ls = repo.ls(&node, &streamer_opts)?;
Generate a list of all files and directories in the snapshot (or selected subtree). This creates a stream of file metadata needed for restore.

6. Configure the destination

let destination = "./restore/";
let create = true;
let dest = LocalDestination::new(destination, create, !node.is_dir())?;
Set up the restore destination:
  • destination: Target directory for restored files
  • create: Create the destination if it doesn’t exist
  • !node.is_dir(): Special handling when restoring a single file

7. Prepare the restore

let opts = RestoreOptions::default();
let dry_run = false;
let restore_infos = repo.prepare_restore(&opts, ls.clone(), &dest, dry_run)?;
Prepare the restore operation:
  • Analyzes what needs to be restored
  • Creates necessary directories in the destination
  • Plans the restore order for optimal performance
  • dry_run: Set to true to see what would be restored without actually doing it

8. Execute the restore

repo.restore(restore_infos, &opts, ls, &dest)?;
Perform the actual restore:
  • Downloads required data chunks from the backend
  • Decrypts and decompresses data
  • Writes files to the destination
  • Restores file permissions and timestamps

Expected Output

During restore, you’ll see progress information:
INFO - opening repository at /tmp/repo
INFO - loading snapshot latest
INFO - snapshot a1b2c3d4 from 2024-03-15T10:30:00Z
INFO - preparing restore to ./restore/
INFO - created 45 directories
INFO - restoring 1,234 files (45.2 MB)
INFO - restored: 100/1234 files (8.1 MB)
INFO - restored: 500/1234 files (22.5 MB)
INFO - restored: 1234/1234 files (45.2 MB)
INFO - restore completed successfully
The restored files will be in the destination directory with their original:
  • File contents
  • Directory structure
  • Permissions and attributes
  • Timestamps (if supported by the filesystem)

Advanced Usage

Restore specific files or directories

// Restore only a specific subdirectory
let node = repo.node_from_snapshot_path("latest:/home/user/documents", |_| true)?;

Filter files during restore

let streamer_opts = LsOptions::default()
    .glob("*.txt")?
    .glob("!*.tmp")?;

Select snapshot by tag or ID

// Filter by tag
let node = repo.node_from_snapshot_path(
    "latest",
    |snap| snap.tags.contains(&"important".to_string())
)?;

// Use specific snapshot ID
let node = repo.node_from_snapshot_path("a1b2c3d4", |_| true)?;

Dry run to preview restore

let dry_run = true;
let restore_infos = repo.prepare_restore(&opts, ls.clone(), &dest, dry_run)?;
// Inspect restore_infos to see what would be restored
println!("Would restore {} files", restore_infos.len());

Error Handling

Common errors and how to handle them:
match repo.restore(restore_infos, &opts, ls, &dest) {
    Ok(_) => println!("Restore completed successfully"),
    Err(e) => {
        eprintln!("Restore failed: {}", e);
        // Possible errors:
        // - Permission denied on destination
        // - Insufficient disk space
        // - Network error accessing backend
        // - Corrupted data in repository
    }
}

Next Steps

Build docs developers (and LLMs) love