---
title: "restic Object Lock on Backblaze B2: what prune cannot delete"
description: "forget --prune succeeds on a locked B2 bucket, but locked versions stay stored and billed. What Object Lock protects, the retention to set, how to restore."
source: "https://wardio.net/guides/restic-b2-object-lock/"
---

**Backups**

# Backblaze B2 Object Lock with restic: what it protects

You enabled Object Lock, ran `restic forget --prune`, and it succeeded. The files are gone from the listing, yet the lock works as designed. It protects something other than what you assumed, and that difference decides whether it would save you.

By David Kabel · Updated 2026-09-23 · 13 min read

**The short answer**

restic 0.13.0 and later works on a Backblaze B2 bucket with Object Lock, best through the S3-compatible API. Its deletes become hide markers, which Object Lock allows, so forget and prune succeed while the locked versions stay stored and billed until retention ends. Retention counts from each file's upload, so it protects recent uploads and not whole snapshots, which depend on older pack files. Pair it with an append-only key and a delayed cleanup of hidden versions.

## Deleting from a locked bucket with restic

B2 buckets are always versioned, and Object Lock protects a file *version*, not a file name. Deleting by name removes nothing. B2 inserts a delete marker, which its native API calls a hide marker, and the previous version stays stored. Backblaze says so: *"None of the Object Lock modes prevent you from uploading a new version of a locked object... You can also hide a locked object so it doesn't appear in object listings."* The lock only refuses deleting the version itself before its retention date. restic's result depends on how it deletes.

| restic backend | How it deletes | Under Object Lock |
| --- | --- | --- |
| `s3:`, restic 0.13.0 or later | By name, so B2 writes a hide marker | Works. restic's changelog lists Object Lock buckets as supported from 0.13.0. |
| `b2:`, restic 0.15.0 or later | Tries to delete the version, falls back to hiding on HTTP 401 | Works. B2 answers a delete blocked by Object Lock with 401 access\_denied. |
| `b2:`, before 0.15.0 | Deletes versions only | Lock files cannot be removed and the repository stays locked, per issue 3491 on 0.12.1. |
| `rclone:`, default arguments | restic passes `--b2-hard-delete` | B2 refuses permanent deletes of locked versions. |

restic's documentation recommends the S3-compatible API for B2 anyway, and notes that its S3 backend *"will only hide no longer necessary files"*. Prune completes normally and frees nothing until each hidden version's lock has expired and a lifecycle rule or cleanup job deletes it.

## Protection depends on who holds the credentials

This setup is meant to stop someone who owns the server and its backup key. If that key is append-only, with no `deleteFiles` capability, they can hide files and upload new versions over old names, but cannot delete a single version. Against them, Object Lock adds little. Its value is against credentials that *can* delete.

| Who holds the credentials | Append-only key alone | Plus Object Lock |
| --- | --- | --- |
| Attacker on the server, backup key | Safe while hidden versions are kept | Same, and recent uploads stay even if that window is short |
| Attacker on the admin host, key with `deleteFiles` | Can delete every version | Versions still under retention survive |
| Attacker holding the master key or signed in to the account | Can delete everything | Governance falls. The master key has every capability, including `bypassGovernance`. Compliance holds, but closing the account deletes all data, locked or not, so guard the login with two-factor. |
| Your own cleanup job or lifecycle rule misbehaving | Deletes what it is told | Locked versions cannot be deleted |

The biggest limit is that default retention counts from each file's upload. restic deduplicates, so last night's snapshot reuses pack files from whenever that data was first backed up, and their locks may have expired long ago. With 30 days of retention, someone who can delete can still remove an old pack and break every snapshot that uses it, including yesterday's. restic's maintainer wrote in 2023: *"to guarantee that a snapshot can be restored for a certain period, all relevant files must be protected with a sufficiently long Object Lock. Just applying an Object Lock once therefore won't work."*

restic cannot set or extend locks. Its source as of 0.19.1 contains no Object Lock code. Kopia extends locks during maintenance, on B2 through its S3 backend only. If you need snapshot-level immutability, you need a different tool. [restic vs Borg](/guides/restic-vs-borg/) compares how the alternatives handle append-only.

## Storage cost of Object Lock

Object Lock itself is free. You pay normal storage for every locked version, which was $6.95 per TB per month at B2's pay-as-you-go rate in September 2026. A hidden version stays until its lock has expired and a lifecycle rule or cleanup job deletes it. Lifecycle rules run once a day and skip locked versions.

Call D the number of days you keep a version after it is hidden, the window the [storage growth guide](/guides/restic-backblaze-storage-growth/) tells you to size, and R the default retention. If R is no longer than D, Object Lock adds little or nothing to the bill, because nothing it holds would have been deleted sooner. The cost appears when R exceeds D, for example with *Keep only the last version*, where D is one day. Then every file that prune hides within R days of its upload stays billed until its lock runs out.

Compliance retention cannot be shortened, and Backblaze's documented way out of locking too much for too long is closing the account. Default retention also locks everything uploaded, including junk an attacker uploads with the server's key, which you then pay to keep for R days. In governance mode a separately held key with `bypassGovernance` and `deleteFiles` can remove it. In compliance mode nothing can.

## Size retention against detection time

D and R measure different things. D counts from when a file is hidden, so it protects everything, old packs included, against the server's key. R counts from upload, so it protects only recent uploads, but against every credential. Both should be longer than the time it would take you to notice a compromise and act, not whatever keeps the bill small. If someone reads alerts once a week and can be away for two weeks, anything under a month will not cover you.

With an append-only key, also write forget policies with `--keep-within`. restic's documentation recommends it because an attacker can add fake snapshots that push real ones out of a policy such as "keep three weekly".

## Object Lock alongside an append-only key and a cleanup job

