Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save G-UK/ded781ea016e2c95addba2508c6bbfbe to your computer and use it in GitHub Desktop.
Save G-UK/ded781ea016e2c95addba2508c6bbfbe to your computer and use it in GitHub Desktop.
Build a 64bit Debian OS for the Raspberry Pi using Debootstrap

Introduction

The objective of these instructions is to create a complete Arm64 OS (Userland and Kernel) using Debian Debootstrap and RPI-Update for use on the Raspberry Pi 3 and 4.

Prerequisites:

  • An existing Debian/Raspbian system (any architecture will do)
  • An empty SD card formatted as per a standard Raspbian installation mounted to /mnt/sd on the build system
    • 1st Partition 0-256MB = FAT32 (Mount to /mnt/sd/boot)
    • 2nd Partition 256MB+ = EXT4 (Mount to /mnt/sd)

Set-up basic Debian system

The utility used by the Debian installer, and recognized as the official way to install a Debian base system, is debootstrap. We will also need QEMU if we are building an Arm64 system on another host architecture (x64 or ArmHF most likely)

So lets install them.

sudo apt install debootstrap qemu

Now lets tell debootstrap to set-up a base Debian Arm64 installation on our SD card. This may take a while and will download an entire basic Debian system.

sudo debootstrap --arch arm64 bullseye /mnt/sd http://ftp.uk.debian.org/debian

Before we can do anything with our new system we have to make it accessible from our hosts architecture, we do this using QEMU.

sudo cp /usr/bin/qemu-arm64-static /mnt/sd/usr/bin

Now we have a basic Debian system we can chroot into and start setting up.

sudo mount -t proc /proc /mnt/sd/proc/
sudo mount -t sysfs /sys /mnt/sd/sys/
sudo mount -o bind /dev /mnt/sd/dev/
sudo chroot /mnt/sd /bin/bash

Configure your new system

Set-up APT

Now we are chrooted into our new Debian system there are a few things we need to configure to make it useable. Lets deal with "apt" first so we can install new packages.

To set-up "apt" we need to tell it which sources to use, I'll use the UK Debian mirror in this example.

editor /etc/apt/sources.list
deb http://ftp.uk.debian.org/debian stable main contrib non-free
deb-src http://ftp.uk.debian.org/debian stable main contrib non-free

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

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

To minimise the number of packages that apt installs you can also disable the automatic installation of Recommended packages

editor /etc/apt/apt.conf.d/99_norecommends
APT::Install-Recommends "false";
APT::AutoRemove::RecommendsImportant "false";
APT::AutoRemove::SuggestsImportant "false";

Once that file is saved we can update the apt repositories and make sure everything is updated.

apt update
apt upgrade

Configure Basic System Settings

Now we can set-up some other basic system settings.

Timezone

Select your Timezone.

dpkg-reconfigure tzdata

Locales

Select your language and input settings.

apt install locales
dpkg-reconfigure locales

Networking

Basic Setup

I will use echo commands in these instructions for simplicity, alternatively you can use "editor" to edit these files.

Setup your network interfaces. (Example using a basic wired connection using DHCP, customise to suit)

echo \
'auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

allow-hotplug wlan0
iface wlan0 inet dhcp
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf' \
> /etc/network/interfaces

Setup your DNS nameservers (Example using Cloudflare servers)

echo \
'nameserver 1.1.1.1
nameserver 1.0.0.1' \
> /etc/resolv.conf

Setup your hostname (Example using Pi-Example)

echo 'Pi-Example' > /etc/hostname

Set-up your hosts file (including IPV4 and IPV6)

echo \
'127.0.0.1 localhost
127.0.1.1 Pi-Example

::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts' \
> /etc/hosts
Wireless (Optional)

If you are going to be using a wireless network connection rather than wired you will need to setup wpasupplicant with your wifi connection details like this example.

apt install wpasupplicant
editor /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB

network={
     ssid="Your network name/SSID"
     psk="Your WPA/WPA2 security key"
     key_mgmt=WPA-PSK
}

Drive Mounts (Fstab)

Set-up your basic drive mounts for / , /boot & /proc

editor /etc/fstab

This example assumes a standard Raspberry Pi SD card set-up.

# <file system>   <dir>           <type>  <options>         <dump>  <pass>
proc              /proc           proc    defaults          0       0
/dev/mmcblk0p1    /boot           vfat    defaults          0       2
/dev/mmcblk0p2    /               ext4    defaults,noatime  0       1

Set-up Root user password

passwd

Install Additional Basic Packages

