Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.



dfb - Dated File Backup

Dated File Backup (dfb) performs full-file, incremental backups by uploading new and modified files with the date appended, and using markers for deleted files. It can also optionally record references to renamed files. It creates simple, reliable, self-documenting, portable, durable, and auditable backups. It does not rely on any proprietary or complex formats or tools, and backups can be easily understood and restored manually. It uses rclone to connect to and from a variety of cloud providers, offering additional utility such as encryption and enabling backups of cloud storage. However, due to its simple, full-file nature, it is not ideal for large files with frequent small changes.

Many popular backup tools — such as macOS Time Machine, rsnapshot, or rsync with --link-dest (example) — work well for local snapshots but often lack robust cloud support and portability. Tools like restic and kopia offer greater efficiency through deduplication and compression, but rely on complex tooling, opaque formats, and can be difficult to audit or restore without the original software.

dfb offers a simpler, more transparent approach:

  • 💾 Straightforward full-copy backups — new and modified files are stored with date-tagged filenames.
  • 📄 No proprietary formats — everything is human-readable and restorable with basic tools.
  • ☁️ Cloud-ready — uses rclone to support many storage providers, with optional encryption 🔐, multiple backends, and other features. Can also support immutable/WORM/append-only storage including S3 Object Lock.
  • ✅ Reliable and auditable — human-interpretable backups, no indexes required, no fragile snapshots, no surprises!

Choose dfb if you value clarity, resilience, and control over aggressive storage optimization.

Command Help

See CLI Help

File names

When files are backed up, they are renamed to include the date of the backup in the name. Filenames are:

<filename>.YYYYMMDDhhssmm<optional R or D>.ext

where the time is always in UTC (Z) time. When a file is modified at the source, it is copied to the remote in the above form. If it is deleted, it is marked with a tiny file with D. If a file is moved, a reference R is created pointing to the original. If moves are not tracked, then a move will generate a new copy of the file.

There may also be empty directory markers named .dfbempty.<date> which are used to hold the empty directory on the destination.

Directory names remain unchanged.

Alternate Short Format

If path length is a concern, you can set filename_timestamp_format = "base36-epoch" in the config. New backup objects will then use an 8-character lowercase base36 UTC epoch timestamp, such as file.00s7v4f1.txt, instead of the default file.20240126094501.txt.

This only affects future writes. Existing backups remain readable, and mixed histories with both formats are supported. Lexical ordering still matches chronological ordering within either format by itself, but a mixed-format history will no longer sort chronologically by filename alone.

Reference Files

The only exception to full-file backups are references. References write JSON data like1:

{"ver": 2, "rel": "<RELATIVE path to referenced file>"}

Note that references are considered and guarded when pruning (with associated tests). Be careful when pruning manually so as to not break a reference file.

The reference file is "ground truth" but some additional files are also backed up to help speed up refreshes without having to read every reference file.

Install

From PyPI:

$ pip install dfb

or

$ pip install -U dfb

Just install from github directly.

$ python -m pip install git+https://github.com/Jwink3101/dfb.git

Setup

To start, run:

$ dfb init path/to/config.py

The config file is heavily and extensively documented. It is read in Python without any sandboxing so make sure it is trusted! Some variables are defined inline including os and a few modules. The variables __file__ and __dir__ are pathlib.Path objects for the config file and the directory of the config file respectively. These can be used to specify paths to things like files for --filter-from rclone flag.

Simple Usage

Assuming this is a new setup, just run it:

$ dfb backup --config path/to/config.py

Or set the environment:

export DFB_CONFIG=path/to/config.py
$ dfb backup

Or, directly execute the config file (it has a custom shebang):

./config.py # <--- Assumed backup mode
./config.py backup
./config.py ls
# ...

Override

The config file can be overridden at the command line by specifying code to evaluate before and after the configuration file. In order to control if it is evaluated before or after the configuration file, the variables pre and post are defined.

Local Database

A local sqlite3 database is kept that serves to greatly speed up interaction. The database is used for listing the remote either in the CLI or for use in backups. It is updated as real-time as is possible with uploads so even interrupted or failed backups should be correct (or, if not, it may re-backup but not lose anything).

However, if the remote is modified manually, the database should be refreshed. Or if you wish to interact with the backup on another machine, it should either be refreshed or copied.

Note that, depending on the configuration, not all data may be in the database. For example, if not using mtime for dst_compare, it won't be fetched. That will still be there for restore if the remote supports it.

The database is stored in:

<rclone cache dir>/DFB/<config_id from config>.db

Where "rclone cache dir" is found from: $ rclone config paths.

When refreshing the database, dfb will also optionally read the logs/indexes of all uploaded files to fill in source-specific metadata and hashes, and speed up resolving reference files. These are secondary to the operation of dfb and the native remote storage is sufficient to restore (up to the limitations of the remote).

Pruning

You can prune dfb by removing unneeded files at snapshots older than a set time. The date specified is the oldest point-in-time where you can restore fully. Files older than that time may still be present as needed.

This pruning strategy is discting from snapshot-based backups where you keep a certain number of snapshots, but offers a more practical approach as it doesn't risk losing important data bewtween snapshots and makes it very easy to understand changes.

Pruning is as simple as shown in some examples:

$ dfb prune "365 days"
$ dfb prune 2023-08-11
$ dfb prune 2022-10-14 --keep-versions 10  # keeps 10 *additional* before the date
$ dfb prune now --keep-versions 20         # just keep the last 20

Files older than the specified time or verion number may still exist if they are the latest or are referenced.

Pruning individual files (Advanced)

Pruning individual files is supported through the advanced sub command. You need to know the "real-path" or "rpath" of the file. Assume $DFB_CONFIG is set for these examples.

For example, if you want to delete my/large/file.ext, you can do:

$ dfb versions my/large/file.ext --real-path --ref-count

You will see a table of all versions of the file. Note that --ref-count is included since if you delete a referent (Ref. Count > 1), it will make the referrer appear deleted (that may be the intended outcome!). Be careful about references!

Simply identify the versions you wish to delete, copy the Real Path, and run

$ dfb advanced prune-file my/large/file.<timestamp>.ext

You can, of course, do it all manually but (a) it won't check/delete references and (b) it won't refresh the database. To refresh after manual pruning, either call refresh or use the advanced dbimport command. See notes on formats for details.

Additional Docs

Known Issues

  • Logging in dfb mount is not complete nor does it make much sense at the moment.
  • dfb mount requires mfusepy (python -m pip install dfb[mount] or python -m pip install mfusepy) and a working FUSE install; see docs/mount.md.
  • dfb mount empty-directory removal can be expensive for deep trees; use --empty-check-depth or leave it disabled (see docs/mount.md). Use --show-deleted to surface deletions as [DEL].
  • Restore still reads file contents from the backup object on the remote. However, it can optionally replay source-origin metadata already stored in the local DB (and also preserved in logs/snapshots when available) during the restore copy call.
    • Example: If the source have ModTime but the destination does not, when restoring through DFB, it can try to apply the metadata during the restore.
  • Deletes are marked with a 3 byte file (just ASCII encoded DEL). This is smaller than most filesystem block sizes so they take more space on a local filesystem.
  • It is assumed that the source does not change from when listing to backing up. If a file is deleted in the iterim, it could cause an error. If it is modified, there may be a future false-positive (which is a safe scenario).
  1. Older versions were just a single line with the absolute path to the reference. This implied version 1 was less flexible and made for some other issues. ↩

Metadata

Release files for dfb 20261029.0b0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dfb 20261029.0b0
File Size Uploaded Provenance Actions
dfb-20261029.0b0.tar.gz 126.9 kB Without provenance Details for dfb-20261029.0b0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for dfb 20261029.0b0
File Interpreter ABI Platform Actions
dfb-20261029.0b0-py3-none-any.whl Python 3 none any Details for dfb-20261029.0b0-py3-none-any.whl

Total release size: 222.4 kB

Release files / dfb-20261029.0b0.tar.gz

Download URL dfb-20261029.0b0.tar.gz
Size 126.9 kB
Tags Source
SHA-256 checksum
How to use checksums
271fdcbc180cad56544ba0d3c51e6432bfae43fe5ebb336e0fb3a6070b06dabf
BLAKE2b-256 checksum
How to use checksums
96d4ed19064c577edfba73cd45e01222fa0a4a19f4f80d8e9c032a838e043a0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / dfb-20261029.0b0-py3-none-any.whl

Download URL dfb-20261029.0b0-py3-none-any.whl
Size 95.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
45b55e4290608132f0c853d435b8e2cdeeaa85d4885367def95e7a9e86834689
BLAKE2b-256 checksum
How to use checksums
82d0ed58bd1bbb7b94c48fae64e99cc5d44060baed20bca8f3e6aac4e2d14428
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Security for dfb 20261029.0b0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page