In Part 1 of this blog post I described the Raspberry Pi boot image structure (a very simple version) and provided a very brief description of how the boot process works.

This part talks about how you can build your own images for a Raspberry Pi and setups a framework for people to collaborate and share their image components with each other.

In subsequent blogs I’ll build on the concepts here and describe other more sophisticated configurations and progressively share more of the elements I have built for myself, cleaning them up, before I make them public.

This blog post is structured into three parts:

Building your own images

Writing your own elements

Collaborating with elements

I build my own Raspberry Pi boot images from scratch. I do this so I can build images in a repeatable way, configuring the software I want in just the way I want it.

It is much more effective to do this, than to take an image from some other source and then remove things which I don’t want and work around the quirks of the person who build that image to begin with.

In doing this, I can control exactly what goes on the image and make sure that it comes out the exact same way each time.

My tool of choice (as I use this for work related things as well) is diskimage-builder. Like other image building tools, this one is quirky, but I’ve got to understand it and I prefer it to the pi-gen tool that is used to build the official Raspbian images.

Building your own images

This will work for you if you have any old Linux machine (I use an Ubuntu 16.04LTS x86_64 machine). A simple to follow Quick Start Guide is available here.

To a reasonably Linux savvy person, building your first image should take no more than 20 minutes and you can have your Raspberry Pi up and running on your own custom image in about 30 minutes.

The steps are simple:

Clone the repository of tools and elements

Install prerequisite software

Build your image

Install your image

Have fun!

Writing your own elements

diskimage-builder is a very flexible tool and it is very easy to write elements once you understand the general flow of things. Like all image building tools, it uses two environments, a host environment and an image environment. Some tools make the image environment a chroot (like diskimage-builder) others use a VM or a docker container.

diskimage-builder works by allowing users to select elements, define dependencies between elements, and execute scripts provided in those elements in a deterministic order. Some of the scripts are executed in the host environment, others are executed in the image environment. Finally, it takes the image environment that was created and packages it up into the format of your choice.

I use diskimage-builder to make images for a cloud (and I use qcow2 for that). For Raspberry Pi, I use the ‘raw’ format which is nothing more than a disk image complete with MBR (boot record), partition table, and volumes.

To write your own element, you can follow along with one of the many elements provided and insert your own commands. It is a very short, and not very steep learning curve.

Collaborating with elements

I’ve shared (and I will continue to share) elements that build a basic Raspbian based image, a basic Debian based image, and elements that will install specific packages, configure WiFi, configure a secure user, and so on.

For example, consider the element rpi-generic which produces a Raspberry Pi image that I use as a basic building block for many of my applications. It does not, by itself, provide an operating system. So, I could choose either rpi-debian-core or rpi-raspbian-core. With just those two elements, I could execute the command

disk-image-create rpi-debian-core rpi-generic \

-a armhf -o debian -t raw -n

and this would produce a file (debian.raw) which was a properly bootable Raspberry Pi image.

But, since rpi-generic depends on other elements the diskimage-builder tool looks for those and uses them. One of the elements it depends on is rpi-resize-root. Let’s look at that for a second.

The MicroSD card I use is often 8 or 16GB, the image (for speed) is just 2GB. So, when you install the image onto the MicroSD card, and you boot the machine for the first time, the software installed and configured by the rpi-resize-root element expands the root file system to take the full available space.

Similarly, rpi-generic depends on rpi-wifi. That element does the few things to configure my Raspberry Pi to automatically connect to the WiFi network.

If someone else wrote an element that I wanted to use, all I would have to do is get that element (clone the repository that they shared) and tell diskimage-builder where to find the element!

For example, the sample build.bash script contains this line, which can easily be extended like a standard Linux PATH variable!

As a practical matter, I have an element for my WiFi access point that I use at home (which I will share as soon as I can clean it up). That element is on my machine at /rpi-image-builder-private/elements/rpi-wifi-access-point.

The elements which I’ve shared on the rpi-image-builder repository are at /rpi-image-builder/elements/.

To build my WiFi access point, I specify:

export ELEMENTS_PATH=/rpi-image-builder-private/elements:/rpi-image-builder/elements

I encourage you to write and share elements, and I believe that this will be a great way for all Raspberry Pi users to collaborate on their cool projects.