Skip to content

A/B slots or snapshots? Two ways to make an OTA update survive failure

Every over-the-air update has to do four things, and only one of them is “install the new software.”

It has to stage the incoming version somewhere the running system is not using. It has to verify that what arrived is authentic and complete. It has to apply it. And if activation goes wrong, like for example a power cut mid-write, or a kernel that boots but never reaches the application layer, it has to get back to a version that works, without a vehicle going back to a dealer or a technician driving out to a substation.

In this post, I want to compare the two approaches we see most often for that last requirement. A/B partitioning solves it with space: keep a second complete copy. Snapshot-based updating solves it with references: maintain the record of old state and only write what changed. Both approaches work. They lead to quite different platforms, and in our experience, the difference grows every time the software image does.

I should say up front I work on SnapFS, Tuxera’s snapshot-based file system, so I have a view. I have tried to be fair to A/B here, because it earned its place and I think it still belongs in certain architectures. More on that at the end.

How A/B works

A/B partitioning keeps a copy of the software in two identical slots. One is active and running. The other is idle, and the update writes into it while the running system stays untouched.

  1. Download. The payload arrives. It can be a full image or a delta against what the idle slot already holds. Most update frameworks support deltas in some form, though enabling them often takes extra integration work, so many production fleets still ship full images every release.
  2. Write. Whatever the download looked like, the idle slot ends up holding a complete image. This might require reconstructing and calculating diffs.
  3. Verify. The written image is hashed and its signature checked against a key held in the boot chain.
  4. Activate. A small piece of metadata is updated to tell the bootloader which slot to try next.
  5. Boot and confirm. The device restarts into the new slot on a trial basis. If it reaches a defined health checkpoint, it marks itself successful. If it does not, a retry counter runs down and the bootloader falls back to the slot that was working before.
Five-step OTA update sequence compared. The A/B slot flow downloads, writes the inactive slot, verifies, flips the active slot and reboots, with rollback by flipping back to slot A. The snapshot-based flow establishes a common ancestor, sends the difference, applies into a clone, verifies and commits atomically, with rollback to the retained snapshot.
Figure 1. The same five jobs, done two ways. What differs is what gets written and what rollback costs.

The strength of this design is how little it trusts. The rollback decision lives in the bootloader, below the operating system, so it survives almost any failure above it, including a kernel that panics before userspace starts. The two slots never interact, so there is no shared state to corrupt. Even though there is no shared state to corrupt, a hardware failure can still cause both states to fail, as the update sits on the same flash. For a safety case, that independence of both states is easy to argue and easy to test: cut the power at any point in the sequence and enumerate the outcomes. That is why A/B became the default on many vehicle platforms.

What A/B costs

The cost is structural. Every component covered by the scheme is stored twice, permanently, whether or not an update is in progress.

Flash layout compared. A/B slots permanently reserve a second full system image that is idle except during an update, while the snapshot-based layout stores only changed blocks and leaves the rest of the capacity usable for data, logs and models.
Figure 2. Both approaches can roll back a failed update. They reserve very different amounts of capacity to do it.

On a platform where the system image is a meaningful share of total flash, the second slot is capacity that is needed only a few minutes every few months. Three things follow:

  • The duplicate scales with the image, not the change. A release that modifies a few megabytes still needs a slot large enough for the whole image. As zonal controllers absorb functions from separate ECUs, and as inference models, maps and container images move on-device, the duplicated volume grows even though the typical delta does not.
  • The slot is sized once. If a later release grows past it, the fix is a new storage layout, which on a deployed platform means either a hardware change or a constrained software backlog.
  • Every update writes a full image. Reconstructing the whole image in the idle slot writes far more data than the update contains. On automotive-grade eMMC or UFS with a fixed endurance budget over a 15-year service life, that is worth accounting for rather than assuming away.

None of this makes A/B a wrong approach. It makes it expensive in a specific, measurable way, and that cost is what sent us looking for an alternative.

How snapshot-based updates work

A snapshot-based approach moves the rollback guarantee out of a duplicated region and into the file system itself, using copy-on-write.

Implementations differ, so rather than describe an average I will describe how SnapFS does it. Btrfs and Android’s Virtual A/B solve the same problem in their own ways. This is one way to do it, not the only one.

In a copy-on-write file system, modifying a block never overwrites it. The new version is written to free space, and the metadata is updated to point at it. A snapshot uses this: it records the current root of a subvolume, and from that moment the blocks that root references are frozen. Anything the running system changes afterwards is written elsewhere. Blocks nobody touched are shared by the snapshot and the live system, stored once and visible from both.

The second building block is the writable clone, a snapshot you can write into. A clone shares all its storage with the snapshot it came from and only consumes space where it diverges.

An update then looks like this:

  1. Establish the common ancestor. Sender and receiver identify a snapshot they both hold, normally the version currently running.
  2. Transmit the difference. Incremental send and receive computes the delta between two snapshots and transmits only that.
  3. Apply into a temporary clone. The receiver creates a writable clone of the common ancestor and applies the file and metadata deltas to it. The running subvolume is never written during the update. The changes stay hidden from the live system until they are complete, so the work can proceed while the file system is online. If power is lost partway through, the running subvolume is exactly as it was, and the partial clone can be dropped and the update retried.
  4. Verify. Signature and integrity checks over the received state, before anything depends on it.
  5. Activate. The switch is a single atomic commit. It takes effect completely or not at all. On most systems a reboot or an unmount and remount cycle is then needed to bring the changes live, which keeps open handles and caches consistent. What it does not need is a full re-flash.

The health check that decides whether the new version is good is the same kind of logic as in A/B, and it belongs to the update agent rather than the file system. What changes is what rollback costs once that check fails. The prior state is still a snapshot, so returning to it takes the same time whether the subvolume holds 200 megabytes or 20 gigabytes: no restore, no data movement, no second copy to fall back on, because the original blocks were never overwritten.

The storage cost is what changed, plus bookkeeping, plus however many older snapshots you keep. Not the size of the system. In SnapFS, the bookkeeping cost is small, and it is designed so that keeping snapshots does not slow down normal writes.

What snapshots cost

I want to be as direct about our side as I have been about A/B, because the trade-offs are real and they show up during integration rather than on a datasheet.

Update space comes from a shared pool. With A/B, the space for an update is reserved at design time and nothing else can consume it. With snapshots, the incoming update draws from the same free space as everything else, so update headroom becomes a runtime property rather than a fixed allocation. The question I would ask of any file system in this role is what happens when that space runs out mid-update. The operations that free space, such as deleting a snapshot, rolling back, or deleting a file, should still succeed on a completely full volume, because those are exactly the operations you need when an update has gone wrong. We designed SnapFS to guarantee that. Not every copy-on-write file system does, so it is worth asking rather than assuming.

Retention is a policy, not a habit. Every snapshot you keep pins the blocks it references. Keep ten releases and you may be holding a large share of the pool without meaning to. Snapshot lifetime needs an owner and a written rule.

Reclaiming space is deferred work. Deleting a snapshot or rolling back returns immediately, but the blocks it pinned are freed by garbage collection afterwards. That work can be postponed until free space runs low, and it is bounded rather than open-ended, but it is real work and it belongs in the platform’s timing and capacity budget rather than being discovered in the field.

Flash wear tends to go the other way. An A/B update writes a full image every release; a snapshot-based update writes only what changed. Copy-on-write also never overwrites in place, so the device sees mostly fresh, append-like writes, which is the pattern a flash translation layer handles with the least internal write amplification of its own. How large the effect is depends on the FTL in the part you have qualified, so I would treat this as a direction rather than a number.

The rollback domain is narrower than the bootloader’s. A snapshot protects file system state. It does not roll back the bootloader, MCU firmware elsewhere in the system, or anti-rollback counters that deliberately block a return to a version with a known vulnerability. If the failure is below the file system, the file system cannot recover it.

That last point, more than any other, shapes the architecture I would recommend.

Side by side

A/B slotsSnapshot-based
Rollback decision made byBootloader, below the OSBootloader selects the snapshot to mount
Permanent storage reservedOne full duplicate of covered componentsBookkeeping plus retained snapshots
Where the update is stagedThe idle slotTemporary clone with common ancestor
Data written per updateFull or large imageChanged blocks only
Download sizeFull or delta, depending on toolingDelta between two snapshots
Recovers from a kernel that will not bootYesYes
Recovers from a corrupted application updateYesYes
Space for the updateFixed, reserved at design timeShared pool, bounded by quotas
Effect of image growthDuplicated volume grows with itDelta size may grow with the change, not the image
Failure independenceTwo regions share the same hardware; a hardware malfunction can render both regions uselessShared pool and shared metadata
Validation effortWell-trodden, simple state spaceMore states to characterize, especially near-full

They are strong in different places. A/B is strongest at the bottom of the stack, where independence matters most and the thing being protected is small. On smaller ECUs or devices like meters, A/B is a large part of the cost of the device. Snapshots are strongest higher up, where payloads are large and change incrementally.

Why I would use both

On most modern platforms I do not think this is a choice between the two. It is a split:

  • A/B for the boot chain and the minimal system needed to recover, for example bootloader, kernel, initramfs (initial RAM file system). These are small, so duplicating them is cheap, and they are exactly the components a file system cannot roll back on its own.
  • Snapshots for everything that grows. Application stacks, inference models, maps, configuration, container images, calibration data. These are large, change incrementally, and sit above the layer where the file system can guarantee recovery.

That split gives you bootloader-level protection against an unbootable device without paying to duplicate the parts of the image that are actually big. It also tends to match how teams already think about the platform: the recovery-critical core is a different kind of thing from the software that ships new features every quarter.

What I would measure before deciding

If you are weighing this on a real platform, I would want numbers for four things, ideally from field data rather than the design spec:

  • Delta size distribution across recent releases. If a typical release changes a small fraction of the image, duplication is buying very little per byte. If releases routinely rewrite most of it, the two approaches converge and A/B’s simplicity wins.
  • Duplicated volume as a share of total flash. How much capacity sits in the idle slot, and what else could use it: telemetry retention, a larger model, or a smaller flash part on the next bill of materials.
  • Bytes written per update against the endurance budget of the qualified part over the service life. Full-image writes versus delta writes, on the specific part, not a generic one.
  • Fleet bandwidth per campaign. Bytes per update, times fleet size, times campaigns per year. This recurs for the life of the platform and is usually the figure that gets attention outside engineering.

These four are the inputs to any savings estimate, and I would rather give you the method than a headline number. We can help bound them for a specific platform, but a figure quoted without a platform attached is not one I would trust either.

Update strategy and storage architecture are often designed by different people at different times, and that is how platforms end up with a rollback mechanism they cannot afford and headroom they cannot reach. Deciding them together is most of the work.

SnapFS is the file system I work on at Tuxera, and the snapshot mechanism above is how it approaches this problem: snapshots and writable clones for staging, incremental send and receive so an update moves only what changed, and rollback that costs a constant-time operation rather than a duplicated partition. It is built for the constraints these platforms impose: a fixed RAM budget set at configuration time, no dynamic allocation, no recursion, and one on-disk format across Linux, QNX, INTEGRITY, VxWorks and others.

If you are working through this trade-off on a real platform, I would like to compare notes ([email protected]).

If the split I describe above matches your platform, the file system side of it is SnapFS.

See how SnapFS works

Suggested content for:

Our products

Your mission-critical systems demand uncompromising reliability. Tuxera products mean absolute data integrity. We specialize in file systems, software flash controllers, and secure networking and connectivity solutions. We are the perfect fit for data-intensive, mission-critical workloads. Using Tuxera’s time-proven solutions means that your data is safe and secure – always.

Proven success

Our solutions are trusted by major brands worldwide. When you need reliable, scalable, and lightening-fast data access and transfer across any system or device, Tuxera delivers. Our track record speaks for itself. We’ve been in this business for decades with a clear mission: to be the partner you can trust. Read on to find out more.

Related pages and blog posts
Technical Articles
Datasheets & Specs
Whitepapers