Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rustic-rs/rustic_core/llms.txt

Use this file to discover all available pages before exploring further.

Repository Management

This guide covers essential repository maintenance operations including checking repository integrity, pruning old data, and repairing issues.

Overview

Proper repository maintenance ensures:
  • Data integrity and consistency
  • Efficient storage usage
  • Fast backup and restore operations
  • Long-term repository health

Check Repository

The check command verifies repository integrity by validating snapshots, index files, and pack files.
1
Basic Check
2
Perform a basic repository check:
3
use rustic_core::{Repository, RepositoryOptions, CheckOptions, Credentials};
use rustic_backend::BackendOptions;

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

let repo = Repository::new(&RepositoryOptions::default(), &backends)?
    .open(&Credentials::password("password"))?;

let opts = CheckOptions::default();
let results = repo.check(opts)?;

// Check if errors were found
results.is_ok()?;
4
Check with Data Verification
5
Read and verify pack file contents:
6
let opts = CheckOptions::default()
    .read_data(true)  // Verify pack file contents
    .read_data_subset(ReadSubsetOption::Percentage(10.0)); // Check 10% of data

let results = repo.check(opts)?;
7
Trust Cache
8
Skip cache verification for faster checks:
9
let opts = CheckOptions::default().trust_cache(true);
let results = repo.check(opts)?;

Check Options

See /home/daytona/workspace/source/crates/core/src/commands/check.rs:183-203

Read Data Subsets

use rustic_core::ReadSubsetOption;

// Check all pack files (slow, thorough)
let opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::All);

// Check 25% of pack files (random sample)
let opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::Percentage(25.0));

// Check specific size of data
let opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::Size(1_000_000_000)); // 1 GB

// Check specific subset by ID
let opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::IdSubSet((1, 5))); // 1st of 5 parts

Time-Based Subset Checking

// Check different subsets on different schedules
let subset = ReadSubsetOption::from_str("hourly/day")?;  // Different hour each day
let subset = ReadSubsetOption::from_str("daily/week")?;  // Different day each week  
let subset = ReadSubsetOption::from_str("weekly/month")?; // Different week each month
let subset = ReadSubsetOption::from_str("monthly/year")?; // Different month each year

let opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(subset);

Prune Repository

Pruning removes unused data and optimizes repository storage.
1
Create Prune Plan
2
First, create a plan to see what will be done:
3
use rustic_core::PruneOptions;

let prune_opts = PruneOptions::default();
let plan = repo.prune_plan(&prune_opts)?;

// Review statistics
println!("Prune Plan Statistics:");
println!("Packs to delete: {}", plan.stats.packs_to_delete.remove);
println!("Packs to repack: {}", plan.stats.packs.repack);
println!("Packs to keep: {}", plan.stats.packs.keep);
println!("Size to delete: {} bytes", plan.stats.size_to_delete.remove);
4
Execute Prune
5
Execute the prune plan:
6
// Execute the prune plan
repo.prune(&prune_opts, plan)?;

Prune Options

See /home/daytona/workspace/source/crates/core/src/commands/prune.rs:52-134

Repack Limits

use rustic_core::LimitOption;

// Limit repacking to 5% of repository size
let opts = PruneOptions::default()
    .max_repack(LimitOption::Percentage(5));

// Limit repacking to specific size
let opts = PruneOptions::default()
    .max_repack(LimitOption::Size(ByteSize::gb(2)));

// No limit on repacking
let opts = PruneOptions::default()
    .max_repack(LimitOption::Unlimited);

Maximum Unused Data

// Tolerate up to 10% unused data
let opts = PruneOptions::default()
    .max_unused(LimitOption::Percentage(10));

// Tolerate up to 1 GB unused data  
let opts = PruneOptions::default()
    .max_unused(LimitOption::Size(ByteSize::gb(1)));

Pack Retention

use jiff::Span;

// Keep packs at least 7 days before repacking
let opts = PruneOptions::default()
    .keep_pack(Span::new().days(7));

// Keep packs marked for deletion for 24 hours before removing
let opts = PruneOptions::default()
    .keep_delete(Span::new().hours(24));

Instant Delete

// Delete files immediately instead of marking them
// WARNING: Only use if no parallel processes access the repository!
let opts = PruneOptions::default()
    .instant_delete(true)
    .early_delete_index(true); // Delete index files early to save space

Repack Strategies

// Repack all packs (maximum compression)
let opts = PruneOptions::default().repack_all(true);

// Repack only uncompressed blobs (for v2 repos)
let opts = PruneOptions::default().repack_uncompressed(true);

// Fast repacking (copy blobs without re-encrypting)
let opts = PruneOptions::default().fast_repack(true);

// Only repack cacheable packs (for hot/cold repos)
let opts = PruneOptions::default()
    .repack_cacheable_only(Some(true));

// Don't resize packs, only remove unused data
let opts = PruneOptions::default().no_resize(true);

Repair Operations

Repair Index

Repair corrupted or missing index files:
use rustic_core::RepairIndexOptions;

let repair_opts = RepairIndexOptions::default()
    .read_all(true); // Read all pack files to rebuild index

repo.repair_index(&repair_opts)?;

Repair Snapshots

Repair or delete broken snapshots:
use rustic_core::RepairSnapshotsOptions;

