Skip to content

Latest commit

Β 

History

245 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“¦ BAUER GROUP custom Raspberry Pi OS base images

Declarative, reproducible, CI-built Raspberry Pi OS images for production and development devices. Config lives in JSON, generation is driven by Python, actual image baking runs on top of CustomPiOS.

Supported hardware:

Target Status
Raspberry Pi 4 βœ…
Raspberry Pi 5 βœ…
Compute Module 4 (CM4) βœ…
Compute Module 5 (CM5) βœ…
Raspberry Pi Zero 2 W ❌ not supported β€” 512 MB RAM is insufficient for Podman + Portainer + base services

Base OS: Raspberry Pi OS Lite arm64 (trixie, 2026-06-18) β€” headless, no desktop. These are appliance images: SSH, Podman and Portainer, no GUI. The Desktop edition is not interchangeable here β€” it adds ~3 GiB of rootfs and pushes the image past what a nominally 8 GB CM4 eMMC can hold (see docs/flash.md).


✨ Features

  • Declarative config in JSON, validated against a strict schema.
  • Variant composition via extends β€” a child variant deep-merges onto a base, arrays of {name: ...} records merge by name.
  • Env-var resolver for secrets (${VAR} / ${VAR:-default}), fail-fast on missing required values.
  • Dockerised dev/build runtime β€” no host Python/qemu dependencies required; Linux Β· macOS Β· Windows (WSL / CMD / PowerShell).
  • CI-ready image builds on GitHub Actions with matrix over all variants, artifact + release asset output, full metadata summary per run.
  • Baked-in:
    • SSH enabled with hardened sshd_config.d (no root login, no challenge-response)
    • Podman (daemonless, default runtime) with Docker CLI emulation and IPv6-capable container networking; Docker CE remains a supported, non-default alternative
    • Portainer CE as two Quadlet units under /etc/containers/systemd/ β€” systemd generates and starts portainer.service at boot, no compose file, no first-boot oneshot
    • podman-auto-update.timer keeps Portainer current on a schedule, with a volume backup taken before every update
    • Unattended upgrades with configurable maintenance + reboot windows, event-driven via apt-daily-upgrade.service post-hook
    • Dynamic MOTD banner showing variant, version, kernel, all interfaces with IPv4/IPv6, CAN state + bitrate, service health, pending reboots
    • Admin user with sudo NOPASSWD, su without password via pam_wheel

πŸ’Ύ Flash an image in 30 seconds

You need Raspberry Pi Imager v2.0.3 or later β€” earlier versions don't persist a custom repository between launches.

One-click:

Open BAUER GROUP repository in Raspberry Pi Imager

Imager opens with our catalog pre-loaded and asks for confirmation. After that, every future release shows up automatically β€” including Compute Module eMMC flashing via the built-in rpiboot.

Manual setup:

  1. Open Raspberry Pi Imager β†’ βš™ Settings β†’ Custom repository
  2. Paste https://bauer-group.github.io/XPD-RPIImage/rpi-imager.json
  3. Close the dialog. Imager reloads the OS list automatically β€” our variants appear under CHOOSE OS β†’ BAUER GROUP.

Landing page with direct downloads + full checksums: https://bauer-group.github.io/XPD-RPIImage/

Full flashing guide (SD, USB-SSD, CM4/CM5 via rpiboot, balenaEtcher, manual dd): docs/flash.md.


πŸš€ Quick start

Option 1 β€” dockerised tools (recommended, zero host deps)

# Linux / macOS / WSL

./tools/run.sh validate                    # validate every variant JSON
./tools/run.sh render canbus-plattform     # generate module artifacts
./tools/run.sh build  canbus-plattform     # full image build (privileged)

# Windows CMD

tools\run.cmd build canbus-plattform

# Windows PowerShell

.\tools\run.ps1 build -Variant canbus-plattform

See docs/tools-container.md for launcher reference.

Option 2 β€” local (needs Python 3.14 + Docker)

cp .env.example .env

# edit .env - set ADMIN_PASSWORD and WIFI_PSK

make deps                                  # pip install requirements
make validate                              # schema-check every variant
make build VARIANT=canbus-plattform        # build the image

# .env is picked up automatically when it sits at the repo root.
# From elsewhere, pass it explicitly: make build ENV_FILE=path/to/.env

