Open-source project
guysoft/CustomPiOS avatar
guysoft/CustomPiOS

CustomPiOS repackages an existing Raspberry Pi image instead of building one

A Raspberry Pi and other ARM devices distribution builder

611 stars169 forksShellGPL-3.0

At a glance

What is it?
CustomPiOS is a shell and Python toolkit that takes a stock Raspbian or Armbian image, applies your own modules inside a chroot and writes out a ready to ship image. It skips kernel work entirely, and it needs root, qemu and a handful of small tools to do it.
Who is it for?
CustomPiOS fits a product team shipping a preconfigured Raspberry Pi or other ARM device, where the value is in what you add to a known-good base image rather than in maintaining a distribution of your own. It is the wrong tool if you need to build a kernel, if your hardware needs a driver that the base image does not carry, or if you want a reproducible build without root and chroot access.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 45 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

It opens an existing image, changes it, and repackages it

CustomPiOS is described as a Raspberry Pi and other ARM devices distribution builder, and the one sentence that explains the whole design is that it opens an already existing image, modifies it and repackages the image ready to ship.

That is the difference from a distribution project. It does not assemble a filesystem from packages, and it does not maintain a kernel. The readme says the repository contains the source script to generate a distribution out of an existing Raspbian distro image, or Armbian devices, and the feature list is explicit that you write only the code you need for your distro, with no need to maintain complicated things like building kernels unless you actually want to.

The payoff is that your artefact inherits a base image that already boots, already has a kernel and already has drivers, and your work is the layer on top. The cost is that you inherit that base image's constraints too, including its kernel and its driver set, which is why the base image choice matters more here than it would in a from-scratch build.

Device coverage is broad by the readme's account, with support for over 40 embedded devices using Armbian and Raspbian, and Raspberry Pi OS arm64 supported through the raspios_lite_arm64 variant. The project is maintained by Guy Sheffer and is GPL-3.0, and the last push was on 2026-09-08 with the 2.0.0 release on 2026-07-14.

Modules are the unit of reuse across distros

The module system is the part of CustomPiOS worth understanding before you write anything, and the first feature in the list is that you write one module and use it for multiple distros.

A module carries a name, and the generator creates a starting module named after your distribution inside the distro folder, under src/modules/<distro name>, with the expectation that there is only one module there at that point. The module is where the code that customises the image lives.

The execution point inside a module is the chroot script. The readme explains that this is where the stuff you want to execute inside the distro is written, and that you use start_chroot_script for the main code and end_chroot_script for cleanup functions, which run at the end of the module namespace.

The distinction between running inside the chroot and running outside it is the important one. A chroot script changes the system that will become the image, so it can install packages, write files and configure services in the way the device will actually run. The host side handles the mechanics of unpacking the image, entering it, running the scripts and repacking the result.

Configuration lives in a config file in the src directory, with module-specific settings that can then be overridden in a distro or a variant. There is also a config.local file for overriding existing settings without touching the tracked ones, and the readme explains that you can override every setting found in src/config that way.

common.sh and the export helpers do the repetitive work

Two sets of helpers keep module scripts short.

The first is common.sh, a script that ships with CustomPiOS carrying functions you can call inside a chroot script, sourced with source /common.sh. The readme documents two of them in detail. unpack takes a source filesystem path, a destination and an optional owner, and puts files from the filesystem folder into that destination with the right ownership, for example unpack /filesystem/home/pi /home/pi pi. gitclone takes a module and repository name plus a destination, and clones a git repository with the settings already preset in the config file, which is how a module pulls in an upstream project such as OctoPrint.

The second is the export mechanism. CustomPiOS can export files created in the chroot into archives that you ship, as a tar.gz. Inside a chroot script you call custompios_export with a name and a file path inside the chroot, and there are two variants, copy_and_export and copy_and_export_folder, which take a source and a destination. The results are saved in the workspace folder.

That combination, a filesystem folder in the repository that is unpacked into the image, plus exported archives produced during the build, is the shape of most real modules. The filesystem folder is how you add files and save them back to the repository, and the export helpers are how you ship a bundled payload out of the build rather than baking it into the base image.

The build needs root, a chroot and qemu for ARM

The requirements list is short and specific, and it explains what kind of build this is.

There is qemu-arm-static, or gentoo qemu with the static USE flag, a downloaded Raspbian image, and root privileges for chroot. Then the ordinary tools: bash, jq, git, realpath, file, sudo, p7zip-full, Python 3.2 or newer and GitPython. The readme notes about sudo that the script itself calls it, so running as root without sudo will not work.

qemu-arm-static is the interesting requirement. A chroot into an ARM filesystem on a non-ARM build machine has to run ARM binaries, and the static user-mode emulator is what makes that possible. That is why the Docker image route exists: the readme lists the CustomPiOS docker image, Linux distributions such as Ubuntu, Debian and Gentoo, and OS X as the configurations known to work.

The output lands in a predictable place. You run src/build as root, and the final image is created at src/workspace. The base image is chosen by config, with the path overridable through BASE_ZIP_IMG in the base module, and by default the most recent file matching the pattern *-raspbian.zip found in src/image is used.

The generator step is separate. Running ./src/make_custom_pi_os with a distro folder creates the folder to build from and downloads the latest Raspbian lite image, and the readme is specific that the folder must not exist and must contain no spaces.

Tests are shell scripts, and the Makefile is the only harness

CustomPiOS tests are shell scripts, and the Makefile at the repository root is the whole harness.

bash
SHELL := /bin/bash

# Find all test files
TEST_DIR := tests
TEST_FILES := $(wildcard $(TEST_DIR)/test_*.sh)

The test target walks the files matching test_*.sh in the tests directory, runs each one and tracks whether every test passed, exiting non-zero if any of them failed. The clean target removes the temporary and log files the tests leave behind. The default target is the test target itself, so a bare make runs the suite.

The readme describes the same thing in one line.

code
    make test

and adds that you can add more tests to the tests folder, so the suite is a directory of executable scripts rather than a framework with fixtures and assertions.

The Python side is small and mostly a build driver. The project metadata declares a custompios package with the version, a Python requirement of 3.11 and newer and GitPython as its dependency, and it exposes a single console entry point.

toml
[tool.poetry.scripts]
custompios_build = 'custompios_core.multi_build:main'

The type stub dependency for YAML types sits in the development group, which tells you the code touches YAML configuration even though the build itself is driven by shell scripts.

Building in CI, and the paths around the direct build

The readme documents several routes to the same image, and the differences between them are about environment rather than output.

The direct route is the one described above: generate a distro folder, edit the config, run the build as root. The readme also points to a Vagrant build section for the case where the direct build fails, and explains that it makes sure sfdisk and other things work right. That is the practical failure mode of this kind of tooling, where the image manipulation step needs a specific set of disk utilities and a specific environment.

There is a Docker route, with a CustomPiOS docker image published on Docker Hub and a wiki entry for building with Docker, and a Vagrant route with its own wiki entry. Both exist because the host requirements are specific enough that a prepared environment is often faster than installing them.

Then there is the CI route. The readme says you can build CustomPiOS images from a continuous integration system in the cloud, and points at a GitHub Actions workflow in the OctoPi repository as an example of how to do it on GitHub. Since OctoPi is built with this tool, that workflow is a working reference rather than a sketch.

The wiki entries for Building, Building with Docker and Building with Vagrant cover the same ground in more detail, and the module documentation is a separate wiki page. The readme itself is deliberately a map.

Where CustomPiOS stops, and what to check first

The boundary of the tool is the base image. Because CustomPiOS modifies an existing Raspbian or Armbian image, it cannot add a kernel, a bootloader or a driver that the base image does not have. If your hardware needs a specific kernel or a driver newer than the base image provides, the answer is a different base image or a different approach entirely, and no amount of module code changes that.

The second boundary is the environment. The build needs root, a chroot into an ARM filesystem, an emulator for ARM binaries and a set of disk utilities, which means it does not run inside a restricted CI runner without preparation, and it is the reason the Docker and Vagrant paths exist.

The third is the shape of the output. A CustomPiOS image is a repackaged base image with your modules applied, not a minimal or reproducible system, and the readme gives no promise about byte-for-byte reproducibility between two builds of the same input.

The checks to run before you commit to it are concrete. Confirm that a base image variant exists for your board, including the arm64 case, and that the kernel it carries supports your hardware. Then write one module with a chroot script that does something small and observable, build it, and confirm the result boots before you build a product around it. The project is at version 2.0.0 with a 1.5.0 release in 2024 and a 1.4.0 in 2020, so the current major line is recent even though the project itself is not, and the last push was on 2026-09-08.

Editorial conclusion

CustomPiOS fits a product team shipping a preconfigured Raspberry Pi or other ARM device, where the value is in what you add to a known-good base image rather than in maintaining a distribution of your own. It is the wrong tool if you need to build a kernel, if your hardware needs a driver that the base image does not carry, or if you want a reproducible build without root and chroot access. Check first that the base image variant matches your board, that the module you write fits the chroot script and common.sh model, and that the build environment has qemu-arm-static and the other listed requirements before you commit to it.

Frequently asked questions

What does CustomPiOS actually build?

It opens an existing Raspbian or Armbian image, modifies it with your own modules inside a chroot, and repackages it ready to ship. It does not build a distribution from packages and does not maintain a kernel.

What do I need installed to build a CustomPiOS image?

qemu-arm-static or gentoo qemu with the static USE flag, a downloaded Raspbian image, root privileges for chroot, and bash, jq, git, realpath, file, sudo, p7zip-full, Python 3.2 or newer and GitPython. The build must run as root because the script calls sudo itself.

Where does the finished image end up?

You run src/build as root and the final image is created at src/workspace. The base image path is set through BASE_ZIP_IMG in the base module, and by default the most recent file matching the pattern *-raspbian.zip found in src/image is used.

How do I test changes to CustomPiOS?

Run make test, which runs every test_*.sh script in the tests folder and exits non-zero if any of them fail. The Makefile is the whole harness, and you can add more tests to the tests folder.

Official sources

  1. guysoft/CustomPiOS on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/guysoft-custompios.svg)](https://hysenlabs.com/projects/guysoft-custompios)

Was this useful?