What is diffzipsend?

It's a free, single-file CLI utility for differential file backups. It allows you to back up one folder to another with the ability to restore your files to any point in time. It currently supports local, FTP, and S3 storage.

It is designed with transparency in mind: all backups are stored in open, standard formats, ensuring your data remains accessible even without the utility.

Installation

Open PowerShell and run:

iwr https://diffzipsend.com/install.ps1 -useb | iex

Or download directly: diffzipsend-windows-amd64.zip

Open the terminal and run:

curl -fsSL https://diffzipsend.com/install.sh | sh

Or download directly: diffzipsend-linux-amd64

Open the terminal and run:

curl -fsSL https://diffzipsend.com/install.sh | sh

Or download directly:

Quick Start

Basic backup command for a local drive folder

diffzipsend backup source_folder to backup_folder

Basic restore command for a local drive folder

diffzipsend restore from backup_folder to restore_folder at 2026-04-08T20:59:04

Check version

diffzipsend --version

View all help options

diffzipsend --help

How To

To back up a local folder to an FTP server, you must use a configuration file (config.toml), as FTP credentials cannot be passed directly through the command line.

1. Create a config.toml file

Create a file named config.toml with your storage and job settings:

config_id = "ftp_backup_example"

# Define your local source folder
[storage.local_source]
type = "local_drive"
base_dir = "C:\\path\\to\\my_folder"

# Define your remote FTP storage
[storage.ftp_remote]
type = "ftp"
server = "ftp.example.com"
port = 21
user = "your_username"
password = "your_password"
base_dir = "/backups/my_folder"

# Define the backup job
[job.my_ftp_job]
backup_from = "local_source"
send_to = "ftp_remote"
full_backup = false # When true, forces a full backup. Otherwise, performs an incremental backup if a full backup has already been completed.
compare_file_hashes = true # If true, uses file hashes to detect changes; otherwise, uses file size and modification time.

2. Run the backup

Open your terminal and run the backup command specifying your job name and the config file:

diffzipsend backup my_ftp_job --config config.toml

The --config argument is optional. See Under the Hood for more details on default configuration locations.

3. Restore the data

To restore the latest version of your data back to the local folder:

diffzipsend restore my_ftp_job --config config.toml

Optional: Restore to a different folder

If you want to restore to a different location:

diffzipsend restore my_ftp_job to "C:\\path\\to\\restore_folder" --config config.toml

Optional: Restore to a specific point in time

If you have multiple backups and want to restore a specific one:

diffzipsend restore my_ftp_job at 20260412135200 --config config.toml

# or

diffzipsend restore my_ftp_job at 2026-04-12T13:52:00 --config config.toml

To back up a local folder to an S3-compatible storage (like AWS S3, MinIO, or DigitalOcean Spaces), you must use a configuration file (config.toml), as S3 credentials cannot be passed directly through the command line.

1. Create a config.toml file

Create a file named config.toml with your storage and job settings:

config_id = "s3_backup_example"

# Define your local source folder
[storage.local_source]
type = "local_drive"
base_dir = "C:\\path\\to\\my_folder"

# Define your remote S3 storage
[storage.s3_remote]
type = "s3"
server = "https://fra1.digitaloceanspaces.com" # Optional: Can be ommited if you use AWS S3
region = "us-east-1" # Required by the SDK, even if you don't use AWS S3
bucket = "my-backup-bucket"
access_key = "YOUR_ACCESS_KEY"
secret_key = "YOUR_SECRET_KEY"
base_dir = "backups/my_folder" # Optional prefix path in bucket

# Define the backup job
[job.my_s3_job]
backup_from = "local_source"
send_to = "s3_remote"
full_backup = false # When true, forces a full backup. Otherwise, performs an incremental backup if a full backup has already been completed.
compare_file_hashes = true # If true, uses file hashes to detect changes; otherwise, uses file size and modification time.

2. Run the backup

Open your terminal and run the backup command specifying your job name and the config file:

diffzipsend backup my_s3_job --config config.toml

The --config argument is optional. See Under the Hood for more details on default configuration locations.

3. Restore the data

To restore the latest version of your data back to the local folder:

diffzipsend restore my_s3_job --config config.toml

Optional: Restore to a different folder

If you want to restore to a different location:

diffzipsend restore my_s3_job to "C:\\path\\to\\restore_folder" --config config.toml

Optional: Restore to a specific point in time

If you have multiple backups and want to restore a specific one:

diffzipsend restore my_s3_job at 20260412135200 --config config.toml

# or

diffzipsend restore my_s3_job at 2026-04-12T13:52:00 --config config.toml

To back up an FTP server to an S3-compatible storage (like AWS S3, MinIO, or DigitalOcean Spaces), you must use a configuration file (config.toml), as credentials for both FTP and S3 cannot be passed directly through the command line.

1. Create a config.toml file

Create a file named config.toml with your storage and job settings:

config_id = "ftp_to_s3_backup"

# Define your source FTP storage
[storage.ftp_source]
type = "ftp"
server = "ftp.example.com"
port = 21
user = "your_username"
password = "your_password"
base_dir = "/home/your_username/data"

# Define your remote S3 storage
[storage.s3_remote]
type = "s3"
server = "https://fra1.digitaloceanspaces.com" # Optional: Can be ommited if you use AWS S3
region = "us-east-1" # Required by the SDK, even if you don't use AWS S3
bucket = "my-backup-bucket"
access_key = "YOUR_ACCESS_KEY"
secret_key = "YOUR_SECRET_KEY"
base_dir = "backups/ftp_data" # Optional prefix path in bucket

# Define the backup job
[job.ftp_to_s3_job]
backup_from = "ftp_source"
send_to = "s3_remote"
full_backup = false # If true, a full backup is forced; otherwise, an incremental backup is performed if the full backup has been performed already.
compare_file_hashes = true # If true, uses file hashes to detect changes; otherwise, uses file size and modification time.

2. Run the backup

Open your terminal and run the backup command specifying your job name and the config file:

diffzipsend backup ftp_to_s3_job --config config.toml

The --config argument is optional. See Under the Hood for more details on default configuration locations.

3. Restore the data

To restore the latest version of your data back to the FTP server:

diffzipsend restore ftp_to_s3_job --config config.toml

Optional: Restore to a local folder

Since FTP might not be the most convenient place for a quick restore, you can restore directly to a local path:

diffzipsend restore ftp_to_s3_job to "C:\\path\\to\\restore_folder" --config config.toml

Optional: Restore to a specific point in time

If you have multiple backups and want to restore a specific one:

diffzipsend restore ftp_to_s3_job at 20260412135200 --config config.toml

# or

diffzipsend restore ftp_to_s3_job at 2026-04-12T13:52:00 --config config.toml

By default, diffzipsend uses file size and modification time (mtime) to detect if a file has changed. However, in some cases, such as when moving files across different file systems or using certain storage types, modification times might not be preserved or accurate.

In such cases, you can enable hash-based comparison to ensure data integrity by comparing the actual content of the files.

1. Configure hash-based comparison in config.toml

To use file hashes instead of size and modification time, set compare_file_hashes = true in your job configuration:

config_id = "hash_comparison_backup"

[storage.local_source]
type = "local"
path = "C:\\data\\source"

[storage.s3_remote]
type = "s3"
server = "https://fra1.digitaloceanspaces.com" # Optional: Can be ommited if you use AWS S3
region = "us-east-1" # Required by the SDK, even if you don't use AWS S3
bucket = "my-backup-bucket"
access_key = "YOUR_ACCESS_KEY"
secret_key = "YOUR_SECRET_KEY"

[job.secure_backup]
backup_from = "local_source"
send_to = "s3_remote"
compare_file_hashes = true # Enable hash-based change detection

2. Why use compare_file_hashes?

  • Reliability: File hashes (using the fast xxHash algorithm) provide a more reliable way to detect changes than modification times, which can sometimes be unreliable or missing.
  • Cross-Platform Consistency: Different operating systems and file systems handle timestamps differently. Hashes are consistent everywhere.
  • Performance Trade-off: While calculating hashes is slightly more CPU-intensive than checking modification times, diffzipsend uses an optimized implementation to minimize the impact.

3. Run the backup

Execute the backup as usual. The application will automatically calculate and store hashes for comparison during the next incremental backup:

diffzipsend backup secure_backup --config config.toml

4. Verification

When compare_file_hashes is enabled, diffzipsend will:

  1. Calculate the hash of each local file.
  2. Compare it with the hash stored in the last backup snapshot.
  3. Only back up the file if the hashes differ (even if the modification time is the same).

When backing up folders, diffzipsend may encounter files that are locked by other processes or have restricted permissions. By default, the application will stop and report an error if it cannot access a file.

If you want to continue the backup process even if some files are inaccessible, you can enable the skip_if_access_denied option.

1. Configure in config.toml

Set skip_if_access_denied = true in your job configuration:

config_id = "robust_backup_config"

[storage.local_src]
type = "local_drive"
base_dir = "C:\\SensitiveData"

[storage.local_dest]
type = "local_drive"
base_dir = "C:\\Backups"

[job.daily_backup]
backup_from = "local_src"
send_to = "local_dest"
skip_if_access_denied = true # Continue even if some files are locked

2. Why use skip_if_access_denied?

  • Continuous Operation: Prevents the entire backup job from failing due to a single locked file (for example, a log file currently being written to).
  • Scheduled Backups: Highly recommended for automated, unattended backups where you want to ensure the majority of files are backed up even if a few are temporarily unavailable.
  • Reporting: diffzipsend will still log which files were skipped, allowing you to review them later.

3. Run the backup

Execute the backup as usual. The application will gracefully skip any files it cannot open:

diffzipsend backup daily_backup --config config.toml

4. Verification

During the backup, check the logs. Skipped files will be mentioned as warnings, but the final status will indicate success for all other files.

By default, diffzipsend uses your local system's time for the timestamps in backup filenames. This is usually fine for a single computer, but if you're coordinating backups across multiple regions or time zones, using Coordinated Universal Time (UTC) is a best practice.

1. Configure UTC time in config.toml

Enable the utc_time_in_file_names option in your job settings:

config_id = "multi_region_backup"

[storage.src]
type = "local_drive"
base_dir = "/var/www/data"

[storage.dst]
type = "local_drive"
region = "eu-central-1"
bucket = "backup-bucket"
access_key = "..."
secret_key = "..."

[job.app_sync]
backup_from = "src"
send_to = "dst"
utc_time_in_file_names = true # Use UTC instead of local time for filenames

2. Why use utc_time_in_file_names?

  • Standardization: Avoid confusion when your server and backup storage are in different time zones.
  • Consistency: Useful if your system time might change due to Daylight Saving Time (DST).
  • Log Correlation: Easier to correlate backup times with server logs that typically use UTC.

3. Run the backup

Execute the backup as usual:

diffzipsend backup app_sync --config config.toml

4. Restore using UTC or Local Time

When restoring, you can specify the point in time in either UTC or your local time. diffzipsend automatically handles the conversion.

Restore using local time:

diffzipsend restore app_sync at 2026-04-12T15:00:00 --config config.toml

Restore using UTC time:

Simply append Z (for Zulu/UTC) or an offset to the timestamp:

diffzipsend restore app_sync at 2026-04-12T10:00:00Z --config config.toml

5. Verification

After the backup is complete, check your backup destination. The filenames will include a timestamp based on UTC. For example, if your local time is UTC+5 and you run the backup at 15:00, the filename will show 10:00.

By default, diffzipsend performs a full backup on its first run and differential backups on subsequent runs. A differential backup only includes files that have changed since the last full backup.

If you need to force a full backup, for instance, to restart your backup cycle, you can use the full_backup option.

1. Configure Full Backup in config.toml

Set full_backup = true in your job configuration:

config_id = "backup_project"

[storage.local_src]
type = "local_drive"
base_dir = "D:\\Projects\\App"

