| .. | ||
| examples | ||
| src | ||
| Cargo.toml | ||
| IMPLEMENTATION_SUMMARY.md | ||
| INTEGRATION.md | ||
| README.md | ||
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 locationRESTIC_PASSWORD: Encryption passwordRESTIC_PASSWORD_FILE: Password file pathAWS_ACCESS_KEY_ID: AWS credentialsAWS_SECRET_ACCESS_KEY: AWS credentials
For BorgBackup
BORG_REPO: Repository locationBORG_PASSPHRASE: Encryption passwordBORG_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