Adds `--distro steamos` as a first-class build target. Pattern is the
same as bazzite-deck/batocera: distros/steamos/build-rootfs.sh fetches
the upstream rootfs (Valve's Steam Deck recovery .img.bz2 from
steamdeck-images.steamos.cloud), extracts the rootfs-A + var-A btrfs
slots into $CHROOT, swaps in our linux-ps5 kernel pkg.tar.zst, and
rebuilds the initramfs with the modules amdgpu needs for the PS5
Oberon GPU.
PS5-specific fixes applied to the SteamOS rootfs:
- Replace /usr/bin/steamos-session-select with a shim that writes
/etc/sddm.conf.d/zzz-session-override.conf directly + restarts sddm.
The vendored helper goes through steamosctl / steamos-manager whose
Deck-hardware code paths silently no-op on PS5, leaving the UI stuck
at "Switching to Desktop..." with no actual session swap.
- Install a small Python dbus monitor (steam-session-switch-listener)
that catches org.freedesktop.DisplayManager.Seat.SwitchToUser from
Big Picture's "Switch to Desktop" button and writes the same
override file. No -steamos3 / -steampal needed (those trigger Steam's
A/B update reboot loop on a non-Deck box).
- Drop a Return-to-Gaming-Mode.desktop into /home/deck/Desktop pointing
at steamos-session-select gamescope.
- chown /home/deck to deck:deck (the recovery image leaves it root:root,
which breaks ~/.config writability on first Plasma launch).
- Enable sshd + set deck:deck so the box is reachable for further
iteration without dragging a USB keyboard around.
- Patch /etc/fstab: comment out the by-partsets atomic mount entries
(those paths don't exist on a flat-rootfs flash), append a LABEL-based
/efi mount so the FAT boot partition comes up cleanly. Required for
ps5-iw620-firmware.service to find the WLAN blob.
- Append amdgpu.dpm=0 + amdgpu.gpu_recovery=0 to the kernel cmdline.
modprobe.d alone isn't enough — amdgpu loads before the rootfs conf
is read, so the cmdline is the only reliable path. Without these
Switch-to-Desktop produces a black screen on the PS5 HDMI output.
- Polkit-deny the deck user reboot/poweroff to break Steam's update
reboot loop on first boot.
- Patch steam-jupiter to skip its OOBE rm -rf path. The OOBE assumes
Deck factory layout and wipes /home/deck/.local if it runs.
Verified end-to-end on real PS5 (HW-rev EA-1102): autologin lands in
gamescope, Switch-to-Desktop and Return-to-Gaming both work, sshd is
reachable, ps5-iw620 brings up internal WiFi after firmware staging.
Files:
- distros/steamos/build-rootfs.sh (646 lines)
- distros/steamos/README.md
- distros/steamos/return-to-gaming-mode.desktop
- distros/steamos/steam-session-switch-listener.{py,service}
- build_image.sh: steamos → FORMAT=arch (uses linux-ps5 pkg.tar.zst)
- .github/workflows/build-image.yml: steamos in matrix options + distros list
PS5 Linux Image Builder
Builds bootable Linux USB images for PlayStation 5 using Docker containers. Supports Ubuntu 26.04, Arch, CachyOS (Gamescope + Steam), Fedora (GNOME), individually or as a multi-distro image with kexec switching.
Prerequisites
- Docker (with permission to run
--privilegedcontainers) — install as per your distro's instructions - ~30GB free disk space for Ubuntu, Arch, or CachyOS
Once Docker is installed, add your user to the docker group and apply it without logging out:
sudo usermod -aG docker $USER
newgrp docker
Quick Start
# Build a single Ubuntu 26.04 image
./build_image.sh --distro ubuntu2604
OR
# Build CachyOS (Arch-based, Gamescope + Steam Big Picture)
./build_image.sh --distro cachyos
OR
OR
# Build Fedora (GNOME desktop)
./build_image.sh --distro fedora
OR
# Build a multi-distro image (ubuntu2604 + arch + cachyos)
./build_image.sh --distro all
The script auto-clones the kernel source, applies PS5 patches, compiles, and builds the image. Subsequent runs reuse cached artifacts automatically. Press Ctrl+C at any time to abort cleanly.
Flash to USB
sudo dd if=output/ps5-ubuntu2604.img of=/dev/sdX bs=4M status=progress
Options
| Flag | Description | Default |
|---|---|---|
--distro |
ubuntu2604, arch, cachyos, fedora, or all |
ubuntu2604 |
--kernel |
Path to kernel source directory | auto-clone version selected by PS5 patch set |
--img-size |
Disk image size in MB | 12000 (32000 for all) |
--clean |
Remove all cached build artifacts and start fresh | off |
--kernel-only |
Build and package the kernel only, then exit | off |
--patches-ref |
Branch, tag, or commit SHA for patches | v1.2 |
Caching
The build automatically skips stages that have already completed:
- Kernel source — reused if
work/linux/exists - Kernel packages — reused if
.deb/.pkg.tar.zstfiles exist inlinux-bin/ - Root filesystem — reused if chroot directories are populated
Use --clean to wipe everything and rebuild from scratch. The build will also suggest --clean if a stage fails.
Build Output
PS5 Linux Image Builder
=======================
Distro: all
(ubuntu2604 arch cachyos)
Image size: 32000MB
Kernel src: /path/to/work/linux
Stages:
1. Kernel cached
2. Root filesystem build
3. Disk image build
Logs: /path/to/build.log
✓ Kernel packages (cached)
✓ Build image builder image
⠹ Building arch rootfs
All verbose output goes to build.log. The terminal shows a spinner with live progress.
Distributions
| Distro | Desktop | Kernel format | Init |
|---|---|---|---|
| Ubuntu 26.04 (Resolute) | GNOME | .deb |
systemd |
| Arch | Sway | .pkg.tar.zst |
systemd |
| CachyOS | Gamescope + Steam Big Picture (Arch + [cachyos] repo, no v3 migration in image build) |
.pkg.tar.zst |
systemd |
Multi-distro Image
--distro all builds a 32GB image with 4 partitions (one EFI boot partition plus three root filesystems):
| Partition | Type | Label | Content |
|---|---|---|---|
| p1 | FAT32 | boot | Shared kernel, per-distro initrds, kexec scripts |
| p2 | ext4 | ubuntu2604 | Ubuntu 26.04 rootfs |
| p3 | ext4 | arch | Arch rootfs |
| p4 | ext4 | cachyos | CachyOS rootfs |
The boot partition contains kexec scripts to switch between distros at runtime. Ubuntu 26.04 is the default boot target.
Building the Kernel Standalone
Use --kernel-only to compile the PS5 kernel and produce installable packages without building a full disk image.
./build_image.sh --kernel-only # .deb (default)
./build_image.sh --kernel-only --distro all # .deb + .pkg.tar.zst
./build_image.sh --kernel-only --patches-ref main # fetch from specific branch/tag
./build_image.sh --kernel-only --clean # wipe and rebuild from scratch
Output packages are written to linux-bin/. Install on a running PS5 Linux system:
sudo dpkg -i linux-bin/linux-ps5_*.deb
Directory Layout
build_image.sh # Image builder (also supports --kernel-only)
docker/
kernel-builder/ # Kernel compilation container
kernel-builder-arch/ # Repackages .deb kernel as .pkg.tar.zst
image-builder/
Dockerfile # Image building container (distrobuilder)
entrypoint.sh # Single-distro build logic
entrypoint-multi.sh # Multi-distro build logic
distros/
ubuntu2604/ # Ubuntu 26.04 (Resolute)
arch/ # Arch Linux
cachyos/ # CachyOS repos + Gamescope/Steam
shared/ # Kernel postinst hooks (single + multi)
boot/
cmdline.txt # Kernel cmdline template (__DISTRO__ placeholder)
vram.txt # VRAM allocation
kexec-{ubuntu2604,arch,cachyos}.sh
work/ # Build artifacts (auto-created)
linux-bin/ # Compiled kernel packages
output/ # Final .img files