Skip to main content
A snapshot represents the state of your backed-up data at a specific point in time. Snapshots are the primary way you interact with your backup history in rustic_core.

What is a Snapshot?

A snapshot is metadata that describes:
  • When the backup was taken
  • What was backed up (paths)
  • Where it came from (hostname)
  • How to find the data (tree ID)
  • Who created it (username)
  • Why it exists (tags, labels, description)
Snapshots are stored as JSON files in the repository’s snapshots/ directory. Each snapshot is identified by a unique 32-byte ID.

Snapshot Metadata

The SnapshotFile structure contains comprehensive metadata:
pub struct SnapshotFile {
    /// Timestamp of this snapshot
    pub time: Zoned,
    
    /// The tree blob id where contents are stored  
    pub tree: TreeId,
    
    /// The list of paths contained in this snapshot
    pub paths: StringList,
    
    /// The hostname where snapshot was created
    pub hostname: String,
    
    /// Tags for filtering and organization
    pub tags: StringList,
    
    /// Optional label for the snapshot
    pub label: String,
    
    /// Parent snapshot ID for incremental backups
    pub parent: Option<SnapshotId>,
    
    /// Summary statistics from the backup
    pub summary: Option<SnapshotSummary>,
    
    /// Optional description
    pub description: Option<String>,
    
    // ... additional fields
}

Core Fields

The timestamp when the backup was created. This is the primary sorting field for snapshots.
// Snapshots are ordered by time
impl Ord for SnapshotFile {
    fn cmp(&self, other: &Self) -> Ordering {
        self.time.cmp(&other.time)
    }
}
The ID of the root tree blob containing the snapshot’s directory structure and file references. This is how you access the actual backup data.
let tree = repo.get_tree(&snapshot.tree)?;
List of filesystem paths that were backed up in this snapshot.
snapshot.paths  // StringList(["home/user/docs", "/etc"])
User-defined tags for organizing and filtering snapshots.
let tags = StringList::from_str("important,daily")?;
snapshot.add_tags(vec![tags]);

Snapshot Immutability

Snapshots are immutable once created. You cannot modify an existing snapshot’s data or metadata.
This immutability provides:
  • Integrity: Snapshots cannot be corrupted or tampered with
  • Consistency: What you backed up is what you’ll restore
  • Audit trail: Complete history of all backups
If you need to “modify” a snapshot (e.g., add tags), rustic creates a new snapshot with the updated metadata:
// Modifying creates a new snapshot
pub fn modify(&mut self, modification: &SnapshotModification) -> RusticResult<bool> {
    modification.apply_to(self)  // Returns true if changed
}

// Original snapshot ID stored for reference
pub original: Option<SnapshotId>,

Incremental Snapshots

Snapshots support incremental backups through parent relationships:
1

First Backup

Initial snapshot with no parent backs up all data.
let snapshot = SnapshotFile {
    parent: None,  // No parent
    // ...
};
2

Incremental Backup

Subsequent backups reference the previous snapshot as parent.
let snapshot = SnapshotFile {
    parent: Some(previous_snapshot_id),
    // Only changed files are stored
};
3

Deduplication

Unchanged files reference existing blobs, saving space.rustic_core automatically detects unchanged files by comparing:
  • File size
  • Modification time
  • Inode (when available)
Each snapshot is self-contained and can be restored independently, even if you delete the parent snapshots!

Snapshot Organization

Finding Snapshots

Multiple ways to identify and retrieve snapshots:
Use full or partial snapshot ID:
// Full ID
let snap = repo.get_snapshot_from_str(
    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
    |_| true
)?;

// Partial ID (must be unique)
let snap = repo.get_snapshot_from_str("0123456", |_| true)?;

Filtering Snapshots

Snapshots can be filtered using custom predicates:
// By hostname
let filter = |snap: &SnapshotFile| snap.hostname == "production";
let snaps = repo.get_matching_snapshots(filter)?;

// By tags
let required_tags = vec![StringList::from_str("daily,important")?];
let filter = |snap: &SnapshotFile| snap.tags.matches(&required_tags);

// By time range
let cutoff = Zoned::now().checked_sub(30.days())?;
let filter = |snap: &SnapshotFile| snap.time > cutoff;

Snapshot Summary

The optional SnapshotSummary provides detailed statistics about the backup:
pub struct SnapshotSummary {
    // File statistics
    pub files_new: u64,
    pub files_changed: u64,
    pub files_unmodified: u64,
    pub total_files_processed: u64,
    pub total_bytes_processed: u64,
    
    // Directory statistics  
    pub dirs_new: u64,
    pub dirs_changed: u64,
    pub dirs_unmodified: u64,
    
    // Blob statistics
    pub data_blobs: u64,
    pub tree_blobs: u64,
    pub data_added: u64,              // Uncompressed
    pub data_added_packed: u64,        // Compressed
    
    // Timing
    pub backup_start: Zoned,
    pub backup_end: Zoned,
    pub backup_duration: f64,
}

Example Summary

{
  "total_files_processed": 15234,
  "files_new": 42,
  "files_changed": 156,
  "files_unmodified": 15036,
  "data_added": 2147483648,
  "data_added_packed": 536870912,
  "backup_duration": 45.2
}
This shows good deduplication: 2GB of data compressed to ~512MB.

Snapshot Lifecycle

Creating Snapshots

Snapshots are created during backup operations:
use rustic_core::{SnapshotOptions, SnapshotFile};

// Configure snapshot metadata
let snap_opts = SnapshotOptions {
    label: Some("Daily Backup".to_string()),
    tags: vec![StringList::from_str("daily,automated")?],
    description: Some("Automated daily backup".to_string()),
    ..Default::default()
};

// Create snapshot from options
let snapshot = SnapshotFile::from_options(&snap_opts)?;

Deleting Snapshots

Snapshots can be removed (respecting deletion policies):
// Check if snapshot must be kept
if snapshot.must_keep(&Zoned::now()) {
    println!("Cannot delete: snapshot has deletion protection");
} else {
    repo.delete_snapshots(&[snapshot.id])?;
}
Deleting snapshots only removes the metadata. The actual data blobs are removed during prune operations.

Deletion Policies

Snapshots support deletion policies:
pub enum DeleteOption {
    NotSet,                    // No policy
    Never,                     // Never delete
    After(Zoned),             // Delete after this time
}
Example usage:
// Mark as permanent
let snap_opts = SnapshotOptions {
    delete_never: true,
    ..Default::default()
};

// Auto-delete after 30 days
let snap_opts = SnapshotOptions {
    delete_after: Some(Span::new().days(30)),
    ..Default::default()  
};

Multiple Parents

Snapshots can have multiple parents for advanced scenarios:
pub struct SnapshotFile {
    pub parent: Option<SnapshotId>,     // Single parent (legacy)
    pub parents: Vec<SnapshotId>,       // Multiple parents
}

// Get all parents
let parents = snapshot.get_parents();  // Returns &[SnapshotId]
This enables:
  • Merged snapshots: Combining multiple backup sources
  • Migration: Preserving history when moving repositories

See Also

Repository

Learn about repository structure

Deduplication

How rustic eliminates duplicate data

Encryption

Snapshot security and encryption

Backends

Where snapshots are stored

Build docs developers (and LLMs) love