Creating custom rsdk image - cli only

Hi all, I’m hunting my way through rsdk to build a custom OS image for the rock5b - and I’m wanting to do it without installing any of the GUI components…

I’ve narrowed it down to pretty much this part - of the libjsonnet files:

diff --git a/src/share/rsdk/build/mod/packages/categories/core.libjsonnet b/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
index 9d2bdcb..745588f 100644
--- a/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
+++ b/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
@@ -117,7 +117,7 @@ else
                 APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                 apt-get install -oDPkg::Chroot-Directory="$1" -y \
                 -oDpkg::Options::=--force-confnew %(recommends)s \
-                task-%(product)s
+                radxa-firmware radxa-system-config-rockchip syncthing
 
                 APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                 apt-get full-upgrade -oDPkg::Chroot-Directory="$1" -y \

When I run through a build for core profile at this point, the image is created, but the system doesn’t boot. There seems to be a number of things that aren’t created properly - including the extlinux.conf, and a few other critical files.

Looking here: https://github.com/radxa-pkg/vendor-profiles/blob/main/debian/control

It seems that even the task-rk3588 package will pull in everything - including chromium, gui components etc etc.

How can I create a working, basic image to put on an NVMe drive for the Rock 5B without any of the GUI components?

I have a number of additions I need to make to automatically provision them on first boot to our internal systems, but need to figure out how to get a customised image booting first without all the GUI components.

These devices will all be deployed in a headless configuration.

Hi @Steve-AU, I think @ChenJaly can help you

I went through the pain of building a cli version. I got a LOT of help from tech support. Here’s a post where I outlined the key steps I used.

Hi @Steve-AU,
You can directly use the rsdk build rock-5b bookworm cli command to build a CLI version without modifying any code. In addition, what deployment is your custom content?

Thanks - The cli build target seems to include core - and core has dependencies that pull in everything from wayland to gstreamer to chromium.

From the various libjsonnet files:

rsdk/src/share/rsdk/build/mod/packages/cli.libjsonnet includes:

local base_packages = import "categories/base.libjsonnet";

rsdk/src/share/rsdk/build/mod/packages/categories/base.libjsonnet includes:

local core_packages = import "core.libjsonnet";

rsdk/src/share/rsdk/build/mod/packages/categories/core.libjsonnet doesn’t seem to include any other libjsonnet files - so in theory - building core directly should include less.

The following in rsdk/src/share/rsdk/build/mod/packages/categories/core.libjsonnet seems to be what triggers the installation dependency chain to then end up with wayland, gstreamer, chromium etc installed.

                APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                apt-get install -oDPkg::Chroot-Directory="$1" -y \
                -oDpkg::Options::=--force-confnew %(recommends)s \
                task-%(product)s

If I comment out this single operation, the image is created successfully - but the result doesn’t boot. It is however slimmed down to ~1.7Gb instead of ~4Gb - as the runaway dependency chain isn’t installed to drag in all the stuff I don’t want.

In our custom image, we need to be able to write it to multiple boards - so it will end up on anywhere from 4 to 200 rock-5b boards over the next year.

We need to install docker, configure private docker CRs, write out initial configuration / network information, create users etc. We currently do this same type of process for nVidia Jetson boards as well.

My current approach is that I create a seperate script which runs inside the build chroot via the following patch:

diff --git a/src/share/rsdk/build/mod/packages/categories/core.libjsonnet b/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
index 9d2bdcb..ce4945d 100644
@@ -112,12 +110,11 @@ else
 
                 APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                 apt-get install -oDPkg::Chroot-Directory="$1" -y \
-                linux-image-%(linux_override)s
+                linux-image-%(linux_override)s ca-certificates
 
-                APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
-                apt-get install -oDPkg::Chroot-Directory="$1" -y \
-                -oDpkg::Options::=--force-confnew %(recommends)s \
-                task-%(product)s
+                ## Copy across the initial-setup script
+                cp -a /home/vscode/initial-setup "$1/"
+                chroot "$1" sh -c "/initial-setup"
 
                 APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                 apt-get full-upgrade -oDPkg::Chroot-Directory="$1" -y \

From that point, we can run the script as if it was in the running system to start configuring up our environment - which includes everything from systemd service files to auto-generated provisioning etc etc. We can easily do things such as installing docker / required services this way - and that is functional enough to be manageable.

As mentioned though - the part I’m stuck on at the moment is to not build in all the GUI components that seem to get installed with the task-%(product)s macro.

Hi @Steve-AU ,
You can try to --no-vendor-packages options and restore the code modified for this

Thanks for this info. It took me a little while to get my head around it - but it seems to agree with @ChenJaly mentioned above too. I didn’t see any documentation / mention about vendor packages, what they are, or what it does - so I’ll certainly go and give this a try.

I can see the mention of this here in rsdk/src/share/rsdk/build/mod/packages/categories/core.libjsonnet

                recommends: (if distro_check(suite) == "debian" && vendor_packages
                    then
                        "--install-recommends"
                    else
                        "--no-install-recommends"
                ),

However, its after the part that installs task-%(product)s - I don’t know if that matters - as I still don’t quite have my head around the libjsonnet assembly process - its quite… convoluted :smiley:

That’s the second reason that I ended up with a customisation script running inside the chroot - as the quoting hell to try and execute steps inside of the libjsonnet process was just too much to deal with and make things readable etc.

I’ll try to modify my patch, do a build and see what I end up with :slight_smile:

EDIT: For what its worth, I might as well post my build script - as its probably a little more convoluted than the main set of instructions - as I need to this end up with a single script to run everything - so its reproducable by others with minimal input… Running multiple scripts with user input isn’t really an option for me.

Open to suggestions on way to optimise if there’s something glaring that I’m missing!

#!/bin/bash
## Exit on any error:
set -e
shopt -s nullglob

## Clone the RSDK repo.
if [ ! -d rsdk ]; then
	git clone --recurse-submodules https://github.com/RadxaOS-SDK/rsdk.git
else
	echo "Using already cloned version of rsdk..."; echo
	pushd rsdk
	git reset HEAD --hard
	git pull --rebase
	git pull --recurse-submodules
	popd
fi

## Clean out old images...
sudo rm -fR out/* rsdk/out/*

## Set up the build environment...
pushd rsdk
npm install @devcontainers/cli
export PATH="$PWD/src/bin:$PWD/node_modules/.bin:$PATH"

## Apply patches against rsdk.
for patch in ../patches/*.patch; do
	echo "- Applying patch $patch"
	patch -p1 < "$patch"
done

rsdk devcon up

## Get the docker container name...
DEVCON=$(docker ps --format "{{.Names}}")
for i in ../files/*; do
	docker cp "$i" "$DEVCON:/home/vscode/"
done
docker exec -ti "$DEVCON" sudo su -l vscode -c /home/vscode/build-rock5b-image
docker stop "$DEVCON"

## Move out the built images...
cp -r out/* ../out/
sudo rm -fR out/*

The build-rock5b-image script does:

#!/bin/bash
echo "**************************************************************************************"
echo "**** Starting $0"
echo "**************************************************************************************"
## Build tweaks
NPROC=$(nproc)

cd /workspaces/rsdk
export PATH="$PWD:$PWD/src/bin:$PWD/node_modules/.bin:$PATH"

## Set up a PAT for github access.
mkdir -p /home/vscode/.config/nix
echo 'access-tokens = github.com=<token>' >> /home/vscode/.config/nix/nix.conf

echo "*** Running build..."
devenv -u $NPROC -j $NPROC shell src/bin/rsdk build rock-5b bookworm core

Thanks to the help here, I changed my patch to:

diff --git a/src/share/rsdk/build/mod/packages/categories/core.libjsonnet b/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
index 9d2bdcb..0bc0200 100644
--- a/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
+++ b/src/share/rsdk/build/mod/packages/categories/core.libjsonnet
@@ -117,7 +117,11 @@ else
                 APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                 apt-get install -oDPkg::Chroot-Directory="$1" -y \
                 -oDpkg::Options::=--force-confnew %(recommends)s \
-                task-%(product)s
+                task-%(product)s ca-certificates
+
+                ## Copy across the initial-setup script
+                cp -a /home/vscode/initial-setup "$1/"
+                chroot "$1" sh -c "/initial-setup"
 
                 APT_CONFIG="$MMDEBSTRAP_APT_CONFIG" \
                 apt-get full-upgrade -oDPkg::Chroot-Directory="$1" -y \

Then changed the line in build-rock5b-image to:

devenv -u $NPROC -j $NPROC shell -- src/bin/rsdk build rock-5b bookworm --no-vendor-packages core

This was enough to get an image produced with rootfs.tar at ~884Mb.

Looking at the files generated, /boot/extlinux/extlinux.conf seems to be populated with the correct information to boot properly now - my prior attempt only ended up with a barebones file that didn’t boot beyond the kernel.

I can see my customisations in initial-setup ran just fine - so in theory, this should give me something to go on to expand our internal provisioning.

I’ll need to edit /config/before.txt to not create additional users etc… I can see this installed by:

Can I safely just alter its contents and expect it to be cleaned up later on by the removal of rsetup-config-first-boot ?

EDIT: Also, is there any documentation on functions / options available within the before.txt etc?

EDIT2: I won’t have access to the physical hardware now until next year - so I’m just creating a more-complete image now to test when I’m back in the office after the break.

before.txt can be deleted directly without uninstalling rsetup-config-first-boot.
The comments in before.txt are the documentation.