Skip to main content

Veeam V13 Immutability Improvements & Fewer Object Storage API Calls - UNCOVERED !

  • July 22, 2026
  • 14 comments
  • 187 views

falkob
Forum|alt.badge.img+13

Everybody keeps repeating the same headline numbers from the Veeam V13 release notes when it comes to the new Veeam V13 immutability model. 40% less storage, an 8x reduction in retention calls and more than 2x fewer object storage API calls in total. I recently presented this exact topic at our German Veeam Usergroup in Cologne, and the number one question afterwards was basically: “Falko, does it really work like that ?”

So instead of trusting the marketing slides, I built a lab, turned on S3 audit logging and counted every single API call myself. I guess most people already know that I love object storage  In this blog post I want to walk you through what I did and what the logs actually revealed.

Object Storage has quietly become the most important repository type in the whole Veeam world, and the reworked immutability engine in Veeam V13 pushes this even further. With V13, every job type can write directly to object so there are no exceptions and no workarounds. That is a pretty big architectural shift, and it comes with a completely reworked immutability model which Veeam claims is way more efficient. However, claims like that are easy to make and hard to prove, so I wanted actual data and not a nice looking slide.

If you have followed my Object Storage for Veeam blog series or my post on why Immutable Storage is not enough, you already know I like to dig into these topics a bit deeper. So let’s do exactly that again !

 

Why Object Storage for Veeam ?

Before we get to the results, it helps to understand why Object Storage gets so much attention in the first place. In my opinion it is simply the best repository type you can run with Veeam nowadays, and V13 only makes that case stronger.

It is universal. With Veeam V13 every job type goes direct to object, one protocol for everything and no separate landing zone you need to build first.

It is secure by design. You get TLS / HTTPS natively on port 443, and Object Lock immutability kicks in the very moment data is written. There is no separate hardening step you have to perform, which is why I like to call it “inline immutability”.

It needs no repository servers. S3 compatible object storage is stateless, so basically you need Ethernet and nothing else. Performance scales automatically across the storage backend, which means there are no repository servers to buy, size, patch or multiply.

And it gives you one pool with total flexibility. A single target can serve all your jobs, with automatic bucket creation and a pool that expands dynamically. No volume resizing, no capacity planning per repository.

That combination being universal, immutable, serverless and elastic is exactly why so many admins are moving their backups to S3 compatible targets. And it is also why the efficiency of the immutability model matters so much once you run this at scale. Of course there is even more “Why’s” for Object Storage such as operational “easyness” but I wanted to keep it short here.

 

The Veeam V13 immutability model, what actually changed ?

 

The headline change in V13 is how immutability is enforced, and this is the part which drives both the storage savings and the reduction in API calls. The Veeam V13 immutability logic is a completely different approach compared to V12.

In Veeam V12 immutability is chain-based. The entire backup chain is treated as a single unit (checkpoints), so every new incremental backup recursively extends the lock on all previous files in the chain. The result is something I like to call “storage creep”: older blocks stay locked much longer than the retention policy actually requires, and Veeam has to fire off a lot of API calls to keep extending the Retain-Until-Date across the whole chain. On top of that, predicting the exact expiration date for a specific block becomes really difficult.

In Veeam V13 immutability is restore point-based, so essentially block-level. Each data block gets its lock calculated and set once, exactly when it is written, and after that the lock is rarely touched again. Data unlocks precisely when the retention policy expires, which frees up space sooner. Fewer lock extensions obviously mean fewer API calls, faster backup windows and a highly predictable expiration. The lock duration simply equals your immutability setting.

 

V12 and V13 Immutability model Comparison side by side

 

Aspect Veeam V12 (chain-based) Veeam V13 (restore point-based)
Logic basis Entire chain treated as one unit Each restore point / block treated individually
Lock mechanism Recursive extension across all prior files set once when the block is written
Storage impact “storage creep” keeps old blocks locked Optimized data unlocks when retention expires
API costs High & constant PutObjectRetention updates Low locks are not repeatedly extended
Performance Slower due to lock extension overhead Faster and more efficient backup windows
Predictability Hard to predict block expiration Lock duration equals the immutability setting

 