Object Lock adds to an append-only design and does not replace it. In that design the backed-up machine cannot delete its own history, and pruning runs from a separate admin host. That design has [downsides of its own](/guides/resticprofile-production/#downsides), compared there with Veeam's immutable repositories.

**The server's key** gets list, read and write on its own bucket, nothing else: no `deleteFiles`, no `bypassGovernance`, no retention or bucket-setting capabilities. Backblaze documents that deleting by name needs `writeFiles`, while deleting a specific version needs `deleteFiles`.

```
# On the admin machine, never on the server being backed up.
# One key per server, limited to its bucket, without deleteFiles.
b2 key create --bucket acme-restic web1-backup \
  listBuckets,listAllBucketNames,listFiles,readFiles,writeFiles

# The long listing shows every key's capabilities. Check them.
b2 key list --long

# On the server: restic through the S3-compatible API.
export AWS_ACCESS_KEY_ID=<keyID of web1-backup>
export AWS_SECRET_ACCESS_KEY=<applicationKey of web1-backup>
restic -r s3:s3.<region>.backblazeb2.com/acme-restic/web1 snapshots
```

**The admin host** runs forget, prune and the version cleanup with a key that has `deleteFiles` but not `bypassGovernance`. Use the `s3:` backend there too. restic's `b2:` backend permanently deletes every unlocked version when its key allows it, which skips your cleanup window. The cleanup must treat a refused delete on a locked version as normal and move on. rclone's `backend cleanup-hidden` removes all old versions rather than those past a cutoff, so it is the wrong tool here.

**Enabling the lock** on an existing bucket means turning on Object Lock and then setting a default retention. Because you cannot undo a compliance mistake, start with governance and switch to compliance once a full retention period of bills matches your estimate.

```
# With a key that can change bucket settings, not the server's key.
# One-way switch: Object Lock cannot be disabled afterwards.
b2 bucket update --file-lock-enabled acme-restic

# Nothing is locked until a default retention exists, and it only
# applies to files uploaded from now on.
b2 bucket update --default-retention-mode governance \
  --default-retention-period "30 days" acme-restic

# Check isFileLockEnabled, defaultRetention and lifecycleRules.
b2 bucket get acme-restic
```

## Restoring after an attack

1.  **Revoke the server's key**: From the admin machine, run b2 key delete with the key ID. The attacker loses access and your history stays as it is.
2.  **Stop the clock on old versions**: Pause the version cleanup job and raise daysFromHidingToDeleting on the lifecycle rule. Rules run once a day, so do this before any analysis, not after.
3.  **Pick a moment before the compromise**: Use your detection data, such as the first alert, the first odd snapshot or the first unexpected upload.
4.  **Copy the repository as it was then**: rclone's --b2-version-at shows the bucket at that moment, including files hidden since, and refuses writes, so it cannot make anything worse. You need local space for the whole repository.
5.  **Check before you trust it**: Clear any lock files the copy carries, run restic check --read-data on it, then restore and bring the application up.
6.  **Rebuild, then start clean**: Set up a new server, new keys, and a new repository or bucket seeded from the verified copy. Unhiding in place needs deleteFiles and does not undo versions uploaded over the originals.

```
# On a clean machine. b2admin is an rclone remote on a key that can
# list and read the bucket. --b2-version-at permits no writes.
rclone copy --b2-version-at 2026-09-01T00:00:00Z \
  b2admin:acme-restic/web1 /srv/restore/web1

# The copy includes any lock file that existed at that moment, and
# check refuses to start past it. Nothing else uses this copy.
restic -r /srv/restore/web1 unlock --remove-all
restic -r /srv/restore/web1 check --read-data
restic -r /srv/restore/web1 snapshots
restic -r /srv/restore/web1 restore latest --target /srv/restore/files
```

Rehearse this before you need it, and time it. See [backups that actually restore](/guides/backups-that-restore/).

## Reasons Object Lock looks broken

**The deletes succeed.** B2 allows hides on locked files by design. `b2 ls -r --versions` on the repository path shows the versions are still there.

**There is no default retention.** Enabling Object Lock locks nothing by itself. Files become immutable only once you set a default retention period.

**The files predate the retention.** You can enable Object Lock on an existing bucket, one way only, except on a replication source or a restricted bucket such as a snapshot. Default retention still covers only files uploaded afterwards. Backblaze's Object Lock page says so, but the `bucket create` help in b2 5.0.0 still says the setting cannot be changed after creation. `bucket update --file-lock-enabled` does it. Other S3-compatible stores differ. MinIO refuses to enable Object Lock on an existing bucket.

**A key can bypass it.** Governance mode only binds keys without `bypassGovernance`.

**A lifecycle rule hides current files.** `daysFromUploadingToHiding` hides every version, locked or not, and restic then cannot read its own packs.

## Gotchas

-   **Prune makes the bucket bigger.** On a governance-mode test bucket, `forget --prune` exited 0 and reported 14.3 MiB remaining, while the stored bytes rose from 22.5 MB to 30.0 MB. The repacked pack is a new upload, and the packs it replaced stay behind as locked, hidden versions. Expect each prune to add to the bill until those locks expire, and measure the bucket with `b2 bucket get --show-size`, which counts hidden versions, not with restic.
-   **An expired lock on an old pack breaks new snapshots.** We backed up twice and then let the first run's locks lapse, simulated by shortening them with a bypass key. A key that could delete versions but not bypass removed those two packs and was refused on the two newer ones. `restic check` then reported both packs missing, and `restic restore latest` recovered only the one file added in the second run. Keep the hidden-version window longer than your detection time. The lock alone does not cover old packs.
-   **The server's key must still be able to hide.** With a key that had no delete permission at all, every restic run printed `error while unlocking` with Access Denied, exited 0 and left its lock file. After one `forget` with that key, its exclusive lock made every later command, backups included, exit 11 with "repository is already locked", and a plain `restic unlock` from another host would not remove a lock under 30 minutes old. Only `unlock --remove-all` did. On B2 the key above avoids this, because `writeFiles` covers deleting by name, which hides. On other S3 storage allow `s3:DeleteObject` and deny `s3:DeleteObjectVersion`. That key ran backup, forget and prune cleanly and still could not delete a version.
-   **Unhiding everything brings back restic's own deletions.** Removing every delete marker after a simulated attack restored four stale lock files, a snapshot that `forget` had removed, and duplicate index entries, while the overwritten config still failed with "ciphertext verification failed". It also erases the record of when each file was hidden, so later point-in-time copies include all of it. Copy a point-in-time view instead, or remove only markers newer than the compromise.
-   **The point-in-time copy can contain lock files.** A copy taken at a moment when a restic command held a lock made `restic check` on the copy exit 11 with "repository is already locked". Run `restic unlock --remove-all` on the copy first, as in the commands above. Nothing else uses the copy.

**How this was tested.** On 23 September 2026 we ran restic 0.19.1 against MinIO RELEASE.2025-09-07T16-13-09Z in Docker on an Ubuntu 24.04 host, as an S3-compatible stand-in for B2, with AWS CLI 1.46.1, mc RELEASE.2025-08-13T08-35-41Z and rclone 1.75.1, whose `--s3-version-at` is the S3 twin of `--b2-version-at`. We tested versioned, governance and compliance buckets, a no-delete key and a hide-only key, an overwrite-and-hide attack, and recovery both from a point-in-time copy and by copying old versions back, each ending in `restic check --read-data`. We checked the b2 commands only against the parser and help of b2 5.0.0. We tested nothing against a live Backblaze B2 account. B2's hide markers and 401 responses, the `b2:` backend, lifecycle rules, key capabilities, the master key, pricing and account closure rest on Backblaze and restic documentation and source. We configured MinIO lifecycle expiry but did not observe it, because its shortest period is one day.

## Sources

1.  [Object Lock](https://www.backblaze.com/docs/cloud-storage-object-lock), Backblaze B2 documentation
2.  [How to Enable Object Lock with the Backblaze B2 Native API](https://www.backblaze.com/docs/cloud-storage-enable-object-lock-with-the-native-api), Backblaze
3.  [How to Enable Object Lock or a Legal Hold on an Existing Bucket](https://www.backblaze.com/docs/cloud-storage-enable-object-lock-or-a-legal-hold-on-an-existing-bucket), Backblaze
4.  [Lifecycle Rules](https://www.backblaze.com/docs/cloud-storage-lifecycle-rules), Backblaze B2 documentation
5.  [S3-Compatible App Keys](https://www.backblaze.com/docs/cloud-storage-s3-compatible-app-keys), Backblaze
6.  [Application Key Capabilities](https://www.backblaze.com/docs/cloud-storage-application-key-capabilities), Backblaze
7.  [b2\_delete\_file\_version](https://www.backblaze.com/apidocs/b2-delete-file-version), Backblaze B2 Native API
8.  [S3 Delete Object](https://www.backblaze.com/apidocs/s3-delete-object), Backblaze B2 S3-Compatible API
9.  [Digging Deeper Into Object Lock](https://www.backblaze.com/blog/digging-deeper-into-object-lock/), Pat Patterson, Backblaze blog, 28 November 2023
10.  [B2 Cloud Storage pricing](https://www.backblaze.com/cloud-storage/pricing), Backblaze, checked September 2026
11.  [B2 command-line tool documentation](https://b2-command-line-tool.readthedocs.io/en/master/), Backblaze, version 5.0.0
12.  [Managing Object Lock](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock-managing.html), Amazon S3 User Guide
13.  [Preparing a new repository: Backblaze B2](https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html), restic documentation
14.  [Removing backup snapshots: security considerations in append-only mode](https://restic.readthedocs.io/en/stable/060_forget.html), restic documentation
15.  [Changelog 0.13.0: upload checksums for S3, enabling Object Lock buckets](https://github.com/restic/restic/blob/master/changelog/0.13.0_2022-03-26/issue-2202), restic, 26 March 2022
16.  [Changelog 0.15.0: B2 falls back to hiding files](https://github.com/restic/restic/blob/master/changelog/0.15.0_2023-01-12/issue-2134), restic, 12 January 2023
17.  [B2 backend source, Remove](https://github.com/restic/restic/blob/master/internal/backend/b2/b2.go), restic
18.  [Issue 3491: forget fails with B2 and object locks](https://github.com/restic/restic/issues/3491), restic, August 2021
19.  [Backblaze Object Lock / Ransomware protection](https://forum.restic.net/t/backblaze-object-lock-ransomware-protection/6651), restic forum, September 2023
20.  [Backblaze B2](https://rclone.org/b2/), rclone documentation
21.  [Ransomware protection](https://kopia.io/docs/advanced/ransomware-protection/), Kopia documentation

## Questions

### Does restic support Backblaze B2 Object Lock?

restic works on a B2 bucket with Object Lock but does not manage locks. Its changelog lists S3 buckets with Object Lock as supported from 0.13.0, and its deletes become hide markers, which locked files allow. restic cannot set or extend retention, so each file is protected for the default retention period from its own upload, and older pack files that current snapshots still use are not locked.

### Why can restic still delete files when B2 Object Lock is enabled?

It is not deleting them. On a B2 bucket, deleting by name inserts a hide marker and the previous version stays stored. Object Lock protects versions, so B2 allows hiding and refuses only deleting the version itself before its retention date. Run b2 ls -r --versions on the repository path and the hidden versions are still there, billed until the lock expires and a cleanup removes them.

### Can I enable Object Lock on an existing Backblaze B2 bucket?

Yes. Backblaze allows enabling Object Lock on an existing bucket through the web console, the native API, the S3-compatible API or the b2 command-line tool, except on a bucket that is a replication source or a restricted bucket such as a snapshot. You cannot switch it off again. Default retention only applies to files uploaded after you set it, so everything already in a restic repository stays unlocked unless you set retention file by file.

### Should restic backups use governance or compliance mode?

Any key with the bypassGovernance capability can shorten or remove governance-mode retention. The master key has every capability, so governance does not survive an account takeover. Nobody can remove compliance mode, Backblaze support included, but the only escape from a retention period that is too long is closing the account. A sensible path is governance while you measure the cost, then compliance.

### Does Object Lock make restic backups immutable against ransomware?

Partly. It makes each uploaded file immutable for the retention period, which protects recent data even from an attacker holding credentials that can delete. It does not make a snapshot immutable, because deduplicated snapshots depend on older pack files whose locks have already expired. Combine it with an append-only key on the server, delayed cleanup of hidden versions, and restore tests.

**Do you know what your backup key can delete?**

The free review includes what a compromised server could do to its own backups, and how far back you could restore. [The wider backup guide](/guides/backups-that-restore/) covers the rest.

[Free server review](/contact/)
