Here's the process for testing "apk -p" - before proceeding, it's best to test on a virtual machine
with the official OpenWrt x86_64 image and a second virtual disk as simulated USB drive
so you don't mess up a production router.
Assume your external storage is mounted at /mnt (e.g. mount /dev/sdb1 /mnt).
# 1. Create the minimum directory structure apk expects
mkdir -p /mnt/etc/apk
mkdir -p /mnt/lib/apk
mkdir -p /mnt/var/cache/apk
mkdir -p /mnt/tmp
# 2. Copy repository list and keys from the running system
cp -r /etc/apk/. /mnt/etc/apk/
# 3. Initialize the apk database in the new root
apk -p /mnt add --initdb
# 4. Update package index
apk -p /mnt update
# 5. Install your package (python3 used as example — it has external lib dependencies)
apk -p /mnt add python3
# or nano, ecc ...
apk -p /mnt add nano
Make binaries and libraries available system-wide
After installation, binaries are under /mnt/usr/bin and libraries under /mnt/usr/lib — neither is
in the default search path. Create a persistent profile script:
cat > /etc/profile.d/extpkg.sh << 'EOF'
export PATH="/mnt/usr/bin:/mnt/usr/sbin:$PATH"
export LD_LIBRARY_PATH="/mnt/usr/lib:/mnt/lib"
EOF
# Apply immediately without rebooting
source /etc/profile.d/extpkg.sh
The library dependency problem
Packages with their own shared libraries (like python3, which ships libpython3.13.so.1.0) will fail
at runtime because the dynamic linker only looks in the system root /usr/lib, not in /mnt/usr/lib:
Error loading shared library libpython3.13.so.1.0: No such file or directory
In my tests on OpenWrt 25.10, exporting LD_LIBRARY_PATH in the shell was not sufficient, while passing it inline worked reliably.
LD_LIBRARY_PATH="/mnt/usr/lib:/mnt/lib" /mnt/usr/bin/python3 --version
# Python 3.13.x
So the cleanest solution is a wrapper script in the system root for each installed binary example:
cat > /usr/bin/python3 << 'EOF'
#!/bin/sh
exec env LD_LIBRARY_PATH="/mnt/usr/lib:/mnt/lib" /mnt/usr/bin/python3 "$@"
EOF
chmod +x /usr/bin/python3
# Test
python3 --version
warning: It's better to create them by hand to avoid problems in my opinion...
thus avoiding having to replace or modify a good binary .
Important warnings before doing this on a real router
-
Test in a VM first. Use the official OpenWrt x86_64 image under QEMU, VirtualBox or Proxmox
with a second virtual disk as the simulated USB drive. This whole procedure is easy to validate
safely before touching production hardware.
-
--initdb is mandatory. Without it, apk refuses to open the database on the alternate root with
"Unable to lock database: No such file or directory", regardless of the path used.
-
Wrapper scripts are in the system root. If you create /usr/bin/python3 as a wrapper and the
external drive is not mounted at boot, the wrapper will silently fail. Make sure /mnt is mounted
before any service or script that calls those binaries (e.g. via /etc/rc.local).
-
/etc/profile.d/extpkg.sh depends on the drive being mounted. If /mnt is not mounted when a new
shell session starts, PATH and LD_LIBRARY_PATH will point to non-existent directories.
ps: I remind you that this is also mentioned for "opkg -d":
"This solution will NOT work out-of-the-box in most cases, OpenWrt packages are designed to be installed in root filesystem and may need additional symlinks or modifications to work at all under the changed path! "