sdm turns one RasPiOS image into a fleet of identical Raspberry Pi cards
Raspberry Pi SD Card Image Manager
At a glance
- What is it?
- sdm is a shell-based SD card image manager for the Raspberry Pi that customises a stock RasPiOS image and burns it to a card in one command, driven by a customization script and a plugin system. It is MIT licensed, ships as a set of sdm-* phase scripts, and its releases track the RasPiOS releases it supports.
- Who is it for?
- Anyone who maintains more than one Raspberry Pi should use sdm, because it turns the per-card setup work into a scripted step that produces identical images with the localisation, user account, SSH and package set already applied, and it burns the card in the same command. It is the wrong tool if you have a single device, where the setup takes longer than the flash, and it is the wrong tool for anything that is not a Raspberry Pi image.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: every card is a manual project
sdm is described as a Raspberry Pi SD card image manager, and the subtitle in the README reads Raspberry Pi SSD/SD Card Image Manager, which is a good hint about how the author thinks about it. SSDs and SD cards are treated as the same thing throughout the documentation.
The problem it solves is stated as a list of situations where you would want it, and the list is worth reading because it defines the tool's scope precisely.
You might have multiple Raspberry Pi systems and want them all to start from an identical and consistent set of installed software packages, configuration scripts and settings. You might want to rebuild your Pi system consistently with all your favourite packages and customisations already installed, every time. You might want to be nice to your future self and make it easy to build fresh customised systems when the next RasPiOS release comes out. You might want to produce disks or images for others without risk of having any of your own bits accidentally distributed. And you might want to do all of this repeatedly, and a lot more quickly and easily.
The last point in that list is the real argument. The tool is not really about making one card, it is about making the second, third and fortieth card identical to the first.
Ready-to-go is defined in the README as every system being fully configured with keyboard mapping, locale, timezone and WiFi set up as you want, with all of your personal customisations and all desired RasPiOS packages and updates installed. In other words, all ready to work on your next project, and the payoff is stated plainly: with sdm you spend a lot less time rebuilding cards, configuring systems and installing packages, and more time on the things you really want to do with your Pi.
The project is MIT licensed, the primary language is shell, and the last push was on 2026-10-10.
ezsdm is the knob, and the defaults are a full setup
The customization surface is a shell script, and the README is explicit about how to start.
The easiest way to get started is to take a copy of /usr/local/sdm/ezsdm into your own directory and edit it as desired.
And if you use the default ezsdm without modifying it, sdm will make a specific set of changes to the image file, which the README lists. It copies your localization settings, meaning keymap, locale, timezone and WiFi country, from the system on which it is running, and that system can be RasPiOS, Debian, or a Debian derivative such as Mint or Ubuntu. It configures the image to have SSH enabled. It creates the user myuser with the given password and enables sudo for that user. It does an apt update and an apt upgrade. It installs a few example apps. It configures WiFi for the SSID myssid and password mywifipassword. And it prevents piwiz and cloudinit from running, with the reason given: neither is needed when using sdm and they could cause unexpected behaviours.
Two of those are worth pausing on. Copying the host's localisation is a shortcut that saves a lot of per-card work, but it also means the image inherits the keyboard layout and timezone of whichever machine you built it on, which is the opposite of what you want for a fleet spread across regions. And disabling piwiz and cloudinit is the right call for an image that has already been set up, since leaving them in place would have them reconfigure a system you have already configured.
The script also carries secrets, in the form of a password and a WiFi key. The README notes that the copies live in your own directory, which is a reasonable place to keep them, but it is a place worth thinking about before you commit one.
One command to customise, burn and expand
Burning an image is a single command, and the flags do several jobs at once.
sudo sdm --burn /dev/sdX --hostname mypi1 --expand-root 2025-10-01-raspios-trixie-arm64.img --plugin sshhostkey:generate-keysThe positional argument is the image file, /dev/sdX is the target device, and the README tells you to modify it to refer to the disk you want to burn, which is the step worth being careful about.
--hostname sets the machine name, and --expand-root resizes the root filesystem after the burn completes, so the card comes up using the whole device rather than the size the image was built at.
The --plugin option runs a plugin as part of the process, and the example is sshhostkey:generate-keys, which regenerates the SSH host keys as part of burning the disk. The reason the README gives is a good one: it ensures that the system has higher entropy than during a system boot, so two cards built from the same image do not end up sharing SSH host keys, which would be a security problem for exactly the kind of fleet this tool exists to produce.
Before burning you need an image to customise, and the README covers that. Download the desired RasPiOS zipped image from the raspberrypi.org website and unxz it, since images from older releases were zipped. Then pick the right variant, with the current Trixie images in the images subfolder of raspios_armhf for 32-bit, raspios_lite_armhf for 32-bit, raspios_arm64 for 64-bit and raspios_lite_arm64 for 64-bit, with Bookworm images in the raspios_oldstable folders.
The distinction between the full and lite images matters, because the lite ones do not include the desktop and change what a first boot looks like.
First boot runs once, and the system restarts twice
The boot behaviour after a burn is unusual, and the README spells it out because it will otherwise look like a fault.
Load the SD card into a Pi and power it up. The system will will not resize the root file system and restart automatically, thanks to the use of --expand-root, which expands the root file system on the SD card after the burn completes. After the system starts it goes through a complete system startup, just as it always does on a fresh SD card. Toward the end of the boot process the sdm FirstBoot service runs, once, and it takes other actions as needed to fulfil the requested configuration. And when the system boot is fully complete, the system automatically restarts again.
So the first boot is a two-stage thing. The first pass expands the filesystem, runs the FirstBoot service and finishes with a restart, and the second pass is the one that lands you at a shell with everything configured. Users who see one extra reboot and assume something is broken will not get far.
The FirstBoot service is also where you can look at what happened, and the README gives the command:
journalctl -b -1 | grep FirstBootThe -b -1 selects the previous boot, so you are reading the log of the run that performed the configuration rather than the one you are currently in.
There is a catch with that log, and the README raises it. Sometime late in Bookworm the default retention of the system journal changed from keeping them all to removing them on shutdown. If you want to retain all system journals, you uncomment or add system:journal=persistent to your personal ezsdm script.
In other words, the diagnostic that tells you what went wrong stops existing on the next reboot unless you change the journal retention in the customization script first. That is a small thing with an outsized effect on how easy this tool is to support.
The plugin set is the extensibility model
The repository listing shows how much of sdm is a framework with pluggable parts, and the directory names describe the surface better than any documentation could.
The main entry points are sdm, which is the command, ezsdm, the customization script, and install-sdm, the installer. The phase scripts are sdm-phase0 and sdm-phase1, with sdm-firstboot, sdm-ssh-initramfs and sdm-apps handling the first boot, the SSH setup in the initramfs and the application installation. There is sdm-apt and sdm-apt-cacher for package work, sdm-customphase for adding your own phase, sdm-spawn for launching processes, sdm-logmsg for logging, sdm-readparams and sdm-cmdsubs for parameter handling, sdm-cparse for parsing, sdm-gburn for the burn step, sdm-rpcsubs for subscriptions, sdm-cportal and sdm-cryptconfig for portal and crypto configuration, sdmcryptfs for encrypted filesystems, sdm-make-luks-usb-key and sdm-add-luks-key for LUKS key handling on USB, sdm-yubi-config for YubiKey configuration, sdm-collect-labwc-config for collecting a lightweight compositor configuration, and satrim, which is a separate tool in the tree.
There are also example plugins, sdm-apps-example and sdm-xapps-example, which are the intended starting point for anyone writing one, and a 1piboot directory that holds the first boot material.
The documentation entry points the README gives are the ones to read first, and they are named in the text: the Command Details and the Plugins documents under Docs, plus an Example-Commands document that contains an official getting started script for people who want to adopt sdm into their RasPiOS management tools.
The feature examples in the README point the same way. You can install applications with the apps plugin, so every card comes up with the editors and browsers you always install, and you can install and configure VNC, using RealVNC on the console, or TightVNC or TigerVNC virtual desktops, or a combination of both. Each of those is a plugin rather than a flag, which is the design decision that makes the tool extensible without making it configurable.
Installing means piping a script to bash, and the README says to read it first
The installation section offers two routes, and the second one is the interesting one.
curl -L https://raw.githubusercontent.com/gitbls/sdm/master/install-sdm | bashThe install-sdm script installs the sdm files to /usr/local/sdm, and installs other required RasPiOS packages if they are not installed. The README names them: binfmt-support, coreutils, gdisk, keyboard-configuration, parted, qemu-user-static, rsync, systemd-container and uuid.
And then the alternative, which is the one to prefer:
curl -L https://raw.githubusercontent.com/gitbls/sdm/master/install-sdm -o /path/to/install-sdm
chmod 755 /path/to/install-sdm
# Inspect the install-sdm script if desired
/path/to/install-sdmDownload the Installer script to examine it before running it. The script is fetched from the master branch, so the content changes as the project does, and the tool is by design a script that copies files to /usr/local/sdm and installs system packages with root privileges.
The rest of the repository supports the same idea. There is a completion.sdm file for shell completion, a CHANGELOG.md, the Docs directory, the extras directory, the plugins directory, and an EZsdmInstaller script. The naming of the sdm-* scripts follows the phase structure, and knowing that structure is what lets you drop your own script in at the right point, which is what sdm-customphase exists for.
The release cadence is fast. The recent versions are V15.8 on 2026-08-05, V16.0 on 2026-10-09 and V16.1 on 2026-10-10, and the last push was on 2026-10-10, so the project moves with the RasPiOS releases it supports rather than on a fixed schedule.
Editorial conclusion
Anyone who maintains more than one Raspberry Pi should use sdm, because it turns the per-card setup work into a scripted step that produces identical images with the localisation, user account, SSH and package set already applied, and it burns the card in the same command. It is the wrong tool if you have a single device, where the setup takes longer than the flash, and it is the wrong tool for anything that is not a Raspberry Pi image. Before running the installer, download the install-sdm script and read it, as the README itself suggests, and copy ezsdm before editing it so you can compare your configuration with the defaults.
Frequently asked questions
How do I install sdm?
The README gives curl -L https://raw.githubusercontent.com/gitbls/sdm/master/install-sdm | bash, which installs the sdm files to /usr/local/sdm along with required RasPiOS packages including binfmt-support, coreutils, gdisk, parted, qemu-user-static, rsync, systemd-container and uuid. The README also shows downloading the installer first so you can inspect it before running it.
How do I customise the Raspberry Pi image that sdm burns?
Take a copy of /usr/local/sdm/ezsdm into your own directory and edit it. The default script copies localisation settings from the host, enables SSH, creates a user with sudo, runs apt update and upgrade, installs example apps, configures WiFi and prevents piwiz and cloudinit from running.
What does the sdm FirstBoot service do?
It runs once toward the end of the first boot, takes the actions needed to fulfil the requested configuration, and the system then restarts automatically. You can review its output with journalctl -b -1 | grep FirstBoot, and the README notes that on Bookworm and later the journal is removed on shutdown unless you add system:journal=persistent to your ezsdm script.
How do I expand the root filesystem and refresh the SSH keys on the burned card?
Pass --expand-root so the root filesystem is expanded on the card after the burn completes, and pass --plugin sshhostkey:generate-keys to regenerate the SSH host keys during the burn, which the README says ensures higher entropy than during a system boot.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gitbls-sdm)Was this useful?