And you can even see this difference right in the Veeam console. With V13 every restore point carries its own Immutable Until date, exactly as the restore point-based model promises.

 

Veeam V13 immutability

 

One important detail: this new architecture currently applies to Direct to Object backups only, so image-based backups written straight to object storage. On-premises object storage is much more impacted by storage creep because its capacity is strictly limited, and that is exactly where Veeam focused first. Once the model has proven itself stable, Veeam has stated they will bring it to the Capacity Tier of the scale-out backup repository as well.

 

What did Veeam actually promise ?

 

The official release notes phrase it like that. A 40% reduction in object storage space, an 8x reduction in PutObjectRetention calls, a 3x reduction in ListBucketcalls and a more than 2x reduction in total API calls. Either way, the promise is clear. The only question left is whether it holds up in a real lab 

 

How I tested it

 

The environment looked like this:

  • Veeam V12 (build 12.3.2.4465) writing to a bucket named v-12 to test Veeam V12 Immutability
  • Veeam V13 (build 13.0.1.180) writing to a bucket named v-13 to test Veeam V13 Immutability
  • One single Linux VM as the workload
  • Active Full with 30 days retention
  • 30 days immutability set on the bucket
  • Storage Optimization set to 1 MB, everything else left at default
  • Default block generation period of 10 days for S3 compatible storage

Here are the screenshots from the v-13 bucket, the same goes for the v-12 bucket.

And the method was easy. I enabled S3 audit logging on the object storage, so that every single API call hitting the member bucket was recorded to a dedicated log target. After each Veeam operation I pulled the logs (using the S3 Browser) and then cleared them, so every run stayed quite comparable.

 

Enabling S3 Audit Logging on my Object Storage System

 

I’m using an Everpure FlashBlade here for the object storage testing and the audit policy I created captures every API call against the member bucket and writes it to a dedicated log target bucket. This is the raw API data behin all the numbers you can see in this blog post.

Veeam V13 Immutability Log Target

While Object and Bucket access auditing was always there, the auditing of S3 API Calls came into the FlashBlade platform in March 2026. Based on filters you can define a scope to audit everyting or only specifc API calls, prefixed and more. In this case we just monitor everything.

 

The Python script to actually count and collect

 

A little Python script then parsed the log files and counted the API calls per operation type, and finally plotted how many PutObject, PutObjectRetention, ListBucketObjectVersions and other calls each version produced.

I published the script I used on Github so you can see it here: https://github.com/falkobanaszak/s3-api-calls

 

import pandas as pd

import matplotlib.pyplot as plt

import json

import glob

import os

 

# Folder mit den Log Files

log_directory = '/Users/fbanaszak/Downloads/veeamapi'

# Suche nach .log, .json and .jsonl files

file_patterns = ["*.log", "*.jsonl", "*.json"]

 

all_events = []

 

print("--- Starting Analysis ---")

 

# Alle Files im angegebenen Directory durchsuchen

files_to_process = []

for pattern in file_patterns:

    files_to_process.extend(glob.glob(os.path.join(log_directory, pattern)))

 

if not files_to_process:

    print(f"Keine Files mit {file_patterns} in '{log_directory}' gefunden.")

else:

    print(f"{len(files_to_process)} Files gefunden - jetzt lese Ich die mal ein....")

    

    for file_path in files_to_process:

        with open(file_path, 'r', encoding='utf-8') as f:

            for line in f:

                line = line.strip()

                if not line: continue

                try:

                    obj = json.loads(line)

                    if 'eventName' in obj:

                        all_events.append(obj['eventName'])

                except json.JSONDecodeError:

                    # Falls JSON falsch formatiert ist wird hier ignoriert

                    continue

 

# Ergebnisse und alles was eingelesen wird, wird jetzt verarbeitet in nen Chart

if not all_events:

    print("No valid 'eventName' entries foud.")

else:

    # Panda Series Konvertierung, braucht Python anscheinend um besser zu zählen, keine Ahnung wieviele Stunden ich hier verschwendet habe...

    series = pd.Series(all_events)

    counts = series.value_counts()

 

    print(f"\nErgebnis der Zählung:")

    print(counts)

 

    # Bar Chart generieren

    plt.figure(figsize=(12, 8))

    

    # Bar Chart

    counts.plot(kind='bar', color='steelblue', edgecolor='black')

    

    plt.title(f'Verteilung aller API-Calls ({len(all_events)} Events insgesamt)', fontsize=14)

    plt.xlabel('API Call (eventName)', fontsize=12)

    plt.ylabel('Häufigkeit', fontsize=12)

    

    # Damit die Namen der API Calls nicht überlappen

    plt.xticks(rotation=45, ha='right')

    

    # Gitter für bessere Lesbarkeit

    plt.grid(axis='y', linestyle='--', alpha=0.7)

    

    plt.tight_layout()

    

    # Speichern oder Anzeigen

    # plt.savefig('api_calls_chart.png') # Optional: als Bild speichern

    plt.show()

 

The log files produced by the logging I activated on the Object Storage system (in this case an Everpure FlashBlade) look like that:

You can see the little red arrow, which are the “eventName” entries the Python script is collecting and plotting.

 

Veeam V13 Immutability Logs S3

Every table in this blog post is based on the outputs from the python script, which prints the results to the console but also plots it into a chart. The charts look like this:

 

Active Full with Veeam V12

 

active full v12 api calls

 

Active Full with Veeam V13

 

active full v13 api calls

Test Sequence

 

The test sequence was: one Active Full with V12, pull and delete logs. Then a weekend of incremental runs with V12, pull and delete logs. And then exactly the same two steps again with V13. That gave me two nice, clean comparisons, a single Active Full and a full weekend of incrementals for storage consumption, total API calls and, most importantly, PutObjectRetention.

By the way, a quick look at a raw log line confirms the immutability metadata is sitting right there on every write. Each PutObject event carries x-amz-object-lock-mode: COMPLIANCEtogether with an x-amz-object-lock-retain-until-date ,which is exactly the Object Lock behaviour we want to see for immutable backups.

 

Results of the Active Full backup

 

The first comparison is a single Active Full of one VM. Here is what my Python counter returned.

API operation V12 (calls) V13 (calls) Difference Change
PutObject (data upload) 132,284 154,233 + 21,949  + 16%
ListBucketObjectVersions 258 198 – 60  – 23%
GetObject (index / metadata reads) 189 74 – 115  – 61%
HeadObject / HeadBucket 72 43 – 29  – 40%
ListObjects / ListObjectsV2 39 5 – 34  – 87%
GetBucketLocation / Acl / Cors / etc. 37 0 – 37  – 100%
PutObjectRetention 15 12 – 3  – 20%

 

PutObject went up by roughly 16%, which is expected, that is the actual data being written, and it is the cheap high-volume operation you never really worry about in object storage. Everything else went down, and some categories collapsed completely.

 

First findings after the Active Full

 

Three things jumped out at me immediately.

First, there are clearly far fewer “search” requests. In V12, Veeam constantly asks the bucket where it is (GetBucketLocation), how it is configured and which objects live in it. In V13 this behaviour is almost completely eliminated. ListObjects In particular dropped by nearly 90% and ListObjects is a so-called “Class A” request, meaning it is one of the more expensive call types.

Second, there is much better metadata handling. GetObject fell from 189 to 74 calls, which tells me V13 does not need to “look up” what is already sitting in the bucket nearly as often. It looks like Veeam optimized the local cache or the index management to require fewer read accesses against the S3 backend.

Third, there are no more redundant inits. V12 ran a whole batch of checks on every run (GetBucketAcl, GetBucketCors , GetPublicAccessBlock), and in V13 those simply do not appear anymore.

Ok let’s run the incrementals now and see what the differences are.

 

Incrementals with Veeam V12 Immutability

 