Output lands in dist/bgrpiimage-<variant>-v<version>.img.xz.

Option 3 β€” GitHub Actions

Push to main or open a PR β†’ automatic build with SHA-stamped artifact (see docs/ci-cd.md).

A conventional commit on main β†’ semantic-release cuts vX.Y.Z and the GitHub Release, then dispatches the image build on that tag so the .img.xz, .sha256 and .manifest.json assets are attached automatically.

The dispatch is a separate job using a PAT: the tag itself is pushed with GITHUB_TOKEN, and GitHub never triggers a workflow from a GITHUB_TOKEN event, so build.yml's tag trigger alone would never fire. If the PAT is ever missing, the release job fails loudly and the assets can be attached by hand:

gh workflow run build.yml --ref vX.Y.Z

πŸ“¦ Variants

Variant Description Hostname Extras
base Generic Raspberry Pi image, Podman-ready, no application-specific hardware. bg-rpi β€”
canbus-plattform Base + Waveshare 17912 dual isolated CAN HAT (MCP2515 on SPI). bg-canbus can0 + can1 at 500 kbit/s with 100 ms bus-off auto-recovery, can-utils, hardware watchdog, dialout/gpio/i2c/spi groups
canbusfd-plattform Base + Waveshare 17075 2-CH isolated CAN FD HAT (2Γ— MCP2518FD), factory "mode A" jumpering. bg-canbusfd can0 + can1 at 500 kbit/s arbitration / 2 Mbit/s data phase, 100 ms bus-off auto-recovery, can-utils, hardware watchdog, dialout/gpio/i2c/spi groups

Adding a new variant is a 10-line JSON file β€” see docs/variants.md.


🧱 Architecture

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ config/variants/*.json │──▢│ scripts/generate.py │──▢│ src/modules/*/filesystem/ β”‚
  β”‚  (declarative, JSON)  β”‚     β”‚  (validate + merge β”‚     β”‚  root/opt/bgrpiimage/    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚   + env resolve)   β”‚     β”‚   (inputs for CustomPiOS)β”‚
                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                      β”‚
                                                                      β–Ό
                                                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                β”‚ guysoft/custompios       β”‚
                                                β”‚ (privileged build in    β”‚
                                                β”‚  docker or GH runner)    β”‚
                                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                            β”‚
                                                            β–Ό
                                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                            β”‚ dist/bgrpiimage-…img.xz  β”‚
                                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

More detail: docs/architecture.md.


πŸ“š Documentation

Topic File
Architecture + build pipeline docs/architecture.md
Flashing (RPi Imager catalog, CM4/CM5 eMMC, Etcher, dd) docs/flash.md
JSON config reference + env resolver docs/configuration.md
Hardware reference (camera, HDMI, RTC, fan, watchdog, overclock, …) docs/hardware.md
Creating a new variant (extends chain) docs/variants.md
Post-flash setup (password Β· WiFi Β· IP) docs/post-flash-setup.md
Dockerised tools container docs/tools-container.md
GitHub Actions CI/CD docs/ci-cd.md
Login banner + unattended updates docs/banner-and-updates.md

πŸ” Secrets & defaults

⚠️ Default credential shipped by this image: admin β†’ 12345678

This is a published default, on purpose: the images are public, so a discoverable credential is what makes them usable at all. It ships expired (chage -d 0) β€” the first login, console or SSH, forces you to set a new one before you get a session, so it cannot silently stay in place, and the MOTD keeps reminding you until it has actually been rotated.

No WiFi PSK ships any more. WiFi is disabled by default; enable it per device with bgrpiimage-setup wifi enable, or bake a network into the variant JSON.

⚠️ Upgrading the canbus-plattform variant from v0.5.0 or older: which physical CAN connector is can0 changes. can0 is now the CS0 chip (screw terminal CAN0); probe order previously gave that name to the CS1 chip. Details and the migration check in docs/hardware.md.

The CAN FD variant has the opposite problem β€” no stable mapping to migrate to:

🚨 canbusfd-plattform: which connector is can0 is not fixed. In the HAT's factory "mode A" jumpering the two controllers sit on different SPI buses (spi0.0 and spi1.0), and the kernel hands out can0 to whichever probes first β€” which nothing orders. Waveshare documents the same behaviour. Check the mapping on the device with bgrpiimage-setup can status (the chip select column) rather than assuming it, and see docs/hardware.md for a ready-made .link recipe if a deployment needs it pinned.

