🌐 VI | EN

Expand OpenWrt Overlay with Extroot: USB, ext4, and Recovery Paths

Nghia Phan
Nghia Phan
🌐 Bản tiếng Việt Technical Guide Views

extroot uses external storage as a writable overlay, giving OpenWrt room for packages and persistent data without replacing the router’s internal flash. This article follows the OpenWrt documentation from device discovery and partitioning through the post-reboot checks, recovery paths, and advanced options.1

Warning: This procedure touches partitions and filesystems. Back up the configuration, identify the correct /dev/sdX, and treat the parted example below as a destructive command. Never run it unchanged on a disk containing data.

1. What extroot does and where it applies

On many devices, OpenWrt combines a read-only rootfs with writable rootfs_data or ubifs as an overlay. The documentation’s conceptual table is:

ComponentMount pointCompressedWritable
rootfs/romYesNo
rootfs_data or ubifs/overlay, /rom/overlayNoYes
overlay/Depends on the filesystemYes

Extroot adds an overlay on USB, SATA, an SD card, or another block device and mounts it during boot. If the external device is removed, the router still has its internal overlay and can fall back to its pre-extroot state.1

Some firmware without an overlay partition in /proc/mtd may ignore fstab configuration; the source describes using / as the mount point in that special case. Do not make that the default—first inspect the actual layout of the model.

2. Preparation

The source assumes that I can reach a shell through SSH or a UART console. LuCI can edit many of these settings, but shell work makes the device name and command output explicit.1

  • Devices with at least 8 MiB of flash generally have enough room for the required packages. Devices with 4 MiB or less should use the Custom image path instead of trying to squeeze packages into the current overlay.
  • The block tooling supports ext2/3/4, f2fs, btrfs, ntfs, and ubifs; the documentation says that FAT16/FAT32 cannot be used for extroot.
  • The example below uses a USB flash drive, one GPT partition, and ext4. A USB SSD may also need kmod-usb-storage-uas.
  • If the internal flash is nearly full, remove unnecessary packages first. Install the standard packages:
opkg updateopkg install block-mount kmod-fs-ext4 e2fsprogs parted kmod-usb-storage# For a USB SSD, add:opkg install kmod-usb-storage-uas

Find the disk instead of guessing from the example:

ls -l /sys/block

Distinguish a disk such as /dev/sda from a partition such as /dev/sda1. Record the output and confirm that the USB device is visible before moving to the destructive step.

3. Partition and format the USB drive

This is the source’s GPT-plus-ext4 method:

DISK="/dev/sda"parted -s ${DISK} -- mklabel gpt mkpart extroot 2048s -2048sDEVICE="${DISK}1"mkfs.ext4 -L extroot ${DEVICE}

The parted command erases the partition table on ${DISK}. If the disk is not new or contains data, stop and identify the correct disk. Other supported filesystems are possible, but their formatting commands and packages must change; do not keep mkfs.ext4 while intending to use f2fs or btrfs.

4. Create the extroot mount entry

Before writing fstab, confirm that OpenWrt sees the UUID of ${DEVICE} and the current overlay mount point:

echo $(block info ${DEVICE} | grep -o -e 'UUID="\S*"')echo $(block info | grep -o -e 'MOUNT="\S*/overlay"')

If either command returns nothing, stop and check the device, filesystem module, and block info output. Once the output is correct, use the source’s UCI sequence:

eval $(block info ${DEVICE} | grep -o -e 'UUID="\S*"')eval $(block info | grep -o -e 'MOUNT="\S*/overlay"')uci -q delete fstab.extrootuci set fstab.extroot="mount"uci set fstab.extroot.uuid="${UUID}"uci set fstab.extroot.target="${MOUNT}"uci commit fstab

${UUID} comes from the external partition and ${MOUNT} from the existing overlay layout. Do not replace /overlay with another path without understanding the firmware’s layout.

Keep the original overlay reachable

The source’s next method mounts the old overlay at /rwm. This is useful when the original rootfs_data/ubifs must be inspected or its fstab adjusted after the external device owns /overlay:

ORIG="$(block info | sed -n -e '/MOUNT="\S*\/overlay"/s/:\s.*$//p')"uci -q delete fstab.rwmuci set fstab.rwm="mount"uci set fstab.rwm.device="${ORIG}"uci set fstab.rwm.target="/rwm"uci commit fstab

The old overlay may then be available at /rwm; the corresponding upper-layer fstab is /rwm/upper/etc/config/fstab. Use the layout returned by block info.

5. Transfer the data and apply the change

Temporarily mount the external partition at /mnt, then copy the current overlay with the source’s pipeline:

mount ${DEVICE} /mnttar -C ${MOUNT} -cvf - . | tar -C /mnt -xf -

Reboot when the copy is complete:

reboot

Do not remove the USB device before the router has completed the reboot. When configuring over Wi-Fi, use Ethernet or a console so a transient disconnect does not interrupt the operation.

6. Verify after reboot

LuCI

  • Open System → Mount Points: the USB partition should be mounted as overlay.
  • Open System → Software: free overlay space should reflect the external partition.

Command line

The external partition should be mounted at /overlay, while / is an overlayfs using it as the upper layer:

grep -e /overlay /etc/mtabdf /overlay /

A successful result shows /dev/sda1 mounted at /overlay and the available space for / matching /overlay; device names and values will differ on the real router.

{{< ads >}}

7. Troubleshooting and recovery boundaries

If boot has a problem, inspect block discovery, fstab, and the preinit stage:

block infouci show fstablogread | sed -n -e "/- preinit -/,/- init -/p"

UUID mismatch after an upgrade

If the log reports block: extroot: UUID mismatch, mount the partition and remove the two markers named by the source:

mount /dev/sda1 /mntrm -f /mnt/.extroot-uuid /mnt/etc/.extroot-uuidumount /mnt

Replace /dev/sda1 with the real device. Do not remove files from another partition.

FAT/FAT32 is not supported

vfat and FAT32 do not work for extroot. If a USB drive is preformatted as FAT, reformat it as ext4 after installing e2fsprogs:

mkfs.ext4 /dev/sda1

This also erases data on /dev/sda1.

The USB device appears too late

If the partition mounts manually but not during boot, the source suggests increasing delay_root, for example to 15 seconds:

uci set fstab.@global[0].delay_root="15"uci commit fstab

Use a delay only when logs and testing indicate that storage becomes ready late; it is not a universal fix.

Pre-25.xx and post-25.xx firmware

For pre-25.xx releases, the older source material includes a way to permit packages larger than the free space in /rom:

echo option force_space >> /etc/opkg.conf

This is not a replacement for extroot and should not be applied blindly. For post-25.xx releases, the source points to the expand_root guide; follow the release-appropriate mechanism instead of mixing both.

On very old systems where only an /etc/rc.local workaround works, the source also records:

export PREINIT=1mount_root

This is a historical compatibility branch with possible side effects. Prefer standard extroot or the current release’s expand_root method.

Extroot on MMC/SD

For a non-USB block device, modules required to make it visible early must be placed in /etc/modules-boot.d. For SDHCI on MT7688/MT7628, the source gives /etc/modules-boot.d/mmc with:

mmc_coremmc_blocksdhcimtk_sd

The correct modules depend on the SoC and driver; this is the source’s example, not a universal list.

8. Useful additions after extroot

Keep package lists out of RAM

To make package lists survive reboot and reduce RAM use, move the list directory from /var/opkg-lists to /usr/lib/opkg/lists.

In LuCI, open System → Software → Configuration, change lists_dir to /usr/lib/opkg/lists, then open System → Software → Actions → Update lists.

Or run:

sed -i -e "/^lists_dir\s/s:/var/opkg-lists$:/usr/lib/opkg/lists:" /etc/opkg.confopkg update

Create swap on extroot

If a router with roughly 32 MB of RAM cannot read package lists, the source presents a 100 MiB swap file on extroot:

DIR="$(uci -q get fstab.extroot.target)"dd if=/dev/zero of=${DIR}/swap bs=1M count=100mkswap ${DIR}/swapuci -q delete fstab.swapuci set fstab.swap="swap"uci set fstab.swap.device="${DIR}/swap"uci commit fstabservice fstab bootcat /proc/swaps

Swap increases writes to the USB/flash device and does not turn a router into a high-memory server. Use reliable storage, monitor I/O errors, and never put secrets in a swap file.

USB dongles and usb-modeswitch

The source allows adding usb-modeswitch to an image, but warns about dongles combining a CD-ROM, modem, and card reader: if /overlay is on the dongle’s memory card, switching modes can make the filesystem disappear. A safer option is a dongle preconfigured to enable the modem/network adapter and card reader at power-on without switching modes on the router.

The source demonstrates inspecting ports with an AT command:

at^setport?^SETPORT:A1,A2;1,3,2,A1,A2OK

Query port meanings with:

at^setport=?

In the source example, 1 is the modem, 2 is PCUI, 3 is DIAG, 16 is NCM, and A2 is the SD card. Do not disable PCUI (2) or you may lock yourself out of the dongle. A sample configuration sequence is:

at^setport="ff;1,2,3,a2"OKat^resetOKat^setport?^SETPORT:;1,2,3,A2OK

This applies only to a dongle compatible with that AT command set, not to any modem with similarly named ports. The source lists a pre-configuration example for Huawei E3131s-2 firmware v21.158.47.00.1094.

Remote filesystems

The source only links to a guide for fstab failing to mount CIFS at boot; it does not provide a complete CIFS procedure in this article. I do not turn that reference into a generic mount command. Check credentials, network availability, and boot ordering for the actual model and release.

9. LUKS encrypted extroot: advanced branch

The source says that OpenWrt 22.03 does not support opening LUKS reliably before the extroot check. Before starting, create a LUKS container using the disk-encryption documentation, and make sure rootfs_data has room for cryptsetup and its dependencies. Once the volume is unlocked, follow the extroot procedure inside it and copy the data from /overlay.1

Preferred PREINIT method

The source describes a cleaner method: during PREINIT, mount_root looks for block on ROM/overlay and calls block extroot. A wrapper can therefore open the encrypted device before invoking the real binary.

Do not skip these conditions:

  1. Install block-mount and cryptsetup.
  2. Install the executable decrypt.sh script from the disk-encryption documentation.
  3. Move the real /sbin/block binary to /sbin/block.bin.
  4. Place the wrapper at /upper/sbin/block on a UBIFS overlay, or /sbin/block when already on overlayfs, and make it executable.
  5. Use /.use_crypt_extroot on overlayfs or /upper/.use_crypt_extroot on /overlay as the enable marker.
  6. Set up /etc/crypttab before rebooting.

When the marker is absent, encrypted extroot remains disabled, which provides a way to turn it off from failsafe. The source includes the complete wrapper code, but it changes PREINIT behavior, creates device nodes, and loads kernel crypto modules; it should be checked against the actual kernel rather than copied into another model without review.

Full PREINIT wrapper

The following is the complete wrapper from the source. Install it in the correct location, make it executable, and use it only after decrypt.sh, /etc/crypttab, and the extroot layout are ready. Check the sd*/mmcblk* device names, crypto modules, and overlay paths against the actual kernel.

#!/bin/sh# Prereqs:#  * packages:#    * block-mount#    * cryptsetup#  * move /sbin/block to /sbin/block.bin#  * install decrypt script to /sbin/decrypt.sh with execute permission## This script should be placed at /upper/sbin/block of the UBIFS overlay,# or /sbin/block if already on the overlayfs and be set with execute# permission.# It is expected that the extroot is on a device that the kernel names as# sd* or mmcblk*, otherwise modify appropriately.# Set to 1 to enable debug logsexport DEBUG=SDIR=${0%/*}BLOCK="${SDIR}/block.bin"LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-.}LD_LIBRARY_PATH="${SDIR}/../usr/lib:${LD_LIBRARY_PATH}"PATH=$PATH:${SDIR}:${SDIR}/../usr/sbin:${SDIR}/../usr/binblock() {  ( exec -a ${0} ${BLOCK} "$@" )}if [ "$PREINIT" != "1" ]; then  exec block "$@"figet_jiffies() {  head -n3 /proc/timer_list | tail -n1 | cut -d' ' -f 3}if [ -z "$BLOCK_LOG" ] && [ -n "$DEBUG" ]; then  TIME=$(get_jiffies)  export BLOCK_LOG="/tmp/block.$(printf '%016d' ${TIME:-9999999999}).log"  exec 2>"$BLOCK_LOG"  set -xfiif [ ! -x "$BLOCK" ]; then  echo "Error: ${BLOCK} is not an executable" >&2  return 1fiif [ "$1" = "extroot" ] && [ -e ${SDIR}/../.use_crypt_extroot ]; then  # We are being called to setup the extroot, so make sure crypto block  # devices are all setup.  # Hotplug runs too late, create device nodes for /dev/sd*, if there are any  for SYSDEVPATH in /sys/class/block/sd*; do    [ ! -f "$SYSDEVPATH"/dev ] && continue    [ -e "/dev/${SYSDEVPATH##*/}" ] && continue    MAJMIN=$(cat "$SYSDEVPATH"/dev | tr ':' ' ')    mknod /dev/${SYSDEVPATH##*/} b $MAJMIN  done  # Load modules needed for cryptsetup  KVER=$(uname -r)  insmod ${SDIR}/../lib/modules/${KVER}/af_alg.ko  insmod ${SDIR}/../lib/modules/${KVER}/algif_rng.ko  insmod ${SDIR}/../lib/modules/${KVER}/algif_hash.ko  insmod ${SDIR}/../lib/modules/${KVER}/algif_skcipher.ko  # FIXME: Why does block info only show ubi devices?#  block info | cut -d: -f1 |  # Do this hack instead, only check scsi and mmc devices  find /dev -type b | grep -E "/(sd|mmcblk).*" |  while read DEVPATH; do    cryptsetup --disable-locks isLuks $DEVPATH || continue    export ACTION=add DEVNAME="${DEVPATH##*/}"    # Assume this script is located in $OVERLAY/sbin when called    ALTROOT="${SDIR}/.." "$SDIR"/decrypt.sh || "$SDIR"/decrypt.sh  donefiblock "$@"

Do not copy this wrapper to another OpenWrt device just because it runs the same distribution. Keep a failsafe path and an off-device backup before replacing /sbin/block.

Fallback /etc/rc.local method

The source gives a more side-effect-prone alternative: normal boot fails to find extroot, then /etc/rc.local opens LUKS at the end of boot and calls mount_root again. Its keyfile example is /root/extroot.key:

# Only setup the encrypted extroot if /.use_crypt_extroot exists on rootfs_data.# This makes it easier disable the encrypted extroot from failsafe mode.mkdir -p /mnt/tmpif [ -e /.use_crypt_extroot ]; then  # Setup crypt device which contains the extroot  cryptsetup open -d /root/extroot.key /dev/sda1 cextroot  umount /overlay  # /tmp will get overridden by another tmpfs by mount_root, but we need the  # initial one because it contains the ubus named socket.  mount --bind /tmp /mnt/tmp  # Re-run mount_root now that we have a block device that it will recognize  # as an extroot. This sleep is needed, otherwise procd seems to freak out  # and the watchdog timer doesn't get reset. Not sure exactly why.  sleep 5  PREINIT=1 mount_root  # Free the new tmpfs just created by mount_root. Since it will never be used,  # its just wasting memory.  umount -l /tmp  # Put the original tmpfs back to where it was in the VFS, primarily so that  # programs can find the ubus socket.  mount --bind /rom/mnt/tmp /tmp  # Need to re-run this too for some reason, otherwise some other mounts are not  # mounted after mount_root, eg. /rwm.  block mount  # Reload rpcd to register rpc objects on the extroot  service rpcd reloadfi

This method does not read an interactive password in /etc/rc.local; the source expects a keyfile and asks the operator to consider the threat model of storing it at /root/extroot.key. The web interface may take another 20–30 seconds to appear, while SSH is not necessarily delayed. Use it only when the PREINIT path fails and a failsafe route is ready.

Full automated restore script

The following is the complete script for the source’s automated upgrade branch. It depends on Hotplug extras, Opkg extras, and the init profile; do not run it by itself on an unprepared router.

cat << "EOF" > /etc/uci-defaults/90-extroot-restoreif uci -q get fstab.extroot > /dev/null \&& [ ! -e /etc/extroot-restore ] \&& [ -e /etc/opkg-restore-init ] \&& lock -n /var/lock/extroot-restorethenUUID="$(uci -q get fstab.extroot.uuid)"DIR="$(uci -q get fstab.extroot.target)"DEV="$(block info | sed -n -e "/${UUID}/s/:.*$//p")"if touch /etc/extroot-restore \&& grep -q -e "\s${DIR}\s" /etc/mtab \&& mount "${DEV}" /mntthenBAK="$(mktemp -d -p /mnt -t bak.XXXXXX)"mv -f /mnt/etc /mnt/upper "${BAK}"cp -f -a "${DIR}"/. /mntumount "${DEV}"filock -u /var/lock/extroot-restorerebootfiexit 1EOFcat << "EOF" >> /etc/sysupgrade.conf/etc/uci-defaultsEOF

The script prevents repeat runs, resolves the extroot UUID/device, mounts it temporarily, backs up the old etc and upper, copies the current contents, adds /etc/uci-defaults to the sysupgrade keep list, and reboots. Read every line, back up first, and verify ${DIR}/${DEV} on the real model.

10. System upgrades and custom images

Do not use opkg upgrade for a blind system upgrade

The source warns against blindly using opkg upgrade on snapshots: the uClibc ABI may change, /rom or /rwm UUIDs may change and break extroot, a kernel/module mismatch can brick the router, and upgrading every package except the kernel/modules can leave packages broken.1

For a stable release, sysupgrade to a coherent image. Afterward, you may need to repeat the mount-entry steps and reinstall packages—especially kernel modules—while checking UUIDs before the next reboot.

Custom image for 4 MiB devices

For routers with 4 MiB or less, the source uses the Image Builder on 64-bit Linux or WSL:

  1. Download the Image Builder for the correct target.
  2. Extract it and run:
make info
  1. Find the correct profile, for example:
tl-wr1043nd-v1: TP-LINK TL-WR1043N/ND v1 Packages: kmod-usb-core kmod-usb2 kmod-ledtrig-usbdev
  1. Build an image with the profile and required packages:
make image PROFILE=tl-wr1043nd-v1 PACKAGES="block-mount kmod-fs-ext4 kmod-usb-storage kmod-usb-ohci kmod-usb-uhci"
  1. Open bin/target/<device-type>/generic/, select the matching factory or sysupgrade image, and install it.
  2. Format the USB drive as ext4 with Linux LiveCD or GParted because, in the source example, e2fsprogs is too large for a 4 MiB device.

Automated setup and automated upgrade

  • openwrt-auto-extroot is an Image Builder frontend that can automatically format and configure a plugged-in storage device that has not been set up.
  • The source’s automated-upgrade branch combines Hotplug extras and Opkg extras; packages needed by extroot are stored in the init profile and restored after the upgrade.
  • Its restore script uses /etc/uci-defaults/90-extroot-restore, checks fstab.extroot and /etc/opkg-restore-init, mounts temporarily, moves etc/upper, then reboots. This is an upgrade pipeline for operators who understand the layout; do not run it on a router without a backup.

Conclusion

Extroot is not just “plug in USB and mount it.” The critical parts are identifying the correct ${DEVICE}, keeping the old overlay reachable through /rwm, copying all data, verifying /overlay after reboot, and preparing recovery for UUID mismatches or late storage discovery. USB mode switching, LUKS, custom images, and automated upgrades each have their own limits; when uncertain, stop at standard extroot and keep the backup outside the router.

Sources

Footnotes

  1. OpenWrt Wiki – Extroot configuration, updated 2026-07-30. 2 3 4 5

Comments & Discussion

Share your thoughts, ask questions and feedback

Markdown & QQ Emoji
Loading comments...