Veeam V12 Immutability Incrementals

 

Incrementals with Veeam V13 Immutability

 

Veeam V13 Immutability Incrementals

 

Results of a whole weekend of incrementals

 

The Active Full is of course only half the story. The immutability model really shows its value during the ongoing incremental runs, because that is exactly when V12 keeps extending locks across the chain. Here is the weekend comparison.

API operation V12 (calls) V13 (calls) Difference Change
PutObject (data upload) 1,919 181,905 + 179,986 see note
ListBucketObjectVersions 14,501 6,813 – 7,688  – 53%
GetObject (reads) 6,402 5,778 – 624  – 10%
HeadObject (metadata) 5,196 3,207 – 1,989  – 38%
PutObjectRetention (lock) 1,770 299 – 1,471  – 83%

 

The PutObject line looks dramatic, and honestly I cannot fully explain the size of that jump tbh. But as I said above, PutObject is not the operation that hurts you in object storage. It is a cheap and expected data-write call, so I am really not losing any sleep over it. Furthermore, this is a lab and I possibly have done stupid things there.

The rows that matter tell exactly the story Veeam promised. ListBucketObjectVersions those expensive “what is in here ?” lookups dropped by more than 50 %

 

The Object Lock efficiency is the real headline

 

The single most convincing number for me is PutObjectRetention : 1,770 calls in V12 versus only 299 calls in V13, which is an 83% reduction. This is the API call Veeam uses to extend immutability locks, and it is precisely where the difference between chain-based and restore point-based becomes clear. V12 keeps re-stamping the retention dates across the chain, while V13 sets the lock once and moves on.

This maps almost perfectly onto Veeam’s claim of an 8x reduction in PutObjectRetention calls. In other words: Veeam actually isn’t lying ! And this was not a “best conditions” test either it was a straightforward lab test with default settings.

 

A quick word on S3 API call costs

 

Why do I keep obsessing over ListObjects and PutObjectRetention instead of that huge PutObject count ? Because S3 API calls are not priced equally at all. They fall into different cost classes.

Class Example operations Cost factor
Class A PutObject, ListObjects, ListBucketObjectVersions, CopyObject Expensive (~$0.005 per 1,000 calls)
Class B GetObject, HeadObject, HeadBucket Cheap (~$0.0004 per 10,000 calls)
Free / Management DeleteObject Often free

 

(These are public S3 pricing numbers , use them as a directional guide.. Source = Trust me bro (or AWS calculators) )

The expensive Class A operations especially the list and the retention calls are exactly the ones V13 slashes. And that is what turns “fewer API calls” from a nice sounding stat into a real line item on your bill.

A little TCO example

 

To make the cost impact a bit more tangible, let’s imagine these call counts running against AWS S3 pricing.

Category Example operations V12 (count) V12 cost V13 (count) V13 cost
Class A PutObject, List, Retention 189,567 $0.9478 18,969 $0.0948
Class B GetObject, HeadObject 9,518 $0.0004 12,531 $0.0005
Total   199,085 $0.9482 31,500 $0.0953

 

The absolute numbers here are tiny, because this is one single VM. But now imagine these were petabytes across a full production estate ! The Class A cost alone drops by roughly 10x. So Veeam is effectively saving you money through a pure software optimization, without you having to change your storage or your process. Especially for Service Providers who bill on QoS and API consumption this difference compounds really quickly.

 

What I would still like to test

 

I want to be honest about the limits of this test. It is one single VM over one single weekend, not a full 30 day run at scale. To make the results even more meaningful you would actually want to let the whole thing run for the complete 30 days retention window (with the 10 day block generation period fully in play) and push far more data through it, so you can really see the effect on large datasets instead of a single workload. Storage consumption in particular is not very meaningful with only one VM.

So here are a couple of follow-up tests I would love to run:

  • CPU and RAM utilization on the Veeam components during runtime
  • Storage performance during runtime
  • Consumption at scale, with a lot more workloads than just one
  • Restores Veeam says nothing about how V12 and V13 behave differently on restores from object storage, and I think that would be genuinely interesting to measure
  • Full TCO calculations, which could be especially spicy for Service Providers