// Repair snapshots by removing broken entries
let repair_opts = RepairSnapshotsOptions::default();
repo.repair_snapshots(&repair_opts)?;

Repair Hot/Cold Repository

For hot/cold backend setups:
// Repair hot/cold repository consistency
repo.repair_hotcold()?;

// Repair specific packs in hot storage
repo.repair_hotcold_packs(&pack_ids)?;

Repository Information

Get Repository Statistics

// Get index information
let index_infos = repo.infos_index()?;
println!("Index files: {}", index_infos.len());

// Get file statistics  
let file_infos = repo.infos_files()?;
println!("Total snapshots: {}", file_infos.snapshots);
println!("Total pack files: {}", file_infos.packs);
println!("Total size: {} bytes", file_infos.total_size);

Maintenance Best Practices

Regular Check Schedule

// Daily: Quick check without data verification
let daily_opts = CheckOptions::default().trust_cache(true);
repo.check(daily_opts)?;

// Weekly: Check 10% of data
let weekly_opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::Percentage(10.0));
repo.check(weekly_opts)?;

// Monthly: Full data verification
let monthly_opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::All);
repo.check(monthly_opts)?;

Prune After Forget

Always prune after deleting snapshots:
use rustic_core::SnapshotFilter;

// Delete old snapshots
let filter = SnapshotFilter::default()
    .keep_daily(7)
    .keep_weekly(4)
    .keep_monthly(6);

repo.forget(&filter)?;

// Prune to reclaim space
let prune_opts = PruneOptions::default();
let plan = repo.prune_plan(&prune_opts)?;
repo.prune(&prune_opts, plan)?;

Verify Before Important Operations

// Always check before major operations
fn safe_prune(repo: &Repository<Open>) -> Result<(), Box<dyn std::error::Error>> {
    // 1. Check repository integrity
    println!("Checking repository...");
    let check_opts = CheckOptions::default()
        .read_data(true)
        .read_data_subset(ReadSubsetOption::Percentage(5.0));
    repo.check(check_opts)?.is_ok()?;
    
    // 2. Create prune plan
    println!("Creating prune plan...");
    let prune_opts = PruneOptions::default();
    let plan = repo.prune_plan(&prune_opts)?;
    
    // 3. Review plan
    println!("Will delete {} packs", plan.stats.packs_to_delete.remove);
    println!("Will repack {} packs", plan.stats.packs.repack);
    
    // 4. Execute prune
    println!("Executing prune...");
    repo.prune(&prune_opts, plan)?;
    
    // 5. Verify after prune
    println!("Verifying repository...");
    repo.check(check_opts)?.is_ok()?;
    
    println!("Prune completed successfully!");
    Ok(())
}

Monitor Repository Growth

use rustic_core::BlobType;

fn analyze_repository(repo: &Repository<Open>) -> Result<(), Box<dyn std::error::Error>> {
    let prune_opts = PruneOptions::default();
    let plan = repo.prune_plan(&prune_opts)?;
    
    let stats = plan.stats;
    let size_sum = stats.size_sum();
    
    println!("Repository Analysis:");
    println!("Total size: {} bytes", size_sum.total());
    println!("Used: {} bytes ({:.1}%)", 
        size_sum.used,
        100.0 * size_sum.used as f64 / size_sum.total() as f64
    );
    println!("Unused: {} bytes ({:.1}%)",
        size_sum.unused,
        100.0 * size_sum.unused as f64 / size_sum.total() as f64
    );
    println!("Can be removed: {} bytes", size_sum.remove);
    println!("Should be repacked: {} bytes", size_sum.repack);
    
    println!("\nPack Statistics:");
    println!("Used packs: {}", stats.packs.used);
    println!("Partly used: {}", stats.packs.partly_used);
    println!("Unused packs: {}", stats.packs.unused);
    println!("To repack: {}", stats.packs.repack);
    
    Ok(())
}

Troubleshooting

Handle Check Failures

let results = repo.check(CheckOptions::default())?;

if let Err(e) = results.is_ok() {
    eprintln!("Repository check found errors:");
    for (level, error) in &results.0 {
        match level {
            CheckErrorLevel::Error => eprintln!("  ERROR: {}", error),
            CheckErrorLevel::Warn => eprintln!("  WARN: {}", error),
        }
    }
    
    // Attempt repair
    eprintln!("\nAttempting to repair index...");
    repo.repair_index(&RepairIndexOptions::default().read_all(true))?;
    
    // Check again
    let results = repo.check(CheckOptions::default())?;
    results.is_ok()?;
}

Recover from Corruption

// 1. Repair index files
repo.repair_index(&RepairIndexOptions::default().read_all(true))?;

// 2. Repair snapshots
repo.repair_snapshots(&RepairSnapshotsOptions::default())?;

// 3. Run full check
let check_opts = CheckOptions::default()
    .read_data(true)
    .read_data_subset(ReadSubsetOption::All);
repo.check(check_opts)?.is_ok()?;

// 4. Prune to remove corrupted data
let prune_opts = PruneOptions::default();
let plan = repo.prune_plan(&prune_opts)?;
repo.prune(&prune_opts, plan)?;

See Also

Build docs developers (and LLMs) love