[storage.s3_dst]
type = "s3"
region = "us-west-2"
bucket = "project-archives"
access_key = "..."
secret_key = "..."

[job.daily_diff]
backup_from = "local_src"
send_to = "s3_dst"
full_backup = false # Do an incremental backup if the full backup is already there

[job.weekly_full]
backup_from = "local_src"
send_to = "s3_dst"
full_backup = true # Force a full backup every time this job runs

2. Why use full_backup?

  • Restart the Backup Chain: Use this to start a fresh backup cycle, especially if the differential chain is getting too large.
  • Data Integrity: Periodic full backups are a good security practice to ensure you have a complete copy of all files.
  • Easier Restore: Restoring from a full backup is faster as it does not need to combine multiple differential zip files.

3. Run the backup

Execute the backup as usual:

# run every day
diffzipsend backup daily_diff --config config.toml

# run once a week
diffzipsend backup weekly_full --config config.toml

4. Verification

When a full backup is performed, diffzipsend creates a new snapshot.json and a full zip archive. The logs will indicate that all files are being backed up.

Under the Hood

If the --config argument is not specified, the application searches for the config.toml file in the following locations (in this order):

  1. The current working directory.
  2. User configuration directory: ~/.config/diffzipsend/config.toml on Linux/macOS or %USERPROFILE%\.config\diffzipsend\config.toml on Windows.
  3. The executable directory: The same directory where the executable is located.

To ensure security, diffzipsend avoids storing sensitive information like passwords or S3 secret keys in plain text within your config.toml file. Instead, it leverages your operating system's native secure storage (the "keyring").

1. Secure Keyring Integration

When you save a configuration containing sensitive fields (such as password for FTP or secret_key for S3), diffzipsend automatically:

  • Moves the actual secret value into your system's secure vault (Windows Credential Manager, macOS Keychain, or Linux Secret Service).
  • Replaces the secret in your config.toml with a placeholder: "[strored_in_keyring]".

2. Automatic Retrieval

When you run a backup or restore job, the application seamlessly retrieves the necessary credentials from the keyring using a unique key derived from your config_id. This means your configuration file remains safe to share or store, as it doesn't contain the actual secrets.

3. Requirements

For this feature to work correctly, you must specify a unique config_id at the top of your config.toml. This ID is used as a namespace to organize and retrieve your secrets from the system keyring.

diffzipsend is designed with transparency and long-term data accessibility in mind. All backups are stored in standard formats, ensuring you can restore your files even without using the utility itself.

1. File Structure & Naming

When a backup is performed, two types of standard ZIP archives are created in your destination storage:

  • Files Archive: Contains the actual changed or new files.
    • YYYYMMDDHHMMSS_full.zip: Created during a full backup.
    • YYYYMMDDHHMMSS_diff.zip: Created during a differential backup.
  • Snapshot Archive: Contains the metadata required to track changes.
    • YYYYMMDDHHMMSS_snapshot.zip: Always created alongside a files archive.

The YYYYMMDDHHMMSS prefix is a UTC (or local, depending on your config) timestamp of when the backup was started.

2. The Files Archive Content

Inside each _full.zip or _diff.zip, the directory structure of your source files is exactly mirrored. You can open these files with any standard ZIP utility (WinZip, 7-Zip, unzip command) to access your data directly.

3. The Snapshot Format

The _snapshot.zip contains a single file: snapshot.json. This is a plain-text JSON file that describes the state of your backup at that point in time. It includes:

  • ver: The format version of the snapshot.
  • base: The timestamp of the previous backup this one is based on.
  • moved: A list of files and folders that were moved or renamed.
  • deleted: A list of files and folders that were removed.
  • fingerprints: A mapping of every file path to its xxHash fingerprint, used to detect changes for the next backup.

4. Restoration without the Utility

Because the format is open, you can manually restore your data:

  1. Locate the latest _full.zip and extract it.
  2. Extract all subsequent _diff.zip files in chronological order on top of the same directory, overwriting files.
  3. Refer to the snapshot.json in the latest _snapshot.zip to identify which files should be deleted or moved to perfectly match the original state.

Contact us