Change credentials at build time (preferred for production)

Bake real values into the image during the build:

  1. Copy .env.example β†’ .env, set real values.
  2. Rebuild: ./tools/run.sh build <variant> --env-file ./.env.
  3. Never commit .env (already gitignored).

In CI, set ADMIN_PASSWORD and WIFI_PSK as repository secrets β€” the workflow passes them through automatically.

Change credentials / network on the device (post-flash)

Every image ships /usr/local/sbin/bgrpiimage-setup β€” a one-stop helper for the routine post-flash changes:

sudo bgrpiimage-setup password                       # rotate admin pw
sudo bgrpiimage-setup password alice                 # rotate another user
sudo bgrpiimage-setup wifi enable "MyNet" "s3cret"   # unblock radio + join
sudo bgrpiimage-setup wifi status                    # rfkill / regdom / link
sudo bgrpiimage-setup wifi disable                   # tear down, drop the PSK
sudo bgrpiimage-setup can status                     # chip select, IRQ, bitrate,
                                                     #   state, restart-ms, counters
sudo bgrpiimage-setup can bitrate can0 250000        # change a CAN bitrate
sudo bgrpiimage-setup can txqueuelen can0 1024       # change a CAN tx queue
sudo bgrpiimage-setup ip eth0 dhcp                   # back to DHCP
sudo bgrpiimage-setup ip eth0 static 10.0.0.5/24 10.0.0.1 1.1.1.1
sudo bgrpiimage-setup status                         # overview

The helper ships inside the image, so its subcommands are those of the version you flashed β€” can needs v0.6.0, can txqueuelen v0.6.1. An unknown command here means the image predates the docs, not a broken install; sudo bgrpiimage-setup status prints the version. See post-flash-setup.md.

From v0.7.0, interactive shells get the usual list shortcuts (ll, la, l) for every account including root, from /etc/profile.d/50-bgrpiimage-shell.sh.

All IP changes land as /etc/systemd/network/05-bgrpiimage-<iface>.network β€” the 05- prefix sorts before the shipped 10-eth.network, and systemd-networkd applies only the first matching file, so ours wins. The shipped file stays on disk, so reverting is just deleting ours.

"Wins" means replaces, not merges. The override has to carry every key the shipped file carried, which is why can bitrate also writes RestartSec= and ip also writes RequiredForOnline=. For the same reason each subcommand refuses an interface of the wrong type: can bitrate eth0 would replace eth0's DHCP configuration, and ip can0 dhcp would drop the CAN bitrate and its bus-off recovery.

See docs/post-flash-setup.md for the full subcommand reference.


πŸ› οΈ Project layout

.
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ schema.json                        # JSON schema for variant config
β”‚   └── variants/
β”‚       β”œβ”€β”€ base.json                      # generic base variant
β”‚       β”œβ”€β”€ canbus-plattform.json          # extends base + CAN additions
β”‚       └── canbusfd-plattform.json        # extends base + CAN FD additions
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ generate.py                        # JSON β†’ CustomPiOS module files
β”‚   β”œβ”€β”€ bootstrap.sh                       # clones CustomPiOS into ./CustomPiOS
β”‚   β”œβ”€β”€ build.sh                           # full image build (privileged docker)
β”‚   └── requirements.txt
β”œβ”€β”€ src/                                   # CustomPiOS distro
β”‚   β”œβ”€β”€ config                             # distro-level config
β”‚   β”œβ”€β”€ modules/                           # bgrpiimage-{base,users,network,boot,
β”‚   β”‚                                      #              hardware,can,podman,docker,
β”‚   β”‚                                      #              portainer,unattended-upgrades}
β”‚   └── variants/                          # per-variant shell config (generated)
β”œβ”€β”€ tools/                                 # portable dev/build runtime
β”‚   β”œβ”€β”€ Dockerfile
β”‚   β”œβ”€β”€ run.sh / run.cmd / run.ps1
β”œβ”€β”€ .github/workflows/build.yml            # CI pipeline
β”œβ”€β”€ Makefile                               # local convenience targets
└── docs/

πŸ“œ License

MIT β€” see LICENSE.

About

Declarative, reproducible, CI-built Raspberry Pi OS images for production and development devices.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages