# RAUC makes firmware updates interruptible, signed and atomic

> RAUC is the update controller for embedded Linux, acting both as a target service that installs signed bundles and as a host tool that creates, signs and inspects them. Its guarantees are fail-safe and atomic, it supports hardware security modules through PKCS#11, and it can stream a bundle over HTTP instead of storing it on the device.

**rauc/rauc** — Safe and secure software updates for embedded Linux

- Repository: https://github.com/rauc/rauc
- Website: https://rauc.io
- Stars: 1,265 · Forks: 273
- Language: C
- License: LGPL-2.1
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/rauc-rauc

## The update client and the bundle tool in one project

RAUC, the Robust Auto-Update Controller, controls the update process on embedded Linux systems, and the description makes its dual role explicit. It is both a target application that runs as an update client and a host tool that allows you to create, inspect and modify update files, the bundles.

That duality is the design decision that makes the project usable. The thing that signs a bundle on a build server and the thing that installs it on a device are the same codebase, which means the format they agree on cannot drift between two implementations. The host side can create and sign update bundles, resign bundles, encrypt bundles and inspect bundle files, and the target side runs as a system service over D-Bus, installs bundles, views system status information and changes the status of symmetric, asymmetric or custom slots.

The audience is a team with devices in the field. That is a different problem from updating a server, and the difference shows up in the constraints. A device may be on a bad network, may lose power mid-update, may be in a state nobody can reach physically, and may be the only copy of the data the customer cares about. A framework designed for that world optimises for not breaking and for being verifiable from the other side of the world, not for throughput.

The project is C, licensed under LGPL-2.1, with the documentation on readthedocs and the project site at rauc.io. Chat is an IRC channel on libera.chat, bridged to a Matrix room, and the repository carries badges for continuous integration, coverage, Coverity, CodeQL, OSS-Fuzz, the OpenSSF best practices and the OpenSSF scorecard, which is a list of the tools a security-sensitive project is expected to be measured by.

## Fail-safe and atomic, and where the boot state lives

The first feature group is the one that justifies the project, and its wording is precise.

An update may be interrupted at any point without breaking the running system. There is an update compatibility check. And bootloader updates are atomic, for eMMC boot partitions, for MBR and for GPT.

The mechanism behind that is redundancy, and it is the target requirement the documentation is most explicit about. A device needs boot state storage, and where that storage is depends on the bootloader. For GRUB it is an environment file on the SD card, eMMC, SSD or disk. For Barebox it is a state partition on EEPROM, FRAM, MRAM or NAND flash. For U-Boot it is an environment variable. For EFI it is EFI variables. And for a custom bootloader it depends on the implementation.

The device also needs boot target selection support in the bootloader, and enough mass storage for two symmetric, asymmetric or custom slots. Two is the number that matters: the redundancy is what makes an interrupted update harmless, because the running system keeps running from the slot that was never touched, and the new slot is only marked bootable once it is complete.

There is one more target requirement that is easy to overlook and that the documentation marks as optional but recommended: a hardware watchdog, and an optional real time clock. The watchdog is the backstop for a device that hangs during a boot attempt, and an RTC is what lets the system tell that it has been running long enough to be trusted.

This is also the honest limitation of the design. Redundancy costs storage, and the state storage has to live somewhere the bootloader can reach, so the device's flash layout is partly a RAUC decision. A device designed without a second slot and without a place to record boot state cannot use this approach, and that is a hardware conversation rather than a software one.

## Signing, hardware keys and encryption for several recipients

The second feature group is about proving where the update came from.

Updates are cryptographically signed and verified using OpenSSL, with signatures based on x.509 certificates. Keys and certificates on PKCS#11 tokens are supported, which is the phrase that matters in practice: the signing key can live in a hardware security module rather than in a file on a build server, and a leaked build machine then cannot produce a bundle that the fleet will accept.

The verification side is the same story from the other end. The device holds the public certificates, checks the signature of what it downloads, and refuses an update that is not signed by a key it trusts. Combined with the compatibility check from the previous section, that gives two independent reasons for a device to reject an update, one about who sent it and one about whether it can run it.

Encryption is a separate feature, and it is specifically bundle encryption for multiple recipients, so a bundle can be encrypted once and read by several parties that hold a key, which is a different requirement from signing and is usually needed when the bundle contains confidential material or when different fleets are meant to accept different bundles.

Control is over D-Bus, and the network layer is libcurl, which gives the supported protocols: https, http, tftp and ftp, and ssh. Using libcurl rather than a bespoke HTTP client means the update path goes through a well reviewed TLS stack, and it means a site with an unusual network setup has more than one protocol to work with.

Several layers of update customization are offered, and this is the part that decides whether the framework fits a specific product: update-specific extensions, which are hooks, system-specific extensions, which are handlers, and a fully custom update script. A device that needs to write a device-specific calibration file after a root filesystem update can hook that, rather than wrapping the whole update process in a shell script of its own.

## Redundancy setups, slots and the storage matrix

The redundancy configuration is flexible, and the three named setups cover most device designs.

The symmetric setup is a root filesystem A and B. The asymmetric setup is a recovery slot and a normal slot, which is the shape you want when the recovery image is smaller and holds a minimal system. Beyond those there are application partitions and data partitions, and the ability to group multiple slots, root filesystem and application filesystem together, as a single update target.

That grouping is worth pausing on, because it is the difference between updating a whole device in one transaction and updating its parts independently. If the application filesystem and the root filesystem are one target, then an update either lands on both or on neither, and a device that boots a new application on an old root filesystem cannot happen. That is a correctness property that costs you the ability to release them separately.

Data migration is supported as well, which is the mechanism for the case where the new system expects a different layout of the data partition.

The storage support list is long, and it tells you what a device is expected to be made of. Read-only filesystems include SquashFS, EROFS and dm-verity protected images. Read-write filesystems include ext4, VFAT, UBIFS and JFFS2. There is support for eMMC boot partitions, for UBI volumes, for raw NAND flash using nandwrite, for raw NOR flash using flashcp, for an MBR partition table and for a GPT partition table.

The read-only list is the one that shapes device decisions, and the presence of dm-verity and SquashFS next to the redundant slot model says something about the intended architecture: a system that cannot be written to, verified on boot, and updated by swapping in a new signed image, with the state of that swap recorded outside the filesystem.

## Three ways to get the bytes to the device

How the bundle reaches the target is the part of the design where the trade-offs are most visible, and RAUC offers three mechanisms with quite different storage requirements.

The first is built-in HTTP(S) streaming mode, where no intermediate storage on the target is required. The bundle is consumed as it arrives, which matters for devices with little free space and for large images.

The second is delta-like adaptive update support, with transparent selection of download optimization. The readme describes it as an alternative network delta-streaming mode using the casync tool, with chunk-based binary delta updates, a special bundle format and an external chunk store. The storage consequence is stated in the target requirements: adaptive updates need some on-target storage for metadata, and casync-based updates need temporary on-target storage for the downloaded chunks.

The third is the plain case, an external non-streaming download, which needs sufficient temporary on-target storage space for the compressed bundle.

That is a genuinely useful set of options rather than one mechanism with flags, because a fleet is rarely homogeneous. Devices on good links and on bad links can be served differently, and a device with plenty of flash and a device with almost none can both be supported. What the documentation does not present is a throughput or bandwidth comparison between them, so the choice of mode is a judgement about your network and your storage rather than a decision with a number attached.

Worth noting for a fleet operator: the update source is independent of the tool. A simple web server will do, and so will a software provisioning server such as hawkBit, with a companion updater project for that case, and a USB stick works as well.

## Building it, and running the test suite under QEMU

The build is Meson, and the host prerequisites are the usual set for a project that talks to D-Bus, to a TLS library and to GLib:

```bash
sudo apt-get install build-essential meson libtool libdbus-1-dev libglib2.0-dev libcurl3-dev libssl-dev
```

Two optional host packages follow from features. HTTP(S) streaming support needs the netlink protocol headers, and JSON support needs libjson-glib-dev. The build itself is three commands, and the project is prepared for cross compilation because it is both a host tool and a target service:

```bash
git clone https://github.com/rauc/rauc
cd rauc
meson setup build
meson compile -C build
```

For a target device, the documentation strongly recommends building through an embedded distribution build suite such as Yocto or OE, PTXdist or Buildroot, and installing the result rather than running it from the build directory, because installing also unpacks the service and D-Bus configuration files needed to run it properly. The install step is meson install.

The target side has its own requirement list, and it is a list of kernel options that have to be enabled, either built in or as modules: CONFIG_MD, CONFIG_BLK_DEV_DM, CONFIG_BLK_DEV_LOOP, CONFIG_DM_VERITY, CONFIG_SQUASHFS and CONFIG_CRYPTO_SHA256, plus CONFIG_BLK_DEV_NBD for streaming support, and CONFIG_DM_CRYPT and CONFIG_CRYPTO_AES for encryption support. If you are using Busybox tar to unpack bundles, two Busybox features are needed as well, the tar autodetect and long options ones.

The test suite runs against QEMU, and the dependencies for it are listed explicitly: qemu-system-x86, the time tool, squashfs-tools, e2fsprogs and a set of Python packages including pytest, the D-Bus Python bindings, aiohttp and requests. The repository layout supports that testing story, with a qemu-test script and its configuration files at the top level, a tap-test directory, a fuzz/ directory, a scan-build wrapper, and a subprojects/ directory for the vendored dependencies.

## Conclusion

RAUC suits a team shipping Linux devices in the field that has to update them remotely, cannot accept a bricked device, and needs the update to be signed by a key it controls. It is the wrong choice if your device has no redundant storage, because the whole design assumes two slots and somewhere to record the boot state, and a poor match if you want updates over cellular links with minimal bandwidth, because the streaming modes trade storage for complexity rather than for bytes on a slow link. Before deploying, decide which bootloader records the boot state, because that choice constrains the storage layout, and read the target kernel options, since several of them are not enabled by default in a vendor kernel.

## FAQ

### What does RAUC guarantee when an update is interrupted?

An update may be interrupted at any point without breaking the running system, because the design keeps two slots and only marks the new one bootable once it is complete. There is also an update compatibility check, and bootloader updates are atomic for eMMC boot partitions, MBR and GPT.

### How are RAUC bundles signed and what hardware is supported?

Updates are cryptographically signed and verified using OpenSSL, with signatures based on x.509 certificates, and keys and certificates on PKCS#11 tokens are supported so the signing key can live in a hardware security module. Bundle encryption for multiple recipients is a separate feature from signing.

### What are the storage requirements for a device using RAUC?

The device needs boot state storage, whose location depends on the bootloader, boot target selection support, and enough mass storage for two symmetric or asymmetric slots. Bundle storage depends on the mode: streaming needs none on the target, adaptive updates need some metadata space, and casync-based updates need temporary space for chunks.

### How do I build and test RAUC?

Install the host prerequisites, then clone, run meson setup build and meson compile -C build, and install on the target with meson install, ideally through an embedded build suite such as Yocto or Buildroot. The test suite runs under QEMU and needs qemu-system-x86, squashfs-tools, e2fsprogs and the listed Python packages.

## Sources

- [License: LGPL-2.1](https://github.com/rauc/rauc/blob/master/LICENSE)
- [Project website](https://rauc.io)
- [rauc/rauc on GitHub](https://github.com/rauc/rauc)
- [README](https://github.com/rauc/rauc/blob/master/README.md)
- [Releases](https://github.com/rauc/rauc/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rauc-rauc
