Building Custom Raspberry Pi SD Card Images

After my post about using a Raspberry Pi 4 as a USB gadget got linked to by a YouTuber who worked out it also worked with the iPad Pro it has been getting a lot of traffic.

Pi4 Gadget

Along with the traffic came a number of comments from people wanting help setting things up. While the instructions are reasonably complete, they do assume a certain amount of existing knowledge and access to a monitor/keyboard/network to complete everything. Also given the majority of the readers were Apple users they couldn’t mount the main partition of the SDCard as it is a ext4 file system.

The quick and dirty solution is for me to modify a standard image and host it somewhere. This is OK, but it means I have to,

  • Find somewhere to host a 500mb file
  • Keep it up to date when new versions are released
  • Provide some way for people to trust what changes I’ve made to the image

I can probably get round the first one pretty easily, bandwidth is a lot cheaper than it used to be. The second and third items are a little harder.

I started to look at a way to script the modifications to a standard Raspbian image, that way I could just host the script and people could provide their own starting image. On a Linux machine I could mount both partitions on the card and modify or add the required config files, but the problem was installing dnsmasq. This needs the image to actually be running for apt-get to run, which gets back to the chicken/egg problem of needing to boot the pi order to make the changes. That and it would only run on Linux, not a OSx or Windows machine.

What I need is a way to “run” a virtual Raspberry Pi and a way to run commands in that virtual machine. I know from working with services like‘s build system that it is possible to emulate the ARM processor found in a Raspberry Pi on Intel based hardware. I found a couple of examples of people using Qemu to run virtual Pis and I was just about to set up the same when I came across dockerpi, which is a docker image with everything preconfigured. You can mount a SD card image

Virtual Raspberry Pi in a terminal window

When started you end up with what is basically the virtual machine console as a command line app. You can interact with it just like any other console application. I logged in as pi and then used sudo to run apt-get update and apt-get install dnsmasq.

That works for doing it manually, but I need to script this, so it’s time to break out some old school Linux foo and use expect.

Expect is a scripting tool that reads from stdin, and outputs to stdout, but will wait for a known output before sending a reply. It was used in the early days of the Internet to script dial up internet.

#!/usr/bin/expect -f
set timeout -1
set imageName [lindex $argv 0]
if {[string trimleft $imageName] eq ""} {
  puts "No Image file provided"
set cwd [file normalize [file dirname $argv0]]
set imagePath [file join $cwd $imageName]
spawn docker run -i --rm -v $imagePath:/sdcard/filesystem.img lukechilds/dockerpi:vm
expect "login: "
send "pi\n"
expect "Password: "
send "raspberry\n"

This expect script takes the name of the SD Card image as an argument, starts the Docker container and then logs in with the default pi/raspberry username & password.

With a bit more work we can get all all the changes done including creating the extra files.

proc slurp {file} {
    set fh [open $file r]
    set ret [read $fh]
    close $fh
    return $ret
set file [slurp "etc/network/interfaces.d/usb0"]
expect "# "
send "cat <<EOF >> /etc/network/interfaces.d/usb0\n"
send "$file\n"
send "EOF\n"

I’ve checked all the files into a github repository here. I’ve tested the output with a Pi Zero and things look good. To run it for yourself, clone the repo, copy the Raspbian Lite image (unzip it first) into the directory and run ./create-image <image file name>

There is a version of the output from here.

I’ve still got to get round to trying to the RNDIS Ethernet device support working so it will load the right drivers on Windows. And I need to extend the script to build the Personal CA Appliance from my last post.

10 thoughts on “Building Custom Raspberry Pi SD Card Images”

  1. I’m still a newbie but isn’t it possible to mount the raspi image in a WSL session and modify it there and then have it spit out a usable image?

    1. To be honest I’ve not run Windows since long before WSL was added. Assuming it has ext4 file system support then you can mount the image and make most of the changes, but you still can’t run apt-get to install dnsmasq.

      Also doing it this way automates the whole thing, when a new base image is released I just need to run the script against it and I have an up to date version ready to rock with no chance of missing a step out.

  2. I didn’t know about expect – that’s a neat an cheap way to autimatically set up a system.
    Would it make sense or any difference to go try this with pi-gen – the offical tool for generating pi images? I was about to try exactly that.

    1. Expect is old school, it’s how dial-up internet used to authenticate and start the slip/ppp daemon on dial-up shell accounts.

      Pi-gen looks to be doing similar sorts of things (using QEMU and dockerised builds) feel free to fork my repo and stick in a pull request if you get things working.

      1. Hi thx for your quick reply.
        old school or not it’s indeed really handy when interactive input is required. plus it’s quicker than the build of the whole image with pi-gen.

        One thing worth mentioning for people trying this: the recent kernel in raspbian buster has a buggy libcomposite. pi is only enumerating on a full reboot of the host. when i’m trying the stretch image it’s all working fine!

  3. First of all, thank you for your posts.

    I seem to be investigating similar things (with some slightly different use requirements) & came across your site.

    > I’ve still got to get round to trying to the RNDIS Ethernet device
    > support working so it will load the right drivers on Windows.

    I hope this helps – works perfectly for my update to date Windows 10.


    1. This doesn’t solve the problem I’m working on, I need a single config that will work for Mac/Windows/Linux and only uses the build in drivers. There is a built in RNDIS for Windows 10 so there shouldn’t be a need for downloading another driver.

      There is a version of the libcomposite setup script in the repo that should work with the built in Windows drivers and Linux, bit doesn’t work with OSx on Mac

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.