If you want to benchmark your own object storage properly before you even get to this point, have a look at my Veeam Object Storage Benchmarking – Part 4 post where I cover exactly that with Warp from Minio.

 

Wrap up on Veeam V13 immutability

 

So, do the Veeam V13 immutability improvements really live up to the hype ? Based on my logs yes, they really do. In a clean lab with default settings, the shift from chain-based to restore point-based immutability produced exactly the pattern Veeam promised: a big drop in expensive Class A operations, a collapse of redundant metadata and init calls, and an 83% reduction in PutObjectRetention calls during the ongoing incrementals. The PutObject count did go up, but that is the cheap and expected data-write operation which never drives your bill anyway.

Ultimately, if you are running Veeam against on-premises S3 compatible object storage, the V13 immutability model is a meaningful efficiency upgrade ! Less storage creep, fewer API calls and lower cost while keeping the exact same immutability guarantees you already rely on. I would still love to validate this over a full 30 day cycle at scale, and I will definitely keep testing.

Did you run your own before-and-after on V13 already ? I would love to compare experiences, so feel free to drop a comment below or reach out to me. And if you want more on this topic, make sure to check out all my Veeam blog posts 

 

The original post can be found here:  https://www.virtualhome.blog/2026/07/22/veeam-v13-immutability/

 

14 comments

keiranshelden
Forum|alt.badge.img+1

Great Post Falko!


falkob
Forum|alt.badge.img+13
  • Author
  • Veeam Vanguard
  • July 22, 2026

Great Post Falko!

Thank you Keiran, some serious efforts spent here. It was a presentation for our german Veeam Usergroup and I decided to put it into a written blog post format so anyone in the community can make use of it.


Forum|alt.badge.img+3
  • Veeam Product Management
  • July 22, 2026

Very interesting post ​@falkob, will re-read a bit more a bit later but wanted to comment on the PUTObject Discrepancy here:

The PutObject line looks dramatic, and honestly I cannot fully explain the size of that jump tbh. But as I said above, PutObject is not the operation that hurts you in object storage.

 

PutObject corresponds to an actual writing of a data block from the backup; backup data is stored in VBKs / VIBs as blocks as set in the Storage Optimization settings on the job (blocksize), and this refers to total size of the backup. If you see more PUT Object calls, it’s not a change in the process to writing to object storage repositories, it’s a change in the amount of data that was written. For the first test, I would assume that time had passed and more data accumulated on the source machine OR a different machine was used or different block size. 

For the item referenced above, I regret to tell that probably it was a script parsing error OR a logging / reporting error from the object storage ;) Assuming Blocksize for the backup was set at 1 MB, that would mean that the record there reflects a 1.9 GiB Full backup vs a 177 GiB Full backup, which I’m guessing was not the case in your situation :)

I suppose it could be that the v12 stat was accidentally checking an incremental backup, as 1.9 GiB seems about right for an incremental

But otherwise interesting article and testing!


NZ_BenThomas
Forum|alt.badge.img+4

Awesome work, love a deep dive like this. Thanks for taking the time to test it! 


Ian Sanderson
Forum|alt.badge.img+7

Great Post Falko!

+1

love the time and attention on your research Falko. 83% reduction in calls is no joke! Thanks for putting this together. 


falkob
Forum|alt.badge.img+13
  • Author
  • Veeam Vanguard
  • July 22, 2026

Very interesting post, will re-read a bit more a bit later but wanted to comment on the PUTObject Discrepancy here:

The PutObject line looks dramatic, and honestly I cannot fully explain the size of that jump tbh. But as I said above, PutObject is not the operation that hurts you in object storage.

 

PutObject corresponds to an actual writing of a data block from the backup; backup data is stored in VBKs / VIBs as blocks as set in the Storage Optimization settings on the job (blocksize), and this refers to total size of the backup. If you see more PUT Object calls, it’s not a change in the process to writing to object storage repositories, it’s a change in the amount of data that was written. For the first test, I would assume that time had passed and more data accumulated on the source machine OR a different machine was used or different block size. 

For the item referenced above, I regret to tell that probably it was a script parsing error ;) Assuming Blocksize for the backup was set at 1 MB, that would mean that the record there reflects a 1.9 GiB Full backup vs a 177 GiB Full backup.

 

But otherwise interesting article and testing!

Thank you David, indeed it was a shared lab and I do not know what happened to the machines in between the runs :D And all in all this was not a “100% clean test” as there is so much going on in a shared lab environment I used. Furthermore like you wrote, it could be a script error as well. All in all I think it’s stilly pretty interesting in how V13 significantly reduces the API calls with a very “rude” and “straightforward” test I did in the lab. This obviously gets even more meaningful with a clean environment and an at scale testing

 


AndreAtkinson
Forum|alt.badge.img+7

+1 great post mate. 


Forum|alt.badge.img+3
  • Veeam Product Management
  • July 22, 2026

Thank you David, indeed it was a shared lab and I do not know what happened to the machines in between the runs :D And all in all this was not a “100% clean test” as there is so much going on in a shared lab environment I used.

Aha, to be clear it’s very good testing, thank you for your work here!

I updated my post after you quoted as I have a theory that the script maybe accidentally reported on an incremental for the v12 value there, 1.9 GiB would make sense for a VIB, but this is purely specualtion.


falkob
Forum|alt.badge.img+13
  • Author
  • Veeam Vanguard
  • July 22, 2026

Thank you David, indeed it was a shared lab and I do not know what happened to the machines in between the runs :D And all in all this was not a “100% clean test” as there is so much going on in a shared lab environment I used.

Aha, to be clear it’s very good testing, thank you for your work here!

I updated my post after you quoted as I have a theory that the script maybe accidentally reported on an incremental for the v12 value there, 1.9 GiB would make sense for a VIB, but this is purely specualtion.

This could probably be the issue, the testing itself is feasible for any object storage system which allows you to collect API Call logs, so it is easy reproducable when you have a clean environment + an object storage system supporting the API call log collection. That being said, if anyone has a lab I’m happy to do it all again. <3 Object Storage


Forum|alt.badge.img+3
  • Veeam Product Management
  • July 22, 2026

Aha, I ran into similar issues when doing similar tests for some internal documentation -- what I ended up doing was polling the backup session from PowerShell and getting the start times, then pulling out that time range from CloudTrail logs and only then parsing the request types.

If you still have your logs locally, you can probably do the same.


  • New Here
  • July 22, 2026

Thanks a lot for this interesting post ​@falkob 

Did you or someone else checked if the Veeam sizer takes it into consideration for V13 vs v12 sizings including S3 and Object storage Direct backup ?


Chris.Childerhose
Forum|alt.badge.img+22

Excellent research Falko.  It is interesting to see this laid out with the charts and love the reductions.  Keep up the great work 👍


coolsport00
Forum|alt.badge.img+23
  • Veeam Legend
  • July 22, 2026

Very well done here Falko. Appreciate the efforts and the well-written post to share with the Community! I dig seeing number-crunching on performance enhancements with Veeam (& other solutions, tbh). Thanks for sharing!


TylerJurgens
Forum|alt.badge.img+6

One thing I always noticed back when I was managing MinIO clusters, was the sheer number of PutObjectRetention (lock) calls that would happen during backup operations, which makes sense as Veeam would go through and ensure each object was immutable. I confess I spent a great deal of time watching the MinIO API calls roll through live, just to gain a better understanding of what we were seeing in terms of performance. Seeing this kind of significant decrease in API calls is awesome. 

That said, it really did make me appreciate more how to properly size a MinIO cluster, what EC sets to use and what the implications of each are. Not all of us had fancy Everpure storage to use. 😉

Really awesome to see you dig down on what is going on behind the scenes. I would love to see a follow up on the stats after running it for 30-60 days.