Skip to content

FAQ: sharpen the restic-interop caveat — restic prune vs any rustic write - #144

Open
l1a wants to merge 1 commit into
rustic-rs:mainfrom
l1a:faq-restic-prune-vs-rustic-writer
Open

FAQ: sharpen the restic-interop caveat — restic prune vs any rustic write#144
l1a wants to merge 1 commit into
rustic-rs:mainfrom
l1a:faq-restic-prune-vs-rustic-writer

Conversation

@l1a

@l1a l1a commented Aug 3, 2026

Copy link
Copy Markdown

The current wording says the only thing to take care of is "that you don't run prune with restic and rustic at the same time", which reads as prune-versus-prune. The case that actually destroys data is restic prune against a rustic writer of any kind, and the sentence as written does not cover it.

This is docs-only and proposes no behaviour change. rustic's lock-free design is correct and works — that is precisely why the interop caveat is narrower and sharper than the current sentence suggests.

Measured — rustic 0.11.3, restic 0.19.1, throwaway local repositories

rustic's grace period works exactly as documented. A repository holding three unreferenced packs:

command packs before packs after
rustic prune (default) 3 3 — reported to delete: 3 packs, removed none
rustic prune --instant-delete 3 0

The mixed case does not. A rustic backup was frozen mid-write with SIGSTOP, then restic prune was run against the same repository:

$ restic prune          # while a rustic backup is mid-write
to delete:             0 blobs / 487.780 MiB
deleting unreferenced packs
[0:00] 100.00%  14 / 14 files deleted
done

It did not wait — rustic had written no lock for it to see. Letting the rustic backup finish, then:

$ restic check --read-data
Load(<data/6e316cf71f>, ...) failed: no such file or directory
... 5 data packs missing ...
The repository is damaged and must be repaired.
Fatal: repository contains errors

As a control, the same restic prune against a repository where a restic client held a lock refused correctly:

unable to create lock in backend: repository is already locked by PID 58305 on <host>
lock was created at ... (27.1s ago)

So the mechanism is confirmed in both directions: restic's immediate deletion is safe only because of the lock, and rustic is invisible to it.

Why I ran into this

I maintain rusticprofile, a small per-machine scheduler for rustic, and was migrating a 7-machine fleet from restic to rustic one host at a time.

Before the first cutover, the designated prune host's restic prune saw the other machines' restic locks and waited. After it, the migrated host wrote with rustic and became invisible to that prune. Nothing about the prune host changed — the cutover removed the only thing making the two tools mutually visible, and there was no signal that anything had.

I initially concluded rustic was unsafe under concurrent access and disabled the prune schedule entirely. Reading your FAQ properly showed that was wrong: rustic's design is sound, and the real rule is much narrower. That round trip is what makes me think the extra precision earns its place — the mid-migration window is exactly the situation this FAQ entry gets read in.

Happy to trim the table, soften the wording, or split the caveat into its own FAQ entry if you'd prefer a different shape.

The restic-compatibility answer says the only thing to take care of is
"that you don't run prune with restic and rustic at the same time",
which reads as prune-versus-prune. The case that actually destroys data
is a restic prune against a rustic writer of any kind, and the sentence
as written does not cover it.

rustic and restic reach safe concurrency by different mechanisms that do
not compose: rustic defers deletion by --keep-delete, restic takes an
exclusive lock which is what lets its prune delete immediately. rustic
neither takes nor honours that lock, so a restic prune can remove packs
an in-flight rustic run has written and not yet referenced.

Measured on rustic 0.11.3 and restic 0.19.1: with a rustic backup frozen
mid-write, restic prune deleted 14 packs and restic check --read-data
then reported the repository damaged. The control, the same prune
against a repository where a restic client held a lock, refused
correctly.

Docs only; no behaviour change is proposed. rustic's lock-free design is
correct and working, which is exactly why the interop caveat is narrower
and sharper than the current sentence suggests.

Assisted-By: Claude Opus 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant