case study 03 / 09Atlas Labs · Embedded · 2026

Custom Kiosk OS

The kiosk OS only ran on one board model, and the fleet was moving to new hardware that had to behave exactly the same.

Purpose-built Linux image and reproducible build pipeline for Orange Pi 5 Pro kiosk hardware, validated on physical devices.

› bootloader› mount rootfs› network up · tailnet› kiosk.service ok
defect found at build time, fixed
1defect found at build time, fixed
packages, every difference explained
1783 / 1780packages, every difference explained
reproducible image build
~34 minreproducible image build

01The situation

Labelling kiosks run a custom Debian image built with the board vendor's build system. On power-up they log in automatically and open a full-screen browser on a local dashboard. No desktop, no setup by the user.

The image worked on the Orange Pi 5. New units were Orange Pi 5 Pro boards, and the job was to produce an image for them that behaves identically, then prove it.

02What it had to do

  • Same behaviour on the new board, from boot to dashboard.
  • A reproducible build, not a hand-patched device.
  • Per-device secrets, such as WireGuard keys, never baked into the image.

03How it works

  1. Map the boot chain

    Auto-login starts a minimal X session, which launches the browser in kiosk mode on a local web app. A process manager keeps that app running. Each link had to work on the new board.

  2. Add a build target

    A new build target for the 5 Pro board, built inside Docker so anyone can reproduce it. A full build takes about 34 minutes.

  3. Fingerprint and compare

    Scripts take a read-only fingerprint of the reference device, the built image (mounted offline) and the new device: packages, services, config and files. Diffing them shows exactly what differs and why.

  4. Check what matters at boot

    Real-time clock, dashboard on its port, the USB Wi-Fi driver present on the newer kernel. Each was checked on hardware, not assumed.

  5. Keep secrets per device

    WireGuard is configured after flashing, with each device's own key pair and address. The image ships with an empty WireGuard directory.

04What I found along the way

The dashboard never started on its own

The fingerprint diff found it. The app's setup script registered its startup service from inside the build chroot, where it couldn't resolve the user, so the service file said User=undefined. I fixed the image build to write a correct service and enable it.

Videos wouldn't play

Some videos failed on the kiosks. They were H.265 (HEVC), and the browser the kiosk launched was a build with proprietary codecs stripped. Pointing the kiosk at a browser with HEVC support fixed it, confirmed on two devices.

05What changed

  • The new board's image was functionally identical to the reference device when compared live.
  • The package difference came down to the expected board-specific bootloader and support packages, plus a GPU firmware package.
  • The startup bug was fixed in the build itself, so every future image gets it.

06Takeaways

  • Comparing fingerprints beats eyeballing: the one real defect was found by a diff, not by testing the UI.
  • Anything that runs inside a build chroot needs checking for assumptions about users, services and the running system.