Stock Debian on the NanoPi R2s Plus


Recently, I got my hands on a NanoPi R2S Plus.

FriendlyElec makes quite a few ARM-based single-board machines, with or without an enclosure, running on an SD card, or on eMMC, or even on NVMe.

You can find the full R2S Plus specs on its product page and its wiki page.

But the salient features that makes it interesting for me are:

  • eMMC (I’ve used the NanoPi Neo, which is only SD card based, and that’s a bit of a pain);
  • A true serial console, in the form of an USB-C port connected to an USB-to-serial adapter which works all the time, whether the R2S Plus is powered or not (more on this later);
  • Two Ethernet ports, one native and one through an internal USB-3 adapter (that makes the R2S suitable for some light firewalling, or for High Availability setups where two devices synchronize on one Ethernet link and provide service to the LAN on another);
  • A metal case (and no fan).

The NanoPi R2S has no HDMI, but I’m not intending to use it as a video device. All I want to see is, can it run Bookworm, and more to the point yet, stock Bookworm? Let’s find out.

Finding a standard Debian image

FriendlyElec provides a lot of images that you can run on the SD, or install on the eMMC of their devices. I’ve got nothing about these images, mind you; they’re invaluable for a quick start with a NanoPi. But they are definitely not stock images.

So I went and looked for a way to have as standard a Debian Bookworm system as possible running on the R2S Plus.

First stop: John Clark’s stock Debian for the NanoPi R5C & R5S. This is what I’ve been using for the few NAnoPi R5S machines which run some of my services. So I figured, maybe John’s looked into the R2S / R2S Plus? Unfortunately, he had not.

Second stop: Johan Gunnarsson’s website of SD card images, which is full of prebuilt SD images for a lot of single-board computers.

These are minimal (no unneeded package), pure (only Debian [resp. Ubuntu] packages) and up-to-date images; exactly what I was looking for, and there are images for the NanoPi R2S Plus.

However, they are for Debian testing (Trixie), unstable (Sid), or experimental (rc-buggy).

But Johan also provides a Github repository for those like me who would prefer to build their own SD image for their distribution of choice.

Johan’s README gives a simple example for the Raspberry Pi 3b, which I could easily adapt to the R2S by comparing both devices on the boards CSV file.

Johan’s example assumes that you did not clone the repo, and that’s actually a nice assumption! But I’d cloned it already, so I could docker build [...] ..

Also, I don’t like putting files in /tmp, or even outside $HOME, or even outside the current directory, so instead of creating sd-images under /tmp/, I wanted to create it under the repository root.

Hence the final recipe:

$ git clone https://github.com/johang/sd-card-images.git
$ cd sd-card-images
$ docker build -t sd-images .
$ mkdir ./sd-images
$ docker run --rm -v ./sd-images:/artifacts sd-images build-boot nanopi_r2s_plus rk3328 nanopi-r2s-plus-rk3328_defconfig aarch64-linux-gnu
$ docker run --rm -v ./sd-images:/artifacts sd-images build-debian debian arm64 bookworm

Once this was done (took 15 minutes, much less than a full Buildroot build, believe me), I could see under ./sd-images the boot image (boot-nanopi_r2s_plus.bin.gz) and the root image (debian-bookworm-arm64-sui4sh.bin.gz).

I could then run the zcat command :

$ zcat sd-images/boot-nanopi_r2s_plus.bin.gz sd-images/debian-bookworm-arm64-sui4sh.bin.gz > sd-card.img

This gave me a 3.6GB (non-root-owned) s-card.img file, which I dded onto a 4GB SD card, which I put in the R2S Plus (flashing took 27 minutes, longer than it took to build the image).

But before I could boot it, I needed to see how/if the console worked.

Testing the console

A blanked device (see below for more) will appear not to boot if you power it up without an SD card. But maybe it talked on the console, since there is one?

I connected the “DEBUG” USB to my computer and ran picocom /dev/ttyUSB0 -b 115200 out of sheer muscle habit. Then I connected the R2S Plus WAN interface to my local network.

And yes, when I connected the power USB, I did see something print. But it was garbage. So I tried the whole thing again, with all baudrates I could think of. Long story short, the right baudrate was 1500000 (1.5Mb/s). At that baudrate, I was greeted with:

U-Boot SPL 2024.10 (Apr 11 2025 - 22:18:21 +0200)
[...]
SPL: failed to boot from all boot devices
### ERROR ### Please RESET the board ###

OK, so that’s U-Boot, and it did not find any device to boot from. Now let’s try again, only this time with the Bookworm SD card.

First Bookworm boot

Here’s the U-Boot part of the boot, abridged

U-Boot SPL 2024.10 (Apr 11 2025 - 22:18:21 +0200)
Trying to boot from MMC2
[...]
U-Boot 2025.04johang-ga40fc5afaec0 (Apr 13 2025 - 13:29:27 +0000)
[...]
Model: FriendlyElec NanoPi R2S
DRAM:  1 GiB (total 1022 MiB)
[...]
Hit any key to stop autoboot:  0
[...]
## Flattened Device Tree blob at 01e00000
   Booting using the fdt blob at 0x1e00000
Working FDT set to 1e00000
   Loading Ramdisk to 3c09c000, end 3cee06c5 ... OK
   Loading Device Tree to 000000003c090000, end 000000003c09bf63 ... OK
