provisioning-platform/prov-ecosystem/crates/backup
2026-07-09 22:04:43 +01:00
..
examples platform: clean-start baseline — Rust workspace: binaries/daemon (constellation materialization) 2026-07-09 22:04:43 +01:00
src platform: clean-start baseline — Rust workspace: binaries/daemon (constellation materialization) 2026-07-09 22:04:43 +01:00
Cargo.toml platform: clean-start baseline — Rust workspace: binaries/daemon (constellation materialization) 2026-07-09 22:04:43 +01:00
IMPLEMENTATION_SUMMARY.md platform: clean-start baseline — Rust workspace: binaries/daemon (constellation materialization) 2026-07-09 22:04:43 +01:00
INTEGRATION.md platform: clean-start baseline — Rust workspace: binaries/daemon (constellation materialization) 2026-07-09 22:04:43 +01:00
README.md platform: clean-start baseline — Rust workspace: binaries/daemon (constellation materialization) 2026-07-09 22:04:43 +01:00

Backup Module

Multi-backend backup management library with support for restic, BorgBackup, TAR, CPIO, and rsync.

Overview

The backup module provides a unified interface for managing backups across different backend systems. It enables:

  • Multi-backend support: Use restic, borg, tar, cpio, or rsync for backups
  • Configuration-driven: TOML/YAML/JSON configuration with validation
  • Dependency management: Detect, verify, and guide installation of required tools
  • Schedule automation: Generate schedules for systemd, launchd, cron, and OpenRC
  • Pattern filtering: Include/exclude patterns with glob support for flexible file selection
  • Retention policies: Flexible backup retention with daily/weekly/monthly/yearly granularity
  • Hooks: Pre-backup, post-backup, and error handling hooks
  • Notifications: Configurable backup result notifications
  • Standalone & Integrable: Use as a library or integrate with Provctl/Provisioning

Features by Backend

Feature Restic Borg TAR CPIO Rsync
Incremental
Deduplication
Encryption
Remote Support
Compression

Installation

Add to Cargo.toml:

[dependencies]
backup = "0.1.0"

Quick Start

Basic Configuration

Create backup.toml:

version = "1.0"
name = "my-backup"
description = "Daily backup of application data"
backend = "restic"

[repository]
type = "local"
path = "/mnt/backups/my-app"
password_file = "/etc/backup/password"

[[targets]]
name = "app-data"
paths = ["/var/lib/my-app", "/etc/my-app"]
excludes = ["*.tmp", "*.log"]
tags = ["daily", "production"]

[retention]
keep_daily = 7
keep_weekly = 4
keep_monthly = 12

[schedule]
enabled = true
cron = "0 2 * * *"  # 2 AM daily
init_system = "systemd"

[notifications]
enabled = true
on_failure = true
command = "mail -s 'Backup failed' admin@example.com"

Using the Library

use backup::config::BackupConfig;
use backup::backends::ResticBackend;
use backup::backend::BackupBackend;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load configuration
    let config = BackupConfig::from_toml("backup.toml")?;
    config.validate()?;

    // Create backend and perform backup
    let backend = ResticBackend::new(None);

    if !backend.is_available().await? {
        println!("Backend not available. Install it:");
        let guide = backend.install_guide();
        println!("{}", guide.description);
        return Ok(());
    }

    // Initialize repository
    backend.init_repository(&config.repository).await?;

    // Perform backup
    let target = &config.targets[0];
    let report = backend.backup(target).await?;

    println!("Backup complete: {} files, {} bytes",
             report.files_processed, report.size_processed);

    // Check repository
    let check = backend.check().await?;
    if !check.valid {
        println!("Repository has issues: {:?}", check.issues);
    }

    Ok(())
}

Configuration

Repository Types

  • local: Local filesystem path
  • s3: Amazon S3 bucket
  • sftp: SFTP remote server
  • rest: REST backend server
  • b2: Backblaze B2

Backend Selection

Choose the backend that best fits your needs:

  • Restic: Best for encrypted, incremental backups with deduplication
  • BorgBackup: Excellent for space-efficient deduplication and compression
  • Rsync: Good for fast incremental file synchronization
  • TAR: Simple, standard archive format for basic backups
  • CPIO: Alternative archive format, portable across systems

Retention Policies

Configure how long to keep backups:

[retention]
keep_last = 10         # Keep last 10 snapshots
keep_hourly = 24       # Keep hourly snapshots for 24 hours
keep_daily = 7         # Keep daily snapshots for 7 days
keep_weekly = 4        # Keep weekly snapshots for 4 weeks
keep_monthly = 12      # Keep monthly snapshots for 12 months
keep_yearly = 5        # Keep yearly snapshots for 5 years
keep_tags = ["important", "release"]  # Never remove tagged snapshots

Scheduling

Generate automated backup schedules:

[schedule]
enabled = true
cron = "0 2 * * *"          # 2 AM every day
init_system = "systemd"     # Use systemd timers
on_failure = "notify"       # Send notification on failure

Supported init systems:

  • auto: Auto-detect (systemd on Linux, launchd on macOS)
  • systemd: systemd timers (Linux)
  • launchd: LaunchAgent (macOS)
  • cron: Crontab scheduling
  • openrc: OpenRC init system

Hooks

Execute commands before/after backups:

[hooks]
pre_backup = [
    "systemctl stop my-app",
    "pg_dump mydb > /tmp/db.sql"
]
post_backup = [
    "rm /tmp/db.sql",
    "systemctl start my-app"
]
on_error = [
    "mail -s 'Backup failed' admin@example.com"
]

API

BackupBackend Trait

All backends implement the BackupBackend async trait:

pub trait BackupBackend: Send + Sync {
    fn name(&self) -> &'static str;
    async fn is_available(&self) -> BackupResult<bool>;
    async fn init_repository(&self, repo: &RepositoryConfig) -> BackupResult<()>;
    async fn backup(&self, target: &BackupTarget) -> BackupResult<BackupReport>;
    async fn list_snapshots(&self) -> BackupResult<Vec<Snapshot>>;
    async fn restore(&self, snapshot_id: &str, target_path: &Path, options: &RestoreOptions) -> BackupResult<()>;
    async fn prune(&self, policy: &RetentionPolicy) -> BackupResult<PruneReport>;
    async fn check(&self) -> BackupResult<CheckReport>;
    async fn stats(&self) -> BackupResult<RepositoryStats>;
}

Dependency Checking

use backup::dependency::DependencyChecker;
use backup::backend_type::BackendType;

// Check if backend is available
let available = DependencyChecker::is_available(BackendType::Restic)?;

// Get dependency information
let info = DependencyChecker::get_info(BackendType::Restic)?;
println!("Restic: {} ({})", info.version, info.path);

// Get installation guide
let guide = DependencyChecker::get_install_guide(BackendType::Restic);

Schedule Generation

use backup::schedule::ScheduleGenerator;
use backup::backend_type::InitSystemType;
use std::path::Path;

let schedule = ScheduleGenerator::generate(
    InitSystemType::Systemd,
    "my-backup",
    Path::new("/usr/local/bin/backup.sh"),
    "0 2 * * *",  // 2 AM daily
    "backup"      // username
)?;

println!("{}", schedule);

File Filtering

use backup::filter::FileFilter;
use std::path::Path;

let filter = FileFilter::new(
    vec!["*.rs".to_string()],                    // Include patterns
    vec!["target/*".to_string(), "*.tmp".to_string()]  // Exclude patterns
)?;

if filter.should_include(Path::new("src/main.rs")) {
    println!("File will be included");
}

Examples

Complete Backup Workflow

use backup::config::BackupConfig;
use backup::backends::ResticBackend;
use backup::backend::BackupBackend;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load and validate config
    let config = BackupConfig::from_toml("backup.toml")?;
    config.validate()?;

    // Create backend
    let backend = ResticBackend::new(None);

    // Check availability
    if !backend.is_available().await? {
        eprintln!("Restic not available");
        return Ok(());
    }

    // Initialize repository
    backend.init_repository(&config.repository).await?;

    // Backup all targets
    for target in &config.targets {
        println!("Backing up: {}", target.name);
        let report = backend.backup(target).await?;

        if !report.success {
            eprintln!("Backup failed: {:?}", report.errors);
        } else {
            println!("Backup complete: {} files processed", report.files_processed);
        }
    }

    // Prune old snapshots
    let prune_report = backend.prune(&config.retention).await?;
    println!("Pruned {} snapshots, freed {} bytes",
             prune_report.snapshots_removed, prune_report.space_freed);

    // Verify repository integrity
    let check = backend.check().await?;
    if check.valid {
        println!("Repository is valid");
    } else {
        eprintln!("Repository issues: {:?}", check.issues);
    }

    Ok(())
}

Restore from Snapshot

use backup::backends::ResticBackend;
use backup::backend::BackupBackend;
use backup::types::RestoreOptions;
use std::path::Path;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let backend = ResticBackend::new(None);

    let options = RestoreOptions {
        overwrite: true,
        verify: true,
        restore_permissions: true,
    };

    backend.restore(
        "snapshot-id-here",
        Path::new("/restore/location"),
        &options
    ).await?;

    println!("Restore complete");
    Ok(())
}

Testing

Run tests:

cargo test

Run with logging:

RUST_LOG=debug cargo test -- --nocapture

Environment Variables

For Restic

  • RESTIC_REPOSITORY: Repository location
  • RESTIC_PASSWORD: Encryption password
  • RESTIC_PASSWORD_FILE: Password file path
  • AWS_ACCESS_KEY_ID: AWS credentials
  • AWS_SECRET_ACCESS_KEY: AWS credentials

For BorgBackup

  • BORG_REPO: Repository location
  • BORG_PASSPHRASE: Encryption password
  • BORG_PASSCOMMAND: Command to retrieve password

For Rsync

  • RSYNC_RSH: Remote shell (e.g., "ssh -i /path/to/key")

Error Handling

The crate uses BackupResult<T> for error handling:

use backup::error::BackupResult;

fn some_operation() -> BackupResult<()> {
    // Operation returns BackupResult
    Ok(())
}

// Handle specific errors
match some_operation() {
    Err(backup::error::BackupError::BackendUnavailable(msg)) => {
        eprintln!("Backend not available: {}", msg);
    }
    Err(e) => eprintln!("Error: {}", e),
    Ok(()) => println!("Success"),
}

Performance Considerations

  • Restic: Most feature-rich but slower due to encryption overhead
  • BorgBackup: Excellent compression and deduplication
  • Rsync: Fast for incremental transfers, best for local networks
  • TAR: Simple and fast for full backups, no incremental support
  • CPIO: Portable format, good for cross-system backups

Limitations

  • TAR/CPIO: No incremental backup support; full backup each time
  • CPIO: Limited metadata support compared to restic/borg
  • Rsync: No built-in deduplication
  • Schedule generation: Simple cron conversion; complex expressions need manual adjustment

Contributing

Contributions are welcome. Please ensure:

  • Code follows idioms
  • Tests are included for new features
  • Documentation is updated
  • Examples are provided

License

MIT License - See LICENSE file for details