This is how I got my NanoPi R5S booting from NVMe:

Tips:
If the Pi isn't booting after installing NVMe try a better power supply.
If your SD Card gets corrupted, stick it in a digital camera to format it.
Use small SD Cards, like 2GB or 4GB.

The firmware img file is a copy of a drive. Inside the img are more img's which are partitions of the drive.

I used rk3568-sd-friendlywrt-23.05-docker-20240606.img

Its partitions are like:

0.uboot.img
1.misc.img
2.dtbo.img
3.resource.img
4.kernel.img
5.boot.img
6.recovery.img
7.rootfs.img
8.userdata.img

#########################################################################

# Step 1: Flash rk3568-sd-friendlywrt-23.05-docker-20240606.img to SD Card
# Step 2: Boot NanoPi from SD Card
# Step 3: dd SD Card to NVMe - see details below
# Step 4: Shutdown NanoPi
# Step 5: Extract rk3568-sd-friendlywrt-23.05-docker-20240606.img with 7-Zip
# Step 6: Open ImHex Program
# Step 7: Modify 2.dtbo.img - see details below
# Step 8: Make a copy of the rk3568-sd-friendlywrt-23.05-docker-20240606.img
# Step 9: Modify copy - see details below
# Step 10: Flash the modified img to a SD Card
# Step 11: Boot NanoPi from SD Card
# The NanoPi will begin booting from SD Card then the bootloader will say "hey, use the NVMe", and the root fs will run from NVMe.

#########################################################################
Step 3 in detail
#########################################################################

# check out your drives first with
fdisk -l
# or
lsblk

# copy away...
dd if=/dev/mmcblk0 of=/dev/nvme0n1 bs=1M status=progress

# then expand the last partition... well, delete it and make a new big one
fdisk /dev/nvme0n1
d
9
n
9
w

# if you want swap, which you might if you're playing with Docker...

parted /dev/nvme0n1
resizepart
9
485GB # assuming your NVMe is 500GB, this leaves about 14GB for swap.
quit

fdisk /dev/nvme0n1
n
10
w

opkg update
opkg install resize2fs
resize2fs /dev/nvme0n1p9

# after you boot from NVMe, add the swap through Luci in System -> Mount Points

#########################################################################
Step 7 in detail
#########################################################################
Using ImHex...
# I did this step on Windows...
# mmcblk2 is EMMC
# mmcblk0 is SD Card

file 2.dtbo.img
# two lines

I changed:
root=/dev/mmcblk0p8 rw rootfstype=ext4
data=/dev/mmcblk0p9

to:
root=/dev/nvme0n1p8 rw rootfstype=ext4
data=/dev/nvme0n1p9

# look for the 6D 6D...

m: 6D
m: 6D
c: 63
b: 62
l: 6C
k: 6B
0: 30

n: 6E
v: 76
m: 6D
e: 65
0: 30
n: 6E
1: 31

#########################################################################
Step 9 in detail
#########################################################################
# make a copy of rk3568-sd-friendlywrt-23.05-docker-20240606.img
# I called mine rk3568-sd-friendlywrt-23.05-docker-20240606-nvme.img
# replace partition 3 by dd-ing the modified partition 3
# I did this step using an Ubuntu VM...

sudo losetup -fP rk3568-sd-friendlywrt-23.05-docker-20240606-nvme.img
losetup -a

# check your loop number here and use it instead of 39

sudo dd if=2.dtbo.img of=/dev/loop39p3 bs=1M

#########################################################################