Working FDT set to 3c090000

Starting kernel ...

This time, U-Boot could read and start the kernel. Next: the Linux boot.

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
[    0.000000] Linux version 6.1.0-33-arm64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP Debian 6.1.133-1 (2025-04-10)
[    0.000000] Machine model: FriendlyElec NanoPi R2S
[...]
[    3.125452] Run /init as init process
[...]
Starting systemd-udevd version 252.36-1~deb12u1
[...]

Debian GNU/Linux 12 debian ttyS2
login:

And yes, I can log in as root (with the password specified in the root image name).

Let’s try and use this system!

Trying to update/upgrade

First things first:

root@debian:~# apt update
Get:1 http://security.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Get:2 http://deb.debian.org/debian bookworm InRelease [151 kB]
Get:3 http://deb.debian.org/debian bookworm-updates InRelease [55.4 kB]
Get:4 http://security.debian.org/debian-security bookworm-security/main Sources [152 kB]
Get:5 http://security.debian.org/debian-security bookworm-security/main arm64 Packages [253 kB]
Get:6 http://deb.debian.org/debian bookworm/main Sources [9495 kB]
Get:7 http://security.debian.org/debian-security bookworm-security/main Translation-en [154 kB]
Get:8 http://deb.debian.org/debian bookworm/main arm64 Packages [8692 kB]
Get:9 http://deb.debian.org/debian bookworm/main Translation-en [6109 kB]
Get:10 http://deb.debian.org/debian bookworm-updates/main Sources [796 B]
Get:11 http://deb.debian.org/debian bookworm-updates/main arm64 Packages [512 B]
Get:12 http://deb.debian.org/debian bookworm-updates/main Translation-en [360 B]
Fetched 24.9 MB in 32s (783 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.

Good: the only apt sources are Debian’s, and the system is up to date.

But can this image boot on the eMMC?

eMMC support?

Let’s find out which device is the eMMC:

root@debian:~# lsblk
NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk1      179:0    0  3.6G  0 disk
├─mmcblk1p1  179:1    0   28M  0 part
└─mmcblk1p2  179:2    0  3.5G  0 part /
mmcblk0      179:256  0 29.1G  0 disk
└─mmcblk0p1  179:257  0  120M  0 part
mmcblk0boot0 179:512  0    4M  1 disk
mmcblk0boot1 179:768  0    4M  1 disk
root@debian:~# _

So: the kernel sees the SD card as mmcblk1 (since mmcblk1p2 is mounted as /) and the eMMC is mmcblk0 (note the size: 29.1G).

Now, the question becomes: what if this image was written onto the eMMC, and the R2S Plus rebooted without the SD card? Would that boot? Well, if the ROM boots from either SD/MMC interface, that should work at least up to and including the kernel.

Then the only thing that would remain is, would the kernel be able to mount the root filesystem? That depends on how the root filesystem is mounted; if it is by reference to mmcblk1p2, then the system can only boot from SD card; if it is mentioned by UUID, then this might work.

For this type of machine, the root filesystem is specified on the kernel command line. Let’s see that:

root@debian:~# cat /proc/cmdline
root=PARTUUID=93c60360-02 rw rootwait
root@debian:~# _

This PARTUUID should match mmcblk1p2. Let’s verify:

root@debian:~# blkid
/dev/mmcblk1p2: [...] PARTUUID="93c60360-02"
/dev/mmcblk1p1: PARTUUID="93c60360-01"
root@debian:~# _

Yup. So if we copy sd-card.img onto /dev/mmcblk0, we should see the same PARTUUIDs.

normally, it is bad to have the same PARTUUIDs on different devices, and it would be a problem if the machine booted with several partitions matching its root PARTUUID, but here we won’t do that: we’ve booted on the SD card, but next time we boot, it will be removed, and only the eMMC will be present.

I’ll do that from my machine, using scp, since the minimal Debian does include a running OpenSSH server which allows (for the moment) root login using a password. The minimal Debian has announced its name as “debian”, and my DHCP/DNS server caught that, so I can use that name as the hostname for my scp command.

$ scp sd-card.img root@debian:/dev/mmcblk0
[...]
$ _

The transfer is slow, not sure why; the whole flashing took 20 minutes.

Once the transfer is done, we can check the PARTUUIDs again:

root@debian:~# blkid
/dev/mmcblk1p2: [...] PARTUUID="93c60360-02"
/dev/mmcblk0p1: PARTUUID="93c60360-01"
/dev/mmcblk0p2: [...] PARTUUID="93c60360-02"
/dev/mmcblk1p1: PARTUUID="93c60360-01"
root@debian:~# _

Indeed, the image was written: we can see each PARTUUID twice, once on mmcblk0, once on mmcblk1.

And now comes the big moment: I power off the machine (# poweroff), unplug its power cable, remove the SD card, and hold my breath. Will it work?

The final test

I plug back the power cable and watch the console.

[...]
debian login: root
Password:
Linux debian 6.1.0-33-arm64 #1 SMP Debian 6.1.133-1 (2025-04-10) aarch64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@debian:~# _

I breathe out.

VICTORY!

We now have Debian 12 running from eMMC on a NanoPi R2S Plus.

Thank you, Johan!

Next step: configuring and securing it. But that’ll be later.