Skip to main content

Overview

This guide will walk you through creating a complete backup workflow:
  1. Initialize a new repository
  2. Create a backup snapshot
  3. List snapshots
  4. Restore data
By the end, you’ll have a working backup system!

Prerequisites

Make sure you have rustic_core installed:
[dependencies]
rustic_core = "0"
rustic_backend = "0"
simplelog = "0.12"  # For logging output
See the Installation guide if you haven’t set up rustic_core yet.

Complete Example

Here’s a complete working example that demonstrates the full backup lifecycle:
use rustic_backend::BackendOptions;
use rustic_core::{
    BackupOptions, CheckOptions, ConfigOptions, KeyOptions, 
    LocalDestination, LsOptions, PathList, Repository, 
    RepositoryOptions, RestoreOptions, SnapshotOptions
};
use simplelog::{Config, LevelFilter, SimpleLogger};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Enable info-level logging to see what's happening
    let _ = SimpleLogger::init(LevelFilter::Info, Config::default());

    // Step 1: Initialize repository
    init_repository()?;
    
    // Step 2: Create a backup
    create_backup()?;
    
    // Step 3: Restore from backup
    restore_backup()?;
    
    // Step 4: Check repository integrity
    check_repository()?;

    println!("\nBackup workflow completed successfully!");
    Ok(())
}

fn init_repository() -> Result<(), Box<dyn Error>> {
    println!("=== Initializing Repository ===")
    
    // Configure backend (local filesystem in this example)
    let backends = BackendOptions::default()
        .repository("/tmp/my-backup-repo")
        .to_backends()?;
    
    // Set repository password
    let repo_opts = RepositoryOptions::default()
        .password("my-secure-password");
    
    // Configure encryption key
    let key_opts = KeyOptions::default();
    
    // Configure repository settings
    let config_opts = ConfigOptions::default();
    
    // Initialize the repository
    let _repo = Repository::new(&repo_opts, &backends)?
        .init(&key_opts, &config_opts)?;
    
    println!("Repository initialized at /tmp/my-backup-repo\n");
    Ok(())
}

fn create_backup() -> Result<(), Box<dyn Error>> {
    println!("=== Creating Backup ===")
    
    // Open the repository
    let backends = BackendOptions::default()
        .repository("/tmp/my-backup-repo")
        .to_backends()?;
    
    let repo_opts = RepositoryOptions::default()
        .password("my-secure-password");
    
    // Open and prepare for backup operations
    let repo = Repository::new(&repo_opts, &backends)?
        .open()?
        .to_indexed_ids()?;
    
    // Define what to back up
    let source = PathList::from_string(".")?.sanitize()?;
    
    // Configure backup options
    let backup_opts = BackupOptions::default();
    
    // Create snapshot with tags
    let snap = SnapshotOptions::default()
        .add_tags("quickstart,example")?
        .to_snapshot()?;
    
    // Perform the backup
    let snap = repo.backup(&backup_opts, &source, snap)?;
    
    println!("Snapshot created successfully!");
    println!("Snapshot ID: {}", snap.id);
    println!("Paths: {:?}\n", snap.paths);
    
    Ok(())
}

fn restore_backup() -> Result<(), Box<dyn Error>> {
    println!("=== Restoring Backup ===")
    
    // Open repository
    let backends = BackendOptions::default()
        .repository("/tmp/my-backup-repo")
        .to_backends()?;
    
    let repo_opts = RepositoryOptions::default()
        .password("my-secure-password");
    
    let repo = Repository::new(&repo_opts, &backends)?
        .open()?
        .to_indexed()?;
    
    // Get the latest snapshot
    let node = repo.node_from_snapshot_path("latest", |_| true)?;
    
    // List contents
    let streamer_opts = LsOptions::default();
    let ls = repo.ls(&node, &streamer_opts)?;
    
    // Configure restore destination
    let destination = "./restore/";
    let create = true;
    let dest = LocalDestination::new(destination, create, !node.is_dir())?;
    
    // Prepare restore
    let opts = RestoreOptions::default();
    let dry_run = false;
    let restore_infos = repo.prepare_restore(&opts, ls.clone(), &dest, dry_run)?;
    
    // Execute restore
    repo.restore(restore_infos, &opts, ls, &dest)?;
    
    println!("Restore completed to ./restore/\n");
    Ok(())
}

fn check_repository() -> Result<(), Box<dyn Error>> {
    println!("=== Checking Repository ===")
    
    let backends = BackendOptions::default()
        .repository("/tmp/my-backup-repo")
        .to_backends()?;
    
    let repo_opts = RepositoryOptions::default()
        .password("my-secure-password");
    
    let repo = Repository::new(&repo_opts, &backends)?.open()?;
    
    // Check repository integrity
    let opts = CheckOptions::default().trust_cache(true);
    repo.check(opts)?;
    
    println!("Repository check passed!\n");
    Ok(())
}

Step-by-Step Breakdown

Let’s break down each step in detail:
1

Initialize the Repository

The first step is creating a new backup repository:
use rustic_backend::BackendOptions;
use rustic_core::{ConfigOptions, KeyOptions, Repository, RepositoryOptions};

