With the modern rpi-imager
tool, we can automate the imaging process for the Raspberry Pi computer board and make the process extremely painless for us each time we need to re-image.
The official Raspberry Pi's rpi-imager's tool (which can be installed from https://www.raspberrypi.com/software/) contains a command-line option that's not widely documented anywhere at the time of this writing (v1.7.5 was the latest release).
The best official documentation I can possibly find on Raspberry Pi Imager did not cover the command-line option.
As a result, I'll only cover the command-line option for macOS here as it's my daily driver.
The first step is to simply install the Imager from the second link mentioned above (yes, it's a graphical app... we'll get there). Installation should be straightforward.
The app should be installed in the following location on macOS:
/Applications/Raspberry\ Pi\ Imager.app
Believe it or not, the app itself is a lot better than the alternatives (e.g. balenaEtcher) as there are a set of various Linux distros other than the Raspberry Pi OSes that you can choose from.
I now swear by it as I previously used balenaEtcher only for imaging without the other preloaded customization (i.e. setting up user account, configuring hostname, enabling wifi connectivity, and set up SSH public key for the said user) that I'd have to create separately.
To understand how the rpi-imager
would behave on the command line, you'd need to at least understand how the graphical Raspberry Pi Imager tool operates.
The basic command to launch the tool is:
/Applications/Raspberry\ Pi\ Imager.app/Contents/MacOS/rpi-imager
But a more apt command would be the one with the debug option to reinforce our understanding:
/Applications/Raspberry\ Pi\ Imager.app/Contents/MacOS/rpi-imager --debug
Here are the available options listed as indicated from --help
output (I slightly modified the output for readability). Although, each one won't be covered here. The --cli
option will be covered later.
rpi-imager [--debug] [--version] [--repo <repository URL>] [--qm <custom qm translation file>] [--disable-telemetry] [<image file to write>]
-OR-
rpi-imager --cli [--disable-verify] [--sha256 <expected hash>] [--debug] [--quiet] <image file to write> <destination drive device>
At this step, you should have the graphical Raspberry Pi Imager launched with the rpi-imager --debug
command.
You will be creating an image with the customization here, which you can later re-use if you need to burn the image again with the same customization. Those steps only need to be done once (i.e. the resulting image is what you anticipated). Otherwise, you'd need to redo the steps again.
The steps should be intuitive to follow:
- Choose your Operating System (hopefully a Raspberry Pi OS would be picked at this step)
- Choose your Storage
- Write to Storage
Although, before you proceed to the WRITE step, you should see the cog wheel button to access the Advanced Option for customization.
Once you click on the button, you'd be prompted:
Would you like to prefill the wifi password from the system keychain?
Proceed if you are on a Mac currently connected using wifi as it'd attempt to use the same SSID and password to configure the Raspberry Pi to connect to your wifi network later.
In the Advanced Option, you'd have the ability to set up the following:
- Hostname
- Enable SSH server
- Using password authentication OR
- ... setup public-key authentication
- Setup a username and password
- Configure wireless connection using SSID and password
- Set the locale settings
The remaining Persistent settings are only for the Raspberry Pi Imager tool itself which I won't cover here.
You can proceed to SAVE and write to the storage card when you are done.
After you have written the image to your card, your card may be ejected by the Imager.
Now you would need to access your SD card through macOS's Finder or through the /Volumes
directory. Physically reinsert the SD card if you can't access the location.
In the SD card's boot/
or bootfs/
directory, you should have the firstrun.sh
file as the source code found here was responsible for generating the script file. Back up the file to another location on the computer. For the sake of demonstration here, we'll make a copy to the ~/Desktop
directory.
It's important that you can access the same firstrun.sh
file again in the future if you wish to re-image using the same Raspberry Pi OS and its customization.
With the firstrun.sh
file you have, which was auto-generated for you during the graphical Raspberry Pi Imager steps, you are ready to use the rpi-imager --cli
command.
If you attempt to run rpi-imager --cli
with no argument, you'd see more usable options than the ones you see with --help
output. See below:
Usage: --cli [--disable-verify] [--sha256 <expected hash> [--cache-file <cache file>]] [--first-run-script <script>] [--debug] [--quiet] <image file to write> <destination drive device>
We'll only cover a few options here.
Apparently the --first-run-script <script>
option is where we'll use the firstrun.sh
file.
The mandatory <image file to write>
option is where your OS image filepath should be. Unfortunately, the Raspberry Pi Imager doesn't provide us a filepath conveniently accessible to us (e.g. automatically download the OS image file to ~/Downloads
directory).
However, all the Raspberry Pi OS images can be found on the website here: https://www.raspberrypi.com/software/operating-systems/.
The Raspberry Pi Imager uses the image download locations defined in a downloadable JSON file hosted at the URL address which is officially documented here.
Here's a small Python script you can use to get direct URL address of a specific Raspberry Pi OS image (a 64-bit version of Raspberry Pi OS Lite was used here):
#!/usr/bin/env python3
import json
import urllib.request
url = "https://downloads.raspberrypi.com/os_list_imagingutility_v3.json"
response = urllib.request.urlopen(url)
data = response.read().decode('utf-8')
json_data = json.loads(data)
# See JSON objects with all Raspberry Pi OSes as shown on
# https://www.raspberrypi.com/software/operating-systems/
# print(json_data['os_list'][1]['subitems'])
# Get 'Raspberry Pi OS Lite (64-bit)' URL address
print(json_data['os_list'][1]['subitems'][3]['url'] )
At the time of this writing, the output was:
https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-05-03/2023-05-03-raspios-bullseye-arm64-lite.img.xz
Lastly, we need to get the SD card's location. The best way to do this is use the diskutil list
. You'd need to look for the physical disk that reflects the size of it. It's usually not /dev/disk0
or /dev/disk1
in most default macOS customization.
In a sample output below, the physical disk is /dev/disk2
:
/dev/disk2 (internal, physical):
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *63.9 GB disk2
1: Apple_HFS Untitled 63.8 GB disk2s1
Assuming that your OS image file and your firstrun.sh
script file are in the same directory, here's the complete command below:
/Applications/Raspberry\ Pi\ Imager.app/Contents/MacOS/rpi-imager \
--cli \
--debug \
--first-run-script firstrun.sh \
2023-05-03-raspios-bullseye-arm64-lite.img.xz \
/dev/disk2
Once you run the complete command, the Raspberry Pi Imager would prompt you to provide your password to allow it to write to the SD card. Then that's it! You're done.
You would only need 3 things to ensure that you'd be able to recreate the same image again and again indefinitely (assuming the direct URL addresses wouldn't change):
- Raspberry Pi Imager (you'd probably need to use the same version)
firstrun.sh
script file- OS image file
Although, you may need to resort to using a previous supported macOS version if the Raspberry Pi Imager is no longer compatible with a future version. Let's just hope that the rpi-imager --cli
command options and functionalities would be preserved in all future Raspberry Pi Imager releases as they should be compatible with future macOS releases in theory.