This is a project that enables OpenWrt to be installed using an A/B partition scheme.
Caution
The OpenWrt A/B Partition Project is designed for x86_64 systems.
It will probably not work for other architectures without modification.
- A Linux live USB stick with
wget
/curl
, along with bothparted
andfdisk
. - A computer to install OpenWrt to, with at least 8 GiB of storage.
The initial setup for OpenWrt A/B partitioning is entirely manual.
Please follow these instructions carefully.
(Start by booting up your device with the Linux live USB stick.)
First, you will need to download (or copy) both the generic-squashfs-combined-efi
and
generic-squashfs-rootfs
images.
wget "https://archive.openwrt.org/releases/23.05.1/targets/x86/64/openwrt-23.05.1-x86-64-generic-squashfs-combined-efi.img.gz"
wget "https://archive.openwrt.org/releases/23.05.1/targets/x86/64/openwrt-23.05.1-x86-64-generic-squashfs-rootfs.img.gz"
The next step is to identify the internal storage device to install OpenWrt on:
lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
# loop0 7:0 0 766.5M 1 loop /run/archiso/airootfs
# sda 8:0 0 29.8G 0 disk
# ├─sda1 8:1 0 16M 0 part
# └─sda2 8:2 0 29.7G 0 part
# sdb 8:16 0 1.9G 0 disk
# ├─sdb1 8:17 0 917M 0 part
# └─sdb2 8:18 0 15G 0 part
If you are not sure which device is what, ls -l /dev/disk/by-id
may help:
ls -l /dev/disk/by-id
# total 0
# lrwxrwxrwx 1 root root 9 Feb 27 13:00 ata-DEVICE_NAME_DEVICE_SERIAL -> ../../sda
# lrwxrwxrwx 1 root root 9 Feb 27 13:00 ata-DEVICE_NAME_DEVICE_SERIAL-part1 -> ../../sda1
# lrwxrwxrwx 1 root root 9 Feb 27 13:00 ata-DEVICE_NAME_DEVICE_SERIAL-part2 -> ../../sda2
# lrwxrwxrwx 1 root root 9 Feb 27 13:00 usb-USBSTICK_NAME_DEVICE_SERIAL -> ../../sdb
# lrwxrwxrwx 1 root root 9 Feb 27 13:00 usb-USBSTICK_NAME_DEVICE_SERIAL-part1 -> ../../sdb1
# lrwxrwxrwx 1 root root 9 Feb 27 13:00 usb-USBSTICK_NAME_DEVICE_SERIAL-part2 -> ../../sdb2
Once you determine which disk to install to, flash the uncompressed -combined-efi
image to it:
gunzip -c *-combined-efi.img.gz | dd of=/dev/sda status=progress
With the base installation complete, you will need to modify it to support an A/B partition scheme.
First, you will need to update the partition table using fdisk
.
Remove partition 2 and replace it with the following three partitions:
Partition ID | Size | Filesystem Type |
---|---|---|
2 | 200+ MiB | Microsoft basic data (type 11) |
10 | 50% remaining space | Linux filesystem (type 20) |
11 | 50% remaining space | Linux filesystem (type 20) |
Next, use parted
to rename each of the partitions:
Partition ID | Name |
---|---|
2 | Persistent |
10 | OpenWrt-A |
11 | OpenWrt-B |
Important
Partition 10
and 11
must be named OpenWrt-A
and OpenWrt-B
respectively.
The partition name is used by abupgrade
to detect which partition to flash.
You will need the partition UUIDs for OpenWrt-A
later, so make sure to
find it using blkid
and write it down:
blkid | grep 'OpenWrt-A'
# ...
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
# /dev/sda10: BLOCK_SIZE="262144" TYPE="squashfs" PARTLABEL="OpenWrt-A" PARTUUID="38fe120b-f6d5-4921-888d-1ffa0b1bc370"
Now you will need to re-flash the root filesystem to partition 10:
gunzip -c *-rootfs.img.gz | dd of=/dev/sda10 status=progress
After this, you will want to create a filesystem on partition 2 using mkfs.vfat
.
This will be where you store openwrt-abpp
.
Finally, you will need to rename the kernel image and update the GRUB configuration.
The grub.cfg
file is located on partition 1.
mkdir -p /mnt/efi
mount -t vfat /dev/sda1 /mnt/efi
mv /mnt/efi/boot/vmlinuz /mnt/efi/boot/vmlinuz-a
nano /mnt/efi/boot/grub/grub.cfg
The original GRUB config will like something like this:
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 --rtscts=off
terminal_input console serial; terminal_output console serial
set default="0"
set timeout="5"
search -l kernel -s root
menuentry "OpenWrt" {
linux /boot/vmlinuz root=PARTUUID=abcdef01-2345-6789-abcd-ef0123456789 rootwait console=ttyS0,115200n8 noinitrd
}
menuentry "OpenWrt (failsafe)" {
linux /boot/vmlinuz failsafe=true root=PARTUUID=abcdef01-2345-6789-abcd-ef0123456789 rootwait console=ttyS0,115200n8 noinitrd
}
Tip
If your device does not have a serial interface, you will want to comment out the serial
and terminal_input
lines.
You will need to convert this into a template.
-
Copy the first non-failsafe
menuentry
to the end of the file. -
Prepend a
#
to the beginning of each line of the originalmenuentry
blocks. -
Surround the original menuentry blocks with:
###----- BEGIN ABPP TEMPLATE -----### ###----- END ABPP TEMPLATE -----###
This will now be the template section.
-
In the template section:
- Append
-${LETTER}
after/boot/vmlinuz
. - Append
${VERSION}
after"OpenWrt"
. - Replace
root=PARTUUID=...
withroot=${PARTITION}
- Append
-
Surround the copied menuentry with:
###----- BEGIN ABPP GENERATED -----### ###----- END ABPP GENERATED -----###
This will be the generated section.
-
In the generated section:
- Update the
PARTUUID
to the UUID recorded earlier. - Append
-a
after/boot/vmlinuz
.
- Update the
Caution
It is critical that the section markers have the exact spacing and number of #
and -
characters.
The section markers are used to update the GRUB configuration after upgrades, and it will not be possible to do automatically unless those exact strings are found in the file.
After you are finished, it should look similar to this:
# serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 --rtscts=off
# terminal_input console serial; terminal_output console serial
set default="0"
set timeout="5"
search -l kernel -s root
###----- BEGIN ABPP TEMPLATE -----###
# menuentry "OpenWrt ${VERSION}" {
# linux /boot/vmlinuz-${LETTER} root=${PARTITION} rootwait console=ttyS0,115200n8 noinitrd
# }
#
# menuentry "OpenWrt ${VERSION} (failsafe)" {
# linux /boot/vmlinuz failsafe=true root=${PARTITION} rootwait console=ttyS0,115200n8 noinitrd
# }
###----- END ABPP TEMPLATE -----###
###----- BEGIN ABPP GENERATED -----###
menuentry "OpenWrt" {
linux /boot/vmlinuz-a root=PARTUUID=38fe120b-f6d5-4921-888d-1ffa0b1bc370 rootwait console=ttyS0,115200n8 noinitrd
}
###----- END ABPP GENERATED -----###
You may now reboot into OpenWrt.
Once you have booted into OpenWrt and have internet connectivity, you will need to
install the following packages using opkg
:
blkid
block-mount
dumb-init
kmod-fs-squashfs
kmod-fs-vfat
losetup
nsenter
parted
squashfs-tools-unsquashfs
unshare
After installing the packages, configure partition 2 to be mounted on startup.
Finally, download a tarball of this repo and extract somewhere within partition 2.
You are now done! 🚀
To flash a version of OpenWrt to the inactive partition, run the downloaded /path/to/openwrt-abpp/bin/abupgrade
script. It will:
- Let you select a version to flash.
- Download the rootfs.
- Flash the rootfs to the alternate partition.
- Copy your configuration (using
sysupgrade -b
) to the alternate partition. - Download your currently-installed packages to the alternate partition.
- Update GRUB to automatically select the newly-flashed partition.
Once you reboot into the newly-flashed partition, openwrt-abpp will restore your configuration, install your packages, and trigger a reboot to finalize everything.
Essentially, it flashes a new OpenWrt installation and copies/downloads your changes to it.
Behind the scenes, it involves:
- Re-implementing OpenWrt's overlay filesystem initialization code using shell scripts.
- Creating an extremely lightweight Linux container using
unshare
,nsenter
, anddumb-init
. - Using the container to run
opkg
within the newly-flashed installation.