// Configure where to store backups
let backends = BackendOptions::default()
    .repository("/tmp/repo")
    .to_backends()?;

// Set repository password
let repo_opts = RepositoryOptions::default().password("test");

// Initialize
let key_opts = KeyOptions::default();
let config_opts = ConfigOptions::default();
let _repo = Repository::new(&repo_opts, &backends)?
    .init(&key_opts, &config_opts)?;
The repository password encrypts all data. Keep it safe - there’s no way to recover your backups without it!
What happens: This creates the repository structure with encryption keys and configuration files.
2

Open the Repository

Before performing operations, open the repository:
let repo = Repository::new(&repo_opts, &backends)?
    .open()?
    .to_indexed_ids()?;
The to_indexed_ids() method prepares the repository for backup operations by loading the index of existing data blobs.
Different repository states enable different operations:
  • .open() - Basic operations (list snapshots, check)
  • .to_indexed_ids() - Backup operations
  • .to_indexed() - Restore operations
  • .to_indexed_full() - Prune and maintenance
3

Create a Backup

Now create your first snapshot:
use rustic_core::{BackupOptions, PathList, SnapshotOptions};

// Define what to backup
let source = PathList::from_string(".")?.sanitize()?;

// Configure backup
let backup_opts = BackupOptions::default();

// Create snapshot with metadata
let snap = SnapshotOptions::default()
    .add_tags("tag1,tag2")?
    .to_snapshot()?;

// Execute backup
let snap = repo.backup(&backup_opts, &source, snap)?;
println!("Created snapshot: {}", snap.id);
What happens: rustic_core:
  1. Scans the source directory
  2. Chunks files using content-defined chunking
  3. Deduplicates against existing data
  4. Encrypts and compresses new chunks
  5. Writes to the repository
  6. Creates a snapshot pointing to the data
Subsequent backups are incremental - only changed data is stored!
4

List Snapshots

View all snapshots in the repository:
let repo = Repository::new(&repo_opts, &backends)?.open()?;
let snapshots = repo.get_all_snapshots()?;

for snap in snapshots {
    println!("Snapshot {}", snap.id);
    println!("  Time: {}", snap.time);
    println!("  Paths: {:?}", snap.paths);
    println!("  Tags: {:?}", snap.tags);
}
5

Restore Data

Restore from a snapshot:
use rustic_core::{LocalDestination, LsOptions, RestoreOptions};

let repo = Repository::new(&repo_opts, &backends)?
    .open()?
    .to_indexed()?;

// Get latest snapshot
let node = repo.node_from_snapshot_path("latest", |_| true)?;

// List contents
let ls = repo.ls(&node, &LsOptions::default())?;

// Setup destination
let dest = LocalDestination::new("./restore/", true, !node.is_dir())?;

// Restore
let opts = RestoreOptions::default();
let restore_infos = repo.prepare_restore(&opts, ls.clone(), &dest, false)?;
repo.restore(restore_infos, &opts, ls, &dest)?;
Instead of “latest”, you can use:
  • A specific snapshot ID
  • “latest” for the most recent snapshot
  • A tag like “tag:production”

Backend Options

rustic_core supports various storage backends:
let backends = BackendOptions::default()
    .repository("/path/to/repo")
    .to_backends()?;

Password Management

Never hardcode passwords in production code! Use environment variables or secure credential storage.
Better password handling:
use std::env;

// From environment variable
let password = env::var("RUSTIC_PASSWORD")
    .expect("RUSTIC_PASSWORD must be set");

let repo_opts = RepositoryOptions::default()
    .password(&password);
Or use a password file:
let repo_opts = RepositoryOptions::default()
    .password_file("/secure/path/to/password.txt");

Common Operations

Filtering Snapshots

let all_snapshots = repo.get_all_snapshots()?;

// Filter by tag
let production_snaps: Vec<_> = all_snapshots
    .iter()
    .filter(|s| s.tags.contains(&"production".to_string()))
    .collect();

Checking Repository Health

use rustic_core::CheckOptions;

let repo = Repository::new(&repo_opts, &backends)?.open()?;

let opts = CheckOptions::default()
    .trust_cache(true)  // Skip cache verification for speed
    .read_data(true);   // Verify data pack integrity

repo.check(opts)?;

Backup with Progress

use rustic_core::{BackupOptions, ProgressBars};

let progress = ProgressBars::new();
let backup_opts = BackupOptions::default();

let snap = repo.backup(&backup_opts, &source, snap)?;

Error Handling

Use proper error handling in production:
use rustic_core::{RusticError, RusticResult};

fn backup_with_retry() -> RusticResult<()> {
    match repo.backup(&backup_opts, &source, snap) {
        Ok(snap) => {
            println!("Backup successful: {}", snap.id);
            Ok(())
        }
        Err(e) => {
            eprintln!("Backup failed: {}", e);
            Err(e)
        }
    }
}

Next Steps

Now that you have a working backup system, explore more features:

Repository Management

Learn about repository states and configuration

Backup Operations

Advanced backup options and strategies

Restore Operations

Selective restore and recovery options

Maintenance

Prune, check, and optimize your repository
Check out the examples directory in the repository for more complete examples!

Build docs developers (and LLMs) love