These are additional packages you may expect to find on a basic (Commandline) Debian system on the Raspberry Pi

apt install aptitude ca-certificates crda fake-hwclock firmware-brcm80211 gnupg man-db manpages net-tools ntp usb-modeswitch ssh sudo wget xz-utils

Install additional packages if you want to use a GUI and RDP (lxqt as an example)

apt install xorg xorgxrdp xrdp lxqt lightdm

Install mesa drivers to enable 3D acceleration (Note: I'm unsure of which specific packages are required but this should cover all bases)

apt install libegl-mesa0 libgbm1 libgl1-mesa-dev libgl1-mesa-dri libglapi-mesa libglx-mesa0 libosmesa6  mesa-opencl-icd mesa-va-drivers mesa-vdpau-drivers mesa-vulkan-drivers mesa-utils

Set-up user account

Creating a new user account

Standard Raspbian has the "pi" user so I'll use that as an example.

Create a user called "pi" that uses the "bash" shell, who has a home directory of "/home/pi" and is a member of the same groups as in a standard Raspbian install.

mkdir /home/pi
groupadd spi
groupadd i2c
groupadd gpio
useradd -s /bin/bash -d /home/pi -G sudo,video,adm,dialout,cdrom,audio,plugdev,games,users,input,netdev,spi,i2c,gpio pi
chown pi:pi /home/pi

Set the users password

passwd pi

Setting up user .profile

In order for your BASH settings (~/.bashrc) to load on log-in you require a .profile file in your user and root home directories.

editor /home/pi/.profile
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

# set PATH for RaspberryPi userland files in /opt/vc/bin if they exist
if [ -d "/opt/vc/bin" ] ; then
    PATH="$PATH:/opt/vc/bin"
fi
cp /home/pi/.profile /root/
chown pi:pi /home/pi/.profile

Install the Kernel and Userland Firmware

Install rpi-update script

cd /usr/local/bin
wget https://raw.githubusercontent.com/raspberrypi/rpi-update/master/rpi-update
chmod +x rpi-update

Install dependencies

apt install curl binutils cmake git build-essential

Install Kernel

WANT_32BIT=1 WANT_64BIT=1 WANT_PI4=1 rpi-update

Configure Kernel

Set-up config.txt and cmdline.txt

echo 'dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait net.ifnames=0' > /boot/cmdline.txt
echo $'ngpu_mem=16\narm_64bit=1\ndtoverlay=vc4-fkms-v3d' > /boot/config.txt

Optional: Configure 64-bit Userland Firmware

(Incomplete and Unsupported. For full working Userland firmware I would recommend to multi-arch armhf as you can just use the rpi-update installed files in that case)

Build and install from Source

cd /tmp
git clone https://github.com/raspberrypi/userland
cd userland
./buildme --aarch64

Optional: Configure Multi-Arch to support Stock ArmHF & ArmEL Userland Firmware

dpkg --add-architecture armhf
dpkg --add-architecture armel
apt update
apt install libc6:armhf libc6:armel

Add /opt/vc/lib to linker config

echo '/opt/vc/lib' > /etc/ld.so.conf.d/00-vmcs.conf

Add /opt/vc/bin to sudo secure paths so that firmware commands work with sudo

echo 'Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/vc/bin"' > /etc/sudoers.d/opt-path

Add udev rules to allow access to device tree via user groups

editor /etc/udev/rules.d/99-com.rules
SUBSYSTEM=="vchiq",GROUP="video",MODE="0660"
SUBSYSTEM=="vcio",GROUP="video",MODE="0660"
SUBSYSTEM=="vc-mem",GROUP="video",MODE="0660"
SUBSYSTEM=="input", GROUP="input", MODE="0660"
SUBSYSTEM=="i2c-dev", GROUP="i2c", MODE="0660"
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
SUBSYSTEM=="bcm2835-gpiomem", GROUP="gpio", MODE="0660"
SUBSYSTEM=="rpivid-*", GROUP="video", MODE="0660"

KERNEL=="vcsm-cma", GROUP="video", MODE="0660"

SUBSYSTEM=="gpio", GROUP="gpio", MODE="0660"
SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c '\
	chown -R root:gpio /sys/class/gpio && chmod -R 770 /sys/class/gpio;\
	chown -R root:gpio /sys/devices/virtual/gpio && chmod -R 770 /sys/devices/virtual/gpio;\
	chown -R root:gpio /sys$devpath && chmod -R 770 /sys$devpath\
'"

SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
	chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
	chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* && chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
'"

KERNEL=="ttyAMA[01]", PROGRAM="/bin/sh -c '\
	ALIASES=/proc/device-tree/aliases; \
	if cmp -s $ALIASES/uart0 $ALIASES/serial0; then \
		echo 0;\
	elif cmp -s $ALIASES/uart0 $ALIASES/serial1; then \
		echo 1; \
	else \
		exit 1; \
	fi\
'", SYMLINK+="serial%c"

KERNEL=="ttyS0", PROGRAM="/bin/sh -c '\
	ALIASES=/proc/device-tree/aliases; \
	if cmp -s $ALIASES/uart1 $ALIASES/serial0; then \
		echo 0; \
	elif cmp -s $ALIASES/uart1 $ALIASES/serial1; then \
		echo 1; \
	else \
		exit 1; \
	fi \
'", SYMLINK+="serial%c"

Finally

If there is anything else you wish to configure such as SSH keys (Note SSH is enabled in this build but not configured), this can be done in the chroot environment you have been working.

Once complete you should be able to exit the chroot environment and unmount your new SD card.

exit
sudo umount /mnt/sd/*
sudo umount /mnt/sd

Your 64-bit Debian Bullseye is now ready for use, Just insert the card into your PI and power on.

@nexus166
Copy link

nexus166 commented Jun 23, 2020

what about the missing libraspberrypi stuff (/opt/vc)? do you download those from the official raspbian mirrors or maybe kali?

@G-UK
Copy link
Author

G-UK commented Jun 24, 2020

It's been a while since I looked at this as I dont really use the /opt/vc stuff.

I think it is normally installed using the official rpi-update (Hexxeh) script whilst installing the Kernel although I think that may only downloads 32bit ArmHF files which testing now doesn't want to work.

Building it from the official source https://github.com/raspberrypi/userland using "build-me --aarch64" seems to work with additional steps of adding the user to the video group and adding /opt/vc/lib to the linker config.

I've added what I've done so far to the guide although it's not complete. /opt/vc/bin/vcgencmd works with sudo although I'm still getting an error running it under the "pi" user.

@G-UK
Copy link
Author

G-UK commented Jun 24, 2020

Think I've fixed the permission issue with vcgenncmd. A udev rule needed adding to change the vchiq device permissions.

@G-UK
Copy link
Author

G-UK commented Jun 24, 2020

Added all missing udev rules and additional groups as used by the device tree on a stock Raspbian image.

I cant test this at the moment but the device tree permissions now seem to be set-up the same as on Raspbian.

@nexus166
Copy link

Nice work! I've been meaning to turn this into a shell script with the auto-partition steps from this gist (the raw image can be created with truncate and then mounted with losetup for the debootstrap/configuration steps).

I will add your updates to test them out!

Just a note: when downloading the fixed firmware blobs at this step, the /lib/firmware/brcm dir does not exist.

@faddat
Copy link

faddat commented Sep 20, 2020

I'm with @nexus166.

I'd like to try and use this in my device image generation system, it's honestly better than anything else I have seen in terms of flexibility and minimalism-- but I do need to get it into a virtual filesystem because my image generator is a github action.

@G-UK
Copy link
Author

G-UK commented Dec 7, 2022

Not been active much for a couple of years but had to revisit this as I've needed to rebuild a couple of old systems, it appears some things have changed in the intervening time so I've made some changes.

Unfortunately I've not had time to document everything fully so it's not 100% and there still may be a couple of errors I've not had time to chase through.

  1. Updated to Bullseye.
  2. Remove manual download of missing firmware-brcm80211 files as these seem to now be in the package and my links were obsolete (Haven't tested though).
  3. Couple of warnings now appearing when adding the "pi" user. Not sure why, will have to investigate later if I get time. Everything I use seems to still work so ¯_(ツ)_/¯.
  4. Path to download rpi-update changed from the Hexxah repo to the raspberrypi repo
  5. When running rpi-update for some reason the kernelx.img files didn't download. Again will have to retest and update later if it keeps failing, files available manually from https://github.com/raspberrypi/firmware/tree/master/boot
  6. Comment added about using stock armhf Userland Firmware if you multi-arch arm64 and armhf as I'm still not sure the arm64 userland firmware is working correctly and is unsupported.

To multi-arch you can just run

dpkg --add-architecture armhf
apt update

You may need to install some basic armhf packages as well (eg. libc6:armhf) I'll have to add these later when I've had a look at what I actually have manually installed on my other systems.

  1. Changed some lines to be headers

@G-UK
Copy link
Author

G-UK commented Dec 7, 2022

Added Multi-arch setup for ArmHF and ArmEL including installation of libc6:armhf and libc6:armel which is required if using the standard userland firmware.

@scaryshark124
Copy link

scaryshark124 commented Apr 4, 2024

I really like this guide. I can tell you spent time and effort on it for sure. I think it is well put together compared to a lot of other guides I have come across. I am a fellow enthusiast who learned how to debootstrap my own OS once and got hooked. I have used it on any device I can get my hands on (laptops, Surface Pro, Chromebooks), and now I am moving on embedded Linux systems with Raspberry Pi being my next candidate. Through my journey I have scoured the internet for guides and whatnot to help me out when I stay up until 4AM trying to get the stupid OS to boot.

I find it easier to copy and paste a chroot script I got from some guy on youtube. I really wish I saved the link, or even subscribed, to his channel. So I can't give him credit. I am sure many people use this, but it reduces compilation errors during the build process while in chroot (no clue why) but I tend to have a bit more success when I use it. Also, it is so much easier to copy.

root@crunchy-pro:/# for dir in sys dev proc ; do mount --rbind /$dir /mnt/$dir && mount --make-rslave /mnt/$dir ; done

(edit: I inadvertently added a space where it shouldn't have been in my original post, that is what I get for staying up too late and trying to type things from memory)

So, quick question for you. Why do you use the arm64 architecture when you debootstrap? is it better than armhf? I am asking this because I genuinely do not know. If you want to run a 64 bit OS I have seen others who replaced bootcode.bin with UEFI firmware on the SPI Flash, then you could build an x86 with an EFI boot partition like on a regular laptop/desktop.

I was just searching for a way that I could build my own debian from source on a Raspberry Pi 4 board, and your article is one of the few that is rich with information. If you have no preference, then you can build straight from raspbian repositories. I have not started messing with the Raspberry Pi board yet, so I will be able to tell you for sure when I get it and try it myself, but I would think it would be a lot easier and compatible with the hardware if you use their repositories. You could even go a step further, which is what I plan on doing, and use multistrap to add foreign architecture during build time. Supposedly multistrap does the same thing debootstrap does, but I will figure that one out when I get the Pi board. Raspbian does have multiarch support for bookworm for arm64 in their repositories.

I will probably debootstrap a raspbian testing armhf (trixie/sid) build for the fun of it, and then multistrap raspbian bookworm build (armhf) with support for arm64 through multiarch, just to try it. I will also probably end up flashing UEFI on it too, just because.

The raspbian repo has another pool (rpi) than plain (but still awesome) debian that will probably give you access to any rpi related source you need. Also, I am not sure if it is just a Raspbian thing (debain split non-free and non-free-firmware), but they have a firmware (not non-free-firmware like plain debian) pool in their repo. This would make it so much easier to get everything you need because you would be able to install everything, at least I think, from apt instead of building from source. I only mention it because you did not include non-free-firmware in your sources.list.

if you add -m to useradd, then you would not have to create a home directory using mkdir. like this:

root@crunchy-pro:/# useradd -mG sudo,video,adm,dialout,cdrom,audio,plugdev,games,users,input,netdev,spi,i2c,gpio -s /usr/bin/bash -c 'Name' username

I got this from another awesome debootstrapper and guide maker who I regularly reference. (https://ivanb.neocities.org/blogs/y2024/debootstrap).

I am very curious as to how the bootloader on the Pi board works, and if you can install all the necessary files in the boot partition through command line similarly to how you use grub-install to install necessary files (bootx64.efi, fbx64.efi, grub.cfg, grubx64.efi) needed by UEFI (grub-install). So far I have found that raspberry Pi is not that complex when you understand that the config.txt file essentially replaces the BIOS along with bootcode.bin and start.elf. I would like to use tryboot_a_b out as it seems it might be possible to configure dual boot, if I wanted. That way, I could install the same arch of Debian testing (armh) and the other Raspbian testing (armh). if I truly wanted a Debian only build, I could easily move any firmware modules or config files that are only available on Raspbian repositories. However, I guess you could add raspbian repositories on a vanilla debian sources.list, as debian would be much more forgiving than installing debian repository packages on a raspbian build causing failure (I did this to an Ubuntu Build once while learning, and I had to wipe the drive because it would not boot again).

Anyhow, if you are still having any issues with install I can try and mess with it on my end. I know it has been a couple years (2022) since you have commented on this thread. However, your guide still has relevant information. I have already bookmarked it and will use it because it helps establish a link between vanilla debian and Raspbian, I think anyhow. Let me know if you need anything. I will check in on the comments every once in a while, and will drop anything new I find.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment