This project was born from a desire for a backup tool that is fast, reliable, and simple. It prioritizes robust, best-practice engineering over fancy features that could compromise solidity. The goal is to provide everything you need for solid, performant, cross-platform backups—no more, no less.
pgl-backup is a simple, powerful, and robust file backup utility written in Go. It is designed for creating versioned backups of local directories to another local or network-attached drive. It supports periodic snapshots, an efficient incremental mode with a flexible retention policy, and automatic compression. A core design goal is ensuring backups can be restored no matter what; by relying on standard file structures and open archive formats, your data remains fully accessible via native OS tools without needing pgl-backup installed.
- Core Concepts
- Key Features
- Installation
- Security and Binary Verification
- Getting Started: A 3-Step Guide
- Usage and Examples
- Automation
- How to Restore a Backup
- Cross-Platform Backups and Case-Sensitivity
- Exclusion Rules
- Log Levels
- Retention Policy
- Configuration Details
- Troubleshooting
- Contributing
- Acknowledgments
- License
- Reliability: Pre-flight checks validate paths, permissions, and configuration before a single byte is moved.
- Transparency: No proprietary containers. Backups are stored exactly as they appear in the source or within standard archives.
- Performance: A high-performance, concurrent sync engine designed for modern hardware and high-latency network shares.
- Safety: Built-in protection against "ghost" mount points, permission lockouts, and case-sensitivity mismatches.
- Backup Modes:
- Incremental (default): Maintains a single "current" backup directory that is efficiently updated. At a configurable interval (e.g., daily), the "current" backup is rolled over into a timestamped archive. This is ideal for frequent, low-overhead backups.
- Snapshot: Each backup run creates a new, unique, timestamped directory. This is useful for creating distinct, point-in-time copies.
- Zero Vendor Lock-in: Your data is stored in standard directories and open archive formats (
.zip,.tar.gz,.tar.zst). You can always access and restore your files using native operating system tools, even withoutpgl-backupinstalled. - Flexible Retention Policy: Automatically cleans up outdated backups by keeping a configurable number of hourly, daily, weekly, monthly, and yearly archives. This gives you a fine-grained history without filling up your disk.
- Intelligent Archiving (Incremental Mode): The archive interval can be set to
auto(the default) to automatically align with your retention policy. If you keep hourly backups, it will archive hourly. This prevents configuration mismatches and ensures your retention slots are filled efficiently. - Automatic and Resilient Compression: To save disk space, you can enable automatic compression of backups into
.tar.zst,.zipor.tar.gzarchives. If compression fails (e.g., due to a corrupt file), the original backup is left untouched. - Concurrency Safe: A robust file-locking mechanism prevents multiple backup instances from running against the same target directory simultaneously, protecting data integrity.
- Symbolic Link Support: Preserves symbolic links in the destination. On Windows, creating symlinks requires the user to have the appropriate privileges (Run as Administrator) or Developer Mode enabled.
- Plug and Play: The binary is static and self-contained. You can run it directly from an external drive without installation, making it perfect for portable backup workflows.
- Pre- and Post-Backup Hooks: Execute custom shell commands before the sync begins or after it completes, perfect for tasks like dumping a database or sending a notification.
- Adjustable Configuration: Configure backups using a simple
pgl-backup.config.jsonJSON file, and override any setting with command-line flags for one-off tasks. - High performance Sync Engine:
native: A high-performance, concurrent, cross-platform engine written in pure Go that offers the best performance with no external dependencies.
- Safety First:
- Pre-flight Checks: Validates source and target paths, permissions, and configuration before starting any file operations to fail fast and provide clear errors.
- Dry Run Mode: The
-dry-runflag lets you see exactly what files would be copied, updated, or deleted without making any actual changes. - Permission Lockout Protection: When copying files and directories,
pgl-backupautomatically ensures the backup user has write permissions on the destination, even if the source is read-only. This prevents the backup process from locking itself out on subsequent runs. - Consistent User Execution: While
pgl-backupincludes features to prevent permission lockouts, it is still a best practice to run all backups for a specific target as the same user. This ensures consistent file ownership and avoids potential permission issues on Unix-like systems. - "Ghost Directory" Protection: On Unix-like systems, it helps prevent accidentally backing up to a mount point when the external drive is not actually mounted.
- Cross-Platform Safety: Detects and halts on risky backup scenarios, such as backing up a case-sensitive Linux source from a case-insensitive Windows host, which can lead to silent data loss.
- Path Nesting Protection: Prevents infinite recursion loops by ensuring source and target directories are not nested within each other.
Ensure you have Go installed (version 1.26+ recommended).
If you have a Go environment set up, you can install pgl-backup directly:
go install github.com/paulschiretz/pgl-backup@latestYou can download the latest pre-compiled binaries for your operating system from the Releases page.
As an open-source project, the pre-compiled binaries provided in the Releases section are currently unsigned. This means that operating systems like macOS and Windows may flag them as "unrecognized" or "untrusted" when you try to run them for the first time.
This is expected behavior for unsigned software. You can verify the integrity of the downloaded files using the provided checksums.txt file, or build the project from source if you prefer.
Note: At the current stage, binaries are unsigned to avoid the high costs of code signing certificates for a free open-source tool. If this is a significant issue for you, please open an issue on GitHub. I am willing to sign binaries in the future if there is sufficient demand to justify the cost.
When you first run pgl-backup on macOS, you may see a popup saying it "cannot be opened because the developer cannot be verified."
- Locate the
pgl-backupbinary in Finder. - Right-click (or Control-click) the file and select Open.
- Click Open in the dialog box that appears.
- You only need to do this once.
Alternatively, you can remove the quarantine attribute via the terminal:
xattr -d com.apple.quarantine pgl-backupWindows Defender SmartScreen may prevent the application from starting.
- Click More info in the popup window.
- Click the Run anyway button.
Every release includes a checksums.txt file containing the SHA256 hashes of the artifacts. You can verify that your download has not been tampered with or corrupted.
Linux / macOS:
# Download the checksums.txt and the archive to the same directory
shasum -a 256 -c checksums.txt
# Or manually check a specific file:
shasum -a 256 pgl-backup_v1.3.4_darwin_arm64.tar.gz
# Compare output with the content of checksums.txtWindows (PowerShell):
Get-FileHash .\pgl-backup_v1.3.4_windows_amd64.zip -Algorithm SHA256
# Compare the hash with the content of checksums.txtLet's set up a daily incremental backup for your ~/Documents folder to an external drive.
The easiest way to get started is to use the init command. This will generate a pgl-backup.config.json file in your target directory.
- New Backups: It creates the directory and a default configuration file. The
-baseand-sourceflags are required. - Existing Backups: It reads your existing configuration, applies any new flags you provide (like changing the log level), and saves the updated config. It preserves your retention policies and other settings. Note that
-sourceis required to perform pre-flight checks. - Fresh Start: If you want to completely overwrite an existing configuration with defaults, use
init -defaultinstead.
# Example for Linux/macOS
pgl-backup init -base="/media/backup-drive/MyDocumentsBackup" -source="$HOME/Documents"
# Example for Windows
pgl-backup init -base="E:\Backups\MyDocumentsBackup" -source="C:\Users\YourUser\Documents"You can also combine the init command with other flags to customize the configuration immediately:
# Example for Linux/macOS
pgl-backup init -base="/media/backup-drive/MyDocumentsBackup" -source="$HOME/Documents" -log-level=debug -user-exclude-files="*.mp4"
# Example for Windows
pgl-backup init -base="E:\Backups\MyDocumentsBackup" -source="C:\Users\YourUser\Documents" -log-level=debug -user-exclude-files="*.mp4"This command will:
- Perform pre-flight checks to ensure the paths are valid.
- Create the target directory
/media/backup-drive/MyDocumentsBackup. - Generate a
pgl-backup.config.jsonfile inside it.
Open the newly created pgl-backup.config.json file. It will look something like this, filled with sensible defaults. You can adjust the retention policy, archive policy, or add exclusions.
{
"version": "1.3.4",
"logLevel": "info",
"paths": {
"incremental": {
"current": "PGL_Backup_Incremental_Current",
"archive": "PGL_Backup_Incremental_Archive",
"content": "PGL_Backup_Content",
"archiveEntryPrefix": "PGL_Backup_"
},
"snapshot": {
"current": "PGL_Backup_Snapshot_Current",
"archive": "PGL_Backup_Snapshot_Archive",
"content": "PGL_Backup_Content",
"archiveEntryPrefix": "PGL_Backup_"
}
},
"engine": {
"metrics": true,
"failFast": false,
"performance": {
"syncWorkers": 4,
"mirrorWorkers": 4,
"deleteWorkers": 4,
"compressWorkers": 4,
"bufferSizeKB": 128,
"readAheadLimitKB": 262144
}
},
"sync": {
"enabled": true,
"preserveSourceDirName": true,
"engine": "native",
"disableSafeCopy": false,
"retryCount": 3,
"retryWaitSeconds": 5,
"modTimeWindowSeconds": 1,
"defaultExcludeFiles": [
"*.tmp",
"*.temp",
"*.swp",
"*.lnk",
"~*",
"desktop.ini",
".DS_Store",
"Thumbs.db",
"Icon\r"
],
"defaultExcludeDirs": [
"@tmp",
"@eadir",
".SynologyWorkingDirectory",
"#recycle",
"$Recycle.Bin"
],
"userExcludeFiles": [],
"userExcludeDirs": []
},
"archive": {
"enabled": true,
"intervalMode": "auto",
"intervalSeconds": 86400
},
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 0,
"weeks": 4,
"months": 0,
"years": 0
},
"snapshot": {
"enabled": false,
"hours": -1,
"days": -1,
"weeks": -1,
"months": -1,
"years": -1
}
},
"compression": {
"enabled": true,
"format": "tar.zst",
"level": "default"
},
"hooks": {
"preBackup": [],
"postBackup": [],
"preRestore": [],
"postRestore": []
}
}Now, simply point pgl-backup at the target directory and provide the source. It will automatically load the configuration file from the base directory and run the backup. The -source flag is mandatory.
# Run the backup
pgl-backup backup -base="/media/backup-drive/MyDocumentsBackup" -source="$HOME/Documents"Command Structure:
-base(Required): The root directory of your backup repository (wherepgl-backup.config.jsonis located).-source(Required): The source directory where the files you want to backup are stored.
The first run will copy all files into a PGL_Backup_Content subdirectory inside the main PGL_Backup_Incremental_Current directory.
Subsequent runs will efficiently update the contents of PGL_Backup_Incremental_Current/PGL_Backup_Content. After 1 week (by default) or your configured interval, the next run will first rename the entire PGL_Backup_Incremental_Current directory to a timestamped archive (e.g., PGL_Backup_2023-10-27-...) inside the PGL_Backup_Incremental_Archive sub-directory, and then create a new, clean PGL_Backup_Incremental_Current for the next sync.
When compression is enabled, the PGL_Backup_Content subdirectory within an archive is compressed, and the original subdirectory is removed, leaving the compressed file alongside the backup's metadata.
Your backup target will be organized like this:
/path/to/your/backup-target/
├── pgl-backup.config.json (The configuration for this backup set)
├── PGL_Backup_Incremental_Current/ (The current incremental backup)
├── PGL_Backup_Incremental_Archive/ (Timestamped historical incremental backups)
└── PGL_Backup_Snapshot_Archive/ (Timestamped backups from snapshot mode)
pgl-backup uses a subcommand structure: pgl-backup <command> [flags]. Running pgl-backup without any arguments will display the help message.
Once configured, this is the only command you need for your backup script, cron job or scheduled task.
pgl-backup backup -base="/path/to/your/backup-target" -source="/path/to/your/source-data"You can store the pgl-backup binary directly on your external backup drive alongside your data. This allows you to plug the drive into any computer and run backups immediately without installing any software.
For example, you can place the binary and a simple script on your external drive:
Directory Structure:
X:\ (External Drive)
├── pgl-backup.exe (Windows binary)
├── pgl-backup (Linux/macOS binary)
├── run_backup.bat
├── run_backup.sh
└── MyBackups\
Windows (run_backup.bat):
:: Set working directory to where the .bat file is
cd /d "%~dp0"
:: Run pgl-backup using a relative path for the base
pgl-backup.exe backup -base=".\MyBackups" -source="C:\Users\YourUser\Documents"
pauseLinux / macOS (run_backup.sh):
#!/bin/sh
cd "$(dirname "$0")"
./pgl-backup backup -base="./MyBackups" -source="$HOME/Documents"See what changes would be made without touching any files.
pgl-backup backup -base="/path/to/your/backup-target" -source="/path/to/your/source-data" -dry-runHere's how to perform a single snapshot backup.
pgl-backup backup -base="/path/to/your/backup-target" -source="/path/to/your/source-data" -mode=snapshotExclude temporary files and node_modules directories. Patterns support standard file globbing.
pgl-backup backup -base="..." -source="..." -user-exclude-files="*.tmp,*.log" -user-exclude-dirs="node_modules,.cache"Note on Matching: All exclusion patterns are case-insensitive on all operating systems. A pattern like *.jpeg will match photo.jpeg, photo.JPEG, and photo.JpEg. This ensures your configuration is portable and behaves predictably across Windows, macOS, and Linux.
Run a script before the backup starts. Commands with spaces must be wrapped in single or double quotes.
pgl-backup backup -base="..." -source="..." -pre-backup-hooks="'/usr/local/bin/dump_database.sh', 'echo Backup starting...'"Security Note: Hooks execute arbitrary shell commands. Ensure that any commands in your configuration are from a trusted source and have the correct permissions to prevent unintended side effects.
Manually apply retention policies to clean up outdated backups without running a full backup. This is useful for freeing up disk space immediately. Supports -dry-run to preview deletions.
pgl-backup prune -base="/path/to/your/backup-target"Command Structure:
-base(Required): The root directory of your backup repository (wherepgl-backup.config.jsonis located).-mode: Specify the backup mode (incrementalorsnapshot). If omitted,pgl-backupautomatically prunes incremental and snapshot backups.
View a list of all backups stored in the repository, including their timestamps, modes, and UUIDs. This is useful for finding the specific name of a backup you want to restore.
pgl-backup list -base="/path/to/your/backup-target"Command Structure:
-base(Required): The root directory of your backup repository (wherepgl-backup.config.jsonis located).-mode: Specify the backup mode (incrementalorsnapshot). If omitted,pgl-backupautomatically lists incremental and snapshot backups.-sort: Sort order for the list (descorasc). Defaults todesc(newest first).
You can easily automate your backups using your operating system's built-in scheduler.
- Open Task Scheduler and click Create Basic Task.
- Name it "Daily Backup" and click Next.
- Choose your trigger (e.g., Daily) and set the time.
- Select Start a program as the action.
- Program/script: Browse to your
pgl-backup.exe. - Add arguments: Enter your backup command flags.
backup -base="D:\Backups" -source="C:\Users\YourName\Documents" - Finish the wizard.
Edit your crontab file (crontab -e) and add a line to run the backup daily (e.g., at 2:00 AM):
0 2 * * * /usr/local/bin/pgl-backup backup -base="/mnt/backup-drive" -source="/home/yourname/Documents" >> /var/log/pgl-backup.log 2>&1If your backup target is an external drive and it is not connected when the scheduled task runs, pgl-backup will detect this during its pre-flight checks and exit safely with an error. No data will be lost, and the backup will simply be skipped until the next scheduled run when the drive is available.
pgl-backup provides two ways to restore your data: using the built-in restore command for convenience, or by manually accessing your files with standard OS tools. The manual method guarantees you can always recover your data, even without pgl-backup installed.
The restore command is the easiest and safest way to restore a backup. It automatically handles both compressed archives and uncompressed directories.
The restore command copies data from a specific backup to a target directory.
pgl-backup restore -base="/path/to/backup-repo" -target="/path/to/restore-location"Command Structure:
-base(Required): The root directory of your backup repository (wherepgl-backup.config.jsonis located).-target(Required): The directory where you want to restore the files.-uuid: The UUID of the backup you want to restore. If omitted (the default),pgl-backuplaunches an easy-to-use interactive menu where you can browse and select a backup from a list.-mode: Filter the interactive list by backup mode (incrementalorsnapshot), or restrict the search when-uuidis provided. If omitted (any),pgl-backuplists all backups or searches both locations.-overwrite: Control overwrite behavior (always,never,if-newer,update). If omitted, defaults tonever, so existing files are not overwritten.-fail-fast: Stop immediately on the first error. If omitted, defaults tofalse.
Examples:
1. Interactive Restore (Default & Easiest):
Simply omit the -uuid flag to see a list of all available backups and select one by number.
pgl-backup restore -base="/media/backup-drive/MyDocumentsBackup" \
-target="$HOME/RestoredDocuments"The list displays the backup type:
- [INC]: Incremental backup.
- [SNP]: Snapshot backup
2. Restore a specific backup by UUID:
This finds the backup with the specified UUID, automatically decompresses it if needed, and restores its contents. You can find the UUID using the list command.
pgl-backup restore -base="/media/backup-drive/MyDocumentsBackup" \
-target="$HOME/RestoredFromSnapshot" \
-uuid="123e4567-e89b-12d3-a456-426614174000" \3. Restore the latest backup: Automatically selects and restores the most recent backup.
pgl-backup restore -base="/media/backup-drive/MyDocumentsBackup" \
-target="$HOME/RestoredFromSnapshot" \
-uuid="latest" \4. Restore a specific archived snapshot: This finds the backup with the specified UUID inside the snapshot archive directory, automatically decompresses it if needed, and restores its contents.
pgl-backup restore -base="/media/backup-drive/MyDocumentsBackup" \
-target="$HOME/RestoredFromSnapshot" \
-uuid="123e4567-e89b-12d3-a456-426614174000" \
-mode="snapshot"Overwrite Behavior:
By default, the restore command will never overwrite existing files in the target directory (-overwrite=never). You can change this behavior:
-overwrite=if-newer: Only overwrite if the file in the backup is newer.-overwrite=always: Always overwrite existing files.
A core design philosophy of pgl-backup is zero vendor lock-in. Your data is always accessible.
Navigate to your backup target directory. You will see the following structure:
PGL_Backup_Incremental_Current/: Contains the most recent version of your files within thePGL_Backup_Contentsubdirectory. You can browse this directory directly and copy files out using your file explorer.PGL_Backup_Incremental_Archive/: Contains compressed historical incremental backups (e.g.,PGL_Backup_2023-10-27-14-00-00...tar.zst), if compression is disabled all your files are within thePGL_Backup_Contentsubdirectory.PGL_Backup_Snapshot_Archive/: Contains point-in-time snapshots if you use snapshot mode. If compression is enabled (e.g.,PGL_Backup_2023-10-27-14-00-00...tar.zst), if compression is disabled all your files are within thePGL_Backup_Contentsubdirectory.
If you need to restore files from a compressed archive in PGL_Backup_Incremental_Archive or PGL_Backup_Snapshot_Archive, use the standard tools for your operating system.
Tip: For a consistent graphical experience across platforms, or if you prefer not to use the command line, tools like 7-Zip (Windows) or PeaZip (Windows, Linux, macOS) can handle
.zip,.tar.gz, and.tar.zstarchives effortlessly.
- Right-click the
.zipfile. - Select Extract All....
- Choose a destination and click Extract.
You can double-click the file to extract it, or use the terminal:
# Extract the entire archive
tar -xvf PGL_Backup_2023-10-27.tar.gz
# Extract a specific file
tar -xvf PGL_Backup_2023-10-27.tar.gz "path/to/file.txt"The .tar.zst format uses Zstandard compression for high speed and ratio. You may need to install zstd (e.g., brew install zstd or apt install zstd).
# Extract the entire archive
tar --zstd -xvf PGL_Backup_2023-10-27.tar.zst
# Extract a specific file
tar --zstd -xvf PGL_Backup_2023-10-27.tar.zst "path/to/file.txt"pgl-backup includes a critical safety check to prevent data loss from filesystem case-sensitivity mismatches.
- The Problem: Backing up a case-sensitive source (like a Linux filesystem with both
File.txtandfile.txt) from a case-insensitive host (like Windows) is dangerous. The host OS may only "see" one of the files, leading to silent data loss as the other is never backed up. - The Solution:
pgl-backupautomatically tests the case-sensitivity of both the source and the host environment. If it detects this risky combination, it will stop with a clear error message before the backup begins. - Recommendation: For maximum data integrity when backing up a case-sensitive source (like a Linux server or a WSL environment), you should always run
pgl-backupon a case-sensitive operating system (like Linux, or from within WSL on Windows).
pgl-backup provides a flexible exclusion system to keep your backups clean and efficient.
All exclusion patterns are case-insensitive on all operating systems. A pattern like *.jpeg will match photo.jpeg, photo.JPEG, and photo.JpEg. This ensures your configuration is portable and behaves predictably across Windows, macOS, and Linux.
To provide a better out-of-the-box experience and protect its own metadata, pgl-backup uses two layers of exclusions.
A small, non-configurable list of files are always excluded to prevent the backup tool from backing up its own operational files. These are:
pgl-backup.config.json.pgl-backup.meta.json.~pgl-backup.lock
A list of common temporary and system files are excluded by default. When you generate a new configuration file with -init, these defaults are included, and you can customize them in your pgl-backup.config.json file.
The JSON keys for these are defaultExcludeFiles and defaultExcludeDirs.
- Default Excluded Files:
*.tmp,*.temp,*.swp,*.lnk,~*,desktop.ini,.DS_Store,Thumbs.db,Icon\r. - Default Excluded Directories:
@tmp,@eadir,.SynologyWorkingDirectory,#recycle,$Recycle.Bin.
You can define your own list of files and directories to exclude using the userExcludeFiles and userExcludeDirs keys in the configuration file, or via the command-line flags -user-exclude-files and -user-exclude-dirs. These are combined with the system and default exclusions.
pgl-backup uses a structured logging system with several levels of verbosity, allowing you to control how much detail you see. You can set the level using the -log-level flag or the logLevel key in your configuration file.
error: Only shows critical errors that cause the backup process to halt. Use this if you only want to be alerted to complete failures.warn: Shows errors and warnings. Warnings are non-critical issues that the application has recovered from but that you should be aware of (e.g., a single file failed to copy, a configuration mismatch).info(Default): Provides a high-level summary of the backup process. It shows the start, periodic progress, and end of major phases like synchronization, compression, and retention cleanup. This is the recommended level for daily use and cron jobs, as it provides a clean, readable overview.notice: Shows everything frominfoplus detailed, per-item operational messages. Use this level to see a log of every file being copied, deleted, archived, or compressed. It's useful for verifying that specific files are being handled correctly without the full verbosity ofdebug.debug: The most verbose level. Includes everything fromnoticeplus detailed developer-oriented information for tracing execution flow and diagnosing complex issues.
The retention policy is designed to give you a detailed short-term history and a space-efficient long-term history. pgl-backup supports separate retention policies the Incremental mode archive and Snapshot mode archive.
- Incremental Retention: Designed for your routine backup history. Since incremental backups run frequently (e.g., daily), this policy thins them out over time to save space. It is enabled by default.
- Snapshot Retention: Designed for manual milestones (e.g., "Pre-Upgrade"). By default, this policy is disabled, meaning snapshots are kept forever. If you enable it, you must explicitly set retention periods to
0or greater (defaults are-1for safety).
Both policies work using a "promotion" system. When cleaning up old backups, pgl-backup scans all your archived backups from newest to oldest and decides which ones to keep.
Here's how it works for each backup, in order of priority:
- Hourly: Is there an open "hourly" slot? If yes, keep this backup and move to the next one.
- Daily: If not kept as hourly, is there an open "daily" slot for this backup's calendar day? If yes, keep it and move on.
- Weekly: If not kept as daily, is there an open "weekly" slot for this backup's calendar week? If yes, keep it and move on.
- Monthly: If not kept as weekly, is there an open "monthly" slot? If yes, keep it and move on.
- Yearly: If not kept as monthly, is there an open "yearly" slot? If yes, keep it.
If a backup doesn't fill any available slot, it is deleted. This ensures that a backup is only "promoted" to a longer-term slot (like weekly) if it's no longer needed to fill a shorter-term slot (like daily).
Here are a few example policies you can use in your pgl-backup.config.json file.
The best policy depends on how much data you are backing up and how much disk space you have available. For many use cases, a simple policy of keeping 4 weekly backups is more than enough.
Goal: A minimal policy for users who need a basic safety net without using much disk space. It provides a few recent recovery points. Total Backups Stored: ~4 (2 daily + 1 weekly + 1 monthly) Auto Archive Interval Sets To: 24 Hours
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 2,
"weeks": 1,
"months": 1,
"years": 0
}
}Goal: A simple, "set-it-and-forget-it" policy that provides a month of history without using excessive disk space. This is the default policy when you first initialize pgl-backup.
Total Backups Stored: ~4 (4 weekly)
Auto Archive Interval Sets To: 1 Week
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 0,
"weeks": 4,
"months": 0,
"years": 0
}
}Goal: You mostly care about the current state (which is always available in PGL_Backup_Incremental_Current) but want one previous snapshot just in case.
Total Backups Stored: ~1 (1 monthly)
Auto Archive Interval Sets To: ~30 Days
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 0,
"weeks": 0,
"months": 1,
"years": 0
}
}Goal: For data that changes slowly. You only create an archive once a month. Total Backups Stored: ~12 (12 monthly) Auto Archive Interval Sets To: 30 Days
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 0,
"weeks": 0,
"months": 12,
"years": 0
}
}Goal: Protect active work where recent recovery is critical. The priority is being able to undo a mistake made recently. Total Backups Stored: ~6 (3 hourly + 3 daily) Auto Archive Interval Sets To: 1 Hour
"retention": {
"incremental": {
"enabled": true,
"hours": 3,
"days": 3,
"weeks": 0,
"months": 0,
"years": 0
}
}Goal: A robust policy for important data. It balances a reasonable safety net (3 days) with a solid historical archive (3 months). Total Backups Stored: ~10 (3 daily + 4 weekly + 3 monthly) Auto Archive Interval Sets To: 24 Hours
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 3,
"weeks": 4,
"months": 3,
"years": 0
}
}Goal: For data where you might need to go back many years, but fine-grained daily history isn't needed. Total Backups Stored: ~11 (4 weekly + 7 yearly) Auto Archive Interval Sets To: 1 Week
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 0,
"weeks": 4,
"months": 0,
"years": 7
}
}Goal: Backing up terabytes of movies/photos where data rarely changes and storage costs are high. You just want a few recent versions in case of accidental deletion. Total Backups Stored: ~3 (1 weekly + 1 monthly + 1 yearly) Auto Archive Interval Sets To: 1 Week
"retention": {
"incremental": {
"enabled": true,
"hours": 0,
"days": 0,
"weeks": 1,
"months": 1,
"years": 1
}
}backup: Run the backup operation. Use-helpto see all the options.restore: Run the restore operation. Use-helpto see all the options.list: List all backups in the base directory. Use-helpto see all the options.prune: Apply retention policies to clean up outdated backups. Use-helpto see all the options.init: Initialize or update a configuration. Use-defaultto overwrite an existing configuration with defaults.version: Print the application version.
All command-line flags can also be set in the pgl-backup.config.json file. Note that structural options (like directory paths) are only available in the configuration file to ensure consistency.
| Flag / JSON Key | Type | Default | Description |
|---|---|---|---|
version |
version |
"" |
The pgl-backup version. |
base / base (internal) |
string |
"" |
The base directory where backups are stored. Required. |
source (backup/init) / - |
string |
"" |
The directory to back up. Required for Backup and Init. |
target (restore) / - |
string |
"" |
The destination directory for a restore operation. Required for Restore. |
uuid (restore) / - |
string |
"" |
The UUID of the backup you want to restore. |
mode / runtime.mode (internal) |
string |
"incremental" |
Backup mode: "incremental" or "snapshot". Defaults to "any" for list/prune/restore. |
sort / runtime.listSort (internal) |
string |
"desc" |
Sort order for list command: "desc" or "asc". |
overwrite (backup) / runtime.backupOverwriteBehavior (internal) |
string |
"update" |
Overwrite behavior for backup: 'always', 'never', 'if-newer', 'update'. |
overwrite (restore) / runtime.restoreOverwriteBehavior (internal) |
string |
"never" |
Overwrite behavior for restore: 'always', 'never', 'if-newer', 'update'. |
fail-fast / engine.failFast |
bool |
false |
If true, stops the backup immediately on the first file sync error. |
ignore-case-mismatch / runtime.ignoreCaseMismatch (internal) |
bool |
false |
Bypass the case-sensitivity safety check (use with caution). |
default / - |
false |
Used with init command. Overwrite existing configuration with defaults. |
|
force / - |
false |
Bypass confirmation prompts (e.g., for init -default). | |
dry-run / runtime.dryRun (internal) |
false |
If true, simulates the backup without making changes. | |
log-level / logLevel |
string |
"info" |
Set the logging level: "debug", "notice", "info", "warn", or "error". |
metrics / engine.metrics |
bool |
true |
If true, enables detailed performance and file-counting metrics. |
| Paths | |||
- / paths.incremental.archive |
string |
"PGL_Backup_Incremental_Archive" |
Sub-directory for historical incremental backups. |
- / paths.incremental.current |
string |
"PGL_Backup_Incremental_Current" |
Sub-directory for the current incremental backup. |
- / paths.incremental.content |
string |
"PGL_Backup_Content" |
Sub-directory for the content within an incremental backup. |
- / paths.incremental.archiveEntryPrefix |
string |
"PGL_Backup_" |
Prefix for timestamped archive directories (incremental). |
- / paths.snapshot.archive |
string |
"PGL_Backup_Snapshot_Archive" |
Sub-directory for snapshot backups. |
- / paths.snapshot.current |
string |
"PGL_Backup_Snapshot_Current" |
Sub-directory for the current snapshot backup. |
- / paths.snapshot.content |
string |
"PGL_Backup_Content" |
Sub-directory for the content within a snapshot backup. |
- / paths.snapshot.archiveEntryPrefix |
string |
"PGL_Backup_" |
Prefix for timestamped archive directories (snapshot). |
| Sync Settings | |||
- / sync.enabled |
bool |
true |
Enable file synchronization. |
sync-engine / sync.engine |
string |
"native" |
The sync engine to use: "native". |
sync-disable-safe-copy / sync.disableSafeCopy |
bool |
false |
Disable 'copy then rename' for secure file syncing (faster but less safe). |
sync-retry-count / sync.retryCount |
int |
3 |
Number of retries for failed file copies. |
sync-retry-wait / sync.retryWaitSeconds |
int |
5 |
Seconds to wait between retries. |
sync-mod-time-window / sync.modTimeWindowSeconds |
int |
1 |
Time window in seconds to consider file modification times equal. |
sync-preserve-source-dir-name / sync.PreserveSourceDirName |
bool |
true |
If true, creates a subdirectory in the destination named after the source directory. |
| Exclusions & Hooks | |||
user-exclude-files / sync.userExcludeFiles |
[]string |
[] |
List of file patterns to exclude. |
user-exclude-dirs / sync.userExcludeDirs |
[]string |
[] |
List of directory patterns to exclude. |
- / sync.defaultExcludeFiles |
[]string |
[...] |
Default file patterns to exclude. |
- / sync.defaultExcludeDirs |
[]string |
[...] |
Default directory patterns to exclude. |
pre-backup-hooks / hooks.preBackup |
[]string |
[] |
List of shell commands to run before the backup. |
post-backup-hooks / hooks.postBackup |
[]string |
[] |
List of shell commands to run after the backup. |
pre-restore-hooks / hooks.preRestore |
[]string |
[] |
List of shell commands to run before the restore. |
post-restore-hooks / hooks.postRestore |
[]string |
[] |
List of shell commands to run after the restore. |
| Archive & Retention | |||
archive / archive.enabled |
bool |
true |
Enable archiving (rollover) for incremental backups. |
archive-interval-mode / archive.intervalMode |
string |
"auto" |
"auto" or "manual". |
archive-interval-seconds / archive.intervalSeconds |
int |
86400 |
Interval in seconds for manual mode. |
retention-incremental / retention.incremental.enabled |
bool |
true |
Enable retention policy for incremental backups. |
retention-incremental-hours / retention.incremental.hours |
int |
0 |
Hourly backups to keep. |
retention-incremental-days / retention.incremental.days |
int |
0 |
Daily backups to keep. |
retention-incremental-weeks / retention.incremental.weeks |
int |
4 |
Weekly backups to keep. |
retention-incremental-months / retention.incremental.months |
int |
0 |
Monthly backups to keep. |
retention-incremental-years / retention.incremental.years |
int |
0 |
Yearly backups to keep. |
retention-snapshot / retention.snapshot.enabled |
bool |
false |
Enable retention policy for snapshot backups. |
retention-snapshot-hours / retention.snapshot.hours |
int |
-1 |
Hourly backups to keep. |
retention-snapshot-days / retention.snapshot.days |
int |
-1 |
Daily backups to keep. |
retention-snapshot-weeks / retention.snapshot.weeks |
int |
-1 |
Weekly backups to keep. |
retention-snapshot-months / retention.snapshot.months |
int |
-1 |
Monthly backups to keep. |
retention-snapshot-years / retention.snapshot.years |
int |
-1 |
Yearly backups to keep. |
| Compression | |||
compression / compression.enabled |
bool |
true |
Enable compression for backups. |
compression-format / compression.format |
string |
"tar.zst" |
"zip", "tar.gz", or "tar.zst". |
compression-level / compression.level |
string |
"default" |
Compression level: "default", "fastest", "better", "best". |
| Performance Tuning | |||
sync-workers / engine.performance.syncWorkers |
int |
4 |
Number of concurrent workers for file synchronization. |
mirror-workers / engine.performance.mirrorWorkers |
int |
4 |
Number of concurrent workers for file deletions in mirror mode. |
delete-workers / engine.performance.deleteWorkers |
int |
4 |
Number of concurrent workers for deleting outdated backups. |
compress-workers / engine.performance.compressWorkers |
int |
4 |
Number of concurrent workers for compressing backups. |
buffer-size-kb / engine.performance.bufferSizeKB |
int64 |
128 |
Size of the I/O buffer in kilobytes for file copies and compression. |
readahead-limit-kb / engine.performance.readAheadLimitKB |
int64 |
262144 |
Limit of the I/O readahead in kilobytes for file compression. 0 disables the readahead on systems with very low memory |
This occurs because the binaries are currently unsigned. Please refer to the Security and Binary Verification section for instructions on how to verify and run the application on macOS and Windows.
- Cause: The user running
pgl-backupdoes not have the necessary read permissions for the source directory or write permissions for the target directory. - Solution:
- Ensure you are running the command as a user with appropriate permissions.
- On Unix-like systems, use
ls -lto check ownership and permissions. You may need to usesudoor adjust file permissions withchmodandchown. - For network shares (SMB/NFS), verify that the share is mounted correctly and that the user has write access.
- Cause: Another
pgl-backupprocess is currently running against the same target directory, or a previous run crashed without cleanly releasing the lock. The application has stale-lock detection, but it may not cover all edge cases (e.g., system clock changes). - Solution:
- First, verify that no other
pgl-backupprocess is running. Useps aux | grep pgl-backup(Linux/macOS) or check Task Manager (Windows). - If you are certain no other process is active, the lock file is stale. You can safely delete the
.~pgl-backup.lockfile from the root of your target backup directory and re-run the command.
- First, verify that no other
- Cause: The command or script specified in the hook may not be executable, not in the system's
PATH, or has incorrect quoting. - Solution:
- Ensure that any script you are calling is marked as executable (e.g.,
chmod +x /path/to/your/script.sh). - Use absolute paths for your scripts (e.g.,
/usr/local/bin/my_script.sh) to avoidPATHissues, especially in automated environments likecron. - If your command contains spaces or special characters, ensure it is correctly quoted within the JSON configuration or on the command line (e.g.,
"'echo Backup starting...'","/path/to/script with spaces.sh").
- Ensure that any script you are calling is marked as executable (e.g.,
- Cause: The default number of concurrent workers may not be optimal for your specific hardware (e.g., slow spinning disks vs. fast SSDs, network latency).
- Solution:
- Adjust the
sync-workersandbuffer-size-kbsettings in yourpgl-backup.config.jsonfile. - For systems with slow I/O (like a single spinning disk or a high-latency network share), decreasing the number of
sync-workers(e.g., to1or2) is recommended to minimize seek latency, as higher concurrency can cause significant thrashing. - For systems with very fast I/O (like a local NVMe SSD), increasing
sync-workersmight yield better results.
- Adjust the
- Cause: The default "Safe Copy" mechanism ensures atomicity but increases metadata operations (create temp file + rename). This can be slow on high-latency network shares or with many small files.
- Solution:
- Try disabling Safe Copy by setting
"disableSafeCopy": truein your config or using-sync-disable-safe-copy. This switches to direct writes, improving performance at the cost of atomicity (a power loss during copy might leave a partial file, which will be fixed on the next run).
- Try disabling Safe Copy by setting
- Cause: The backup source contains symbolic links, but the user running
pgl-backupon Windows does not have permission to create them in the destination. - Solution:
- Run the command prompt or PowerShell as Administrator.
- Enable Developer Mode in Windows settings, which allows non-admins to create symlinks.
- Cause: The
pgl-backup.config.jsonfile contains invalid JSON syntax (e.g., a missing comma, a trailing comma after the last item in a list, or unescaped backslashes in paths). - Solution:
- Validate your JSON content using an online JSON validator.
- Ensure backslashes in Windows paths are escaped (e.g.,
"C:\\Users\\Name"instead of"C:\Users\Name") or use forward slashes ("C:/Users/Name"), which work on all platforms.
- Cause: The file is currently open and locked by another application (e.g., an open Word document, Outlook .pst file, or running database).
- Note:
pgl-backupdoes not use the Volume Shadow Copy Service (VSS). VSS is a complex, Windows-only technology that requires Administrator privileges and is typically only needed for specific edge cases.pgl-backupprioritizes simplicity, speed, and cross-platform consistency. - Solution:
- Close the application using the file before running the backup.
- Use the
pre-backup-hooksfeature to stop the relevant service/application before the backup andpost-backup-hooksto restart it.
- Cause: This often happens when backing up to a file system with lower timestamp precision (like FAT32/exFAT) or a network share (SMB/CIFS) where the server time drifts from the client time.
- Solution:
- Increase the
modTimeWindowSecondssetting in your configuration (e.g., to2or5seconds). This tellspgl-backupto treat timestamps as identical if they are within that window.
- Increase the
Please see our Contributing Guidelines for details on how to contribute to the project.
- Special thanks to Klaus Post for his excellent compression libraries (
github.com/klauspost/compress), which enable the high-performance compression features inpgl-backup. - The Go Authors for the Go programming language and standard library.
This project is licensed under the MIT License. See the LICENSE file for full license text.
This software includes components from third-party projects and the Go standard library. See the NOTICE file for third-party attributions and full license texts.