📚 Installation on a single board computer: a complete guide

Introduction

In this long post, I’ll describe how to configure a single board computer such as the Raspberry Pi or the Asus Tinker Board from A to Z to support a self-hosted Nextcloud 20+ instance. We’ll go through the following steps:

My personal board is an Asus Tinker Board, but I’ve played with Raspberry Pi 4s as well before, and the procedure is roughly the same (>90%). I’ll describe the differences when they arise (mainly at the image installation and cleanup steps).

Installing a recent image

Raspberry pi

Download Raspberry Pi OS (32-bit) Lite image.

Unzip the file; you’ll end up with an .img file like YYY-MM-DD-raspios-buster-armhf-lite.img. The image is a dump of a 2Gb partition with ≃ 435Mb of content.

Tinker board

Download the latest image from here. The latest one for TinkerBoard 1 is the 2.1.16. Starting at 2.2.x you must at least have a Tinker Board S (with UMS support).

Unzip the file; you’ll end up with an .img file like Tinker_Board-Debian-Stretch-V2.1.16-20200813.img. The image is a dump of a 2Gb partition with an old Kernel (4.04) and an old Debian stretch 9. We’ll update that shortly, and only keep the bootloader.

Putting the image on the SD Card

To transfer the .img content from your hard drive to the SDCard, copy its content via the dd command (Linux). Be aware you have to know which device name your SD card receives from the OS, your mileage may (will) vary. The best way to know is probably to ls /dev folder, then insert the SD Card, and ls it again. The newly added device is your SD Card (e.g. /dev/sdb). If your SD Card has multiple partitions, they will appear with a number as suffix like /dev/sdb1, /dev/sdb2, etc. We are only interested in the device name (e.g. /dev/sdb), not the partition name(s).

# Transfer the .img content to SD Card
sudo dd bs=4M if=Tinker_Board-Debian-Stretch-V2.1.16-20200813.img of=/dev/sdb status=progress && sync

⌛ This will take some time, be patient.

Once done, starting gparted can be handy to extend the copied partition. SD Cards have > than 16Gb by now, and the copied image is a 2Gb partition.

If you have a raspberry pi, you are good to go and can directly jump to Part 2.

Copying the Bootloader

U-boot vanilla?

Theoretically, the TinkerBoard is officially supported by u-boot and we should compile it like this:

cd /tmp
git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot

make CROSS_COMPILE=arm-linux-gnueabihf- tinker-rk3288_defconfig
# If you want to see U-boot outputs, set SILENT_CONSOLE option to Yes via menuconfig

make CROSS_COMPILE=arm-linux-gnueabihf- -j15

By convention, UART2 is used by Rockchip for serial output: pin 32 for TX, 33 for RX and 30 as GND. Do not connect Vcc, prefer an external power supply.

Transfer it on the SD card via the following command:

sudo dd if=u-boot-rockchip.bin of=/dev/mmcblk0 seek=64
sync

The problem is: via this technique, U-boot has never been able to find the kernel on the first partition. I can see U-boot’s output but I get a weird error. Please contact me (via the comments below) if you were able to configure the sources to make it boot nicely, I would be more than happy to have a recent u-boot on my board!

U-boot from Asus fork?

As we can’t compile a fresh u-boot, let’s compile the sources from Asus’s fork! Located here. I tried to compare the modifications done on the fork with the original repo but the two are too far away now and many options disappeared, changed their name, or have been added. I could find a reliable way to compare and set the same fork’s configuration on u-boot sources.

In addition to that, you need an old Ubuntu distribution to make it compile, otherwise your build will fail.

U-boot from the given image

Our last chance is to use the already compiled u-boot binary from the given image. We can’t recreate it but we can use it! If you copied the given image on an SD card, you have nothing to do. U-boot binary is already at sector 64.

Installing a recent Kernel from kernel.org

Let the fun begin!

Download the kernel sources and compile them on the host machine, don’t do this on the target.

# ON THE HOST MACHINE

# Installing required compilers
sudo apt install gcc-arm-linux-gnueabihf device-tree-compiler gcc-aarch64-linux-gnu bc

# Downloading the vanilla kernel:
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
cd linux
git branch --list --remotes
git branch --show-current
git checkout linux-5.4.y # 5.4 is LTS until Dec, 2025
                         # 5.10   LTS doesn't boot with Asus defconfig
                         # See: https://www.kernel.org/category/releases.html

# Download kernel configuration for the target board
# Asus gives a file for Tinkerboard / TinkerBoard S / TinkerBoard 2
curl -L https://raw.githubusercontent.com/TinkerBoard/debian_kernel/develop/arch/arm/configs/miniarm-rk3288_defconfig -o arch/arm/configs/miniarm-rk3288_defconfig

# Configure the kernel sources with the downloaded configuration file
make ARCH=arm miniarm-rk3288_defconfig -j16

# (a couple of warnings might appear but they should be harmless)
# We now need to add 'overlayfs' support as a module.
# The below command will start a terminal GUI, where you can browse the kernel config options
make ARCH=arm menuconfig
# overlayfs is at 'File systems' → 'Overlay Filesystem Support' and must be set to 'M'
# hardware crypto module support is at ' Cryptographic API' → 'Hardware crypto devices' → 'Rockchip's Cryptographic Engine driver' and must be set to 'M'

# Compile kernel and its modules
make zImage ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
make modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

# Tinker's dts is in mainline kernel, under the name rk3288-tinker.dts
# (see: arm/boot/dts/rk3288-tinker.dts)
# so compile it as well to a dtb file (binary)
make ARCH=arm rk3288-tinker.dtb CROSS_COMPILE=arm-linux-gnueabihf- -j16

# Install modules to the SDCard's '/' partition (adapt 'sdX' below)
sudo make ARCH=arm INSTALL_MOD_PATH=/media/sdX/ modules_install

# Copy freshly compiled kernel to the SDcard's '/boot' partition (adapt 'sdY' below)
cp arch/arm/boot/zImage /media/sdY/zImage
cp arch/arm/boot/dts/rk3288-tinker.dtb /media/sdY/rk3288-miniarm.dtb

🥳 Congratulations, your now have a freshly compiled kernel on your SD Card with overlayfs and rk_crypto support! 🎉

Installing Debian 11 Bullseye

For this part, you’ll need to format (yes, format) the second partition made by copying the .img file. Prefer EXT4 filesystem.

Once done, mount the partition in a temporary folder:

mkdir /tmp/os
sudo mount /dev/sdb2 /tmp/os

And use deboostrap to install a fresh Debian stable. (apt install debootstrap might be necessary).

# First Stage (downloading packages)
sudo /usr/sbin/debootstrap --arch armhf --foreign stable /tmp/os https://deb.debian.org/debian/

# Second stage (unpacking packages in a chrooted env):
# as the target arch is different, needed to use qemu to chroot
sudo apt install qemu-user-static
sudo cp /usr/bin/qemu-arm-static /tmp/os/usr/bin/
sudo chroot /tmp/os /usr/bin/qemu-arm-static /bin/bash -i

# When chrooted, run the second stage
#  /debootstrap/debootstrap --second-stage

Minimal configuration

Still in the chrooted environment,

# Update root password
passwd

# Specify hostname
nano /etc/hostname

# Modify FSTAB
# Add the following lines (mind the tabs):
# /dev/mmcblk0p2	/	ext4	defaults	0	1
# /dev/mmcblk0p1	/boot	vfat	defaults	0	2
nano /etc/fstab 

# Enable serial console
systemctl enable serial-getty@ttyS0.service

# Configure locales
apt install locales

apt update
apt install git openssh-server vim firmware-linux firmware-linux-nonfree firmware-linux-free firmware-realtek firmware-iwlwifi cron acl build-essential debian-keyring initramfs-tools autoconf automake libtool sudo ufw curl lsb-release cryptsetup cryptsetup-bin unzip curl

useradd -m sheeva
echo "sheeva:monpassword" | chpasswd
adduser sheeva sudo
dpkg-reconfigure locales
vim /etc/ssh/sshd_config
Upgrading Source list:

update /etc/apt/sources.list with the following content:

deb http://deb.debian.org/debian/ stable main contrib non-free
deb-src http://deb.debian.org/debian/ stable main contrib non-free

deb http://deb.debian.org/debian/ stable-updates main contrib non-free
deb-src http://deb.debian.org/debian/ stable-updates main contrib non-free

# Added from https://wiki.debian.org/Backports#Using_the_command_line
deb http://deb.debian.org/debian bullseye-backports main contrib non-free
Setting up a static IP address

As this SBC will become a Nextcloud server, it’s better to assign it a fixed IP. Add the following in /etc/network/interfaces.d/eth0 (file won’t exist):

auto eth0
iface eth0 inet static
address 192.168.1.XXX
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1

/etc/network/interfaces imports /etc/network/interfaces.d/*, names doesn’t matter. Replace XXX with the actual IP address you want to assign your board.

Securing SSH

Only allow connection via $MYUSER.

sudo vim /etc/ssh/sshd_config

# Add/Modify the following directives:
AllowUsers MYUSER
PermitRootLogin no

Other ssh tweaks can be found here.

🥳 Congratulations, your now have a freshly installed Debian stable on your SD Card! 🎉

Next

See PART 2