Skip to content

Build your own ISO

Yes, you can produce an Obarun image on your own machine and end up with an .iso file you can write to a stick. It is the same script the project runs every month, iso-builder, and it is meant to be run from inside the cloned repository.

Local builds work, but CI is the primary use case

The repository's own README says it plainly: local builds are possible but not what the pipeline is tuned for. If something breaks, check the pipeline before assuming your machine is at fault.

What you need

  • Root. The script uses losetup, mkfs.ext4, mount and chroot. There is no rootless mode.
  • Disk, on a real filesystem. Around 6 GiB for a minimal build: an 8 GiB ext4 image of which the theme fills part, a 1.1 GiB squashfs, and a 1.3 GiB ISO. A jwm build pulls several times more packages and needs noticeably more.
  • obinstall, because the whole rootfs is built by obinstall-cli, and because the themes live in its package under /var/lib/obarun/obinstall/themes/.
  • The build tools, the ones the pipeline installs:
# pacman -S --needed obinstall mkinitcpio mtools dosfstools squashfs-tools \
    libisoburn grub syslinux arch-install-scripts qemu-base oblog fakeroot

qemu-base is there for the last step, which boots the finished image headless for three seconds as a smoke check.

Build it

Where you build matters: not /tmp, not /root

/var/tmp is the safe choice. Two different traps sit on either side of it.

/tmp is a tmpfs, mounted by the boot module and sized from your RAM. On a 4 GiB machine that is under 2 GiB, and the build needs several times that. The ext4 image cannot grow, so the loop filesystem flips read-only somewhere in the middle of the package install:

error: could not create directory .../var/lib/pacman/local/graphene-1.10.8-2/: Read-only file system
error: failed to commit transaction (transaction aborted)
ERROR   [pacstrap] Step pacstrap failed

Check before you start: findmnt /tmp tells you the type and df -h /tmp the size.

/root is mode 0700. iso-live/pacman.conf sets DownloadUser = alpm, so pacman drops to the unprivileged alpm user to download, and that user cannot cross root's home. The build dies on the very first sync:

error: could not open file .../sync/download-XXXXXX/obcore.db.part: Permission denied
error: failed to synchronize all databases (failed to retrieve some files)

The pipeline meets neither, because it builds in $CI_PROJECT_DIR, on disk and world-traversable.

# mkdir -p /var/tmp/isobuild && cd /var/tmp/isobuild
# git clone https://git.obarun.org/obdev/iso-builder.git
# cd iso-builder
# OBINSTALL_NO_UPDATE=1 ./scripts/build-iso.sh

Run it from inside the cloned iso-builder/ directory, the one holding scripts/ and iso-live/. The script takes $PWD as its base and looks for iso-live/ under it, so it fails anywhere else.

OBINSTALL_NO_UPDATE=1 is not optional in practice

Without it, obinstall tries to update itself before installing anything. That path runs makepkg as the nobody user, makepkg calls sudo to install its missing build dependencies, and sudo asks for a password nobody does not have:

[sudo] password for nobody:
Sorry, try again.
[sudo] password for nobody:

The build sits there until you interrupt it. With the variable set, the step is skipped outright and the log says so:

INFO    update.skip  reason='OBINSTALL_NO_UPDATE env'

obinstall-cli also takes --no-update, and [update].enabled = false in /etc/obarun/obinstall/obinstall.conf disables it permanently, but the environment variable is what reaches obinstall-cli through build-iso.sh, which calls it for you.

If you use sudo rather than a root shell

sudo drops the variable. Pass it as part of the command, or export it and use sudo -E:

$ sudo OBINSTALL_NO_UPDATE=1 ./scripts/build-iso.sh

sudo ./scripts/build-iso.sh with the variable merely exported in your shell runs the self-update anyway, and you are back to the password prompt.

What comes out

iso/obarun-<date>-x86_64.iso
iso/obarun-<date>-x86_64.iso.sha256

The version is the build date, and the ISO 9660 label matches the file name. That is your image: verify the checksum, write it to a stick as described in Download and write the ISO, and boot it.

The last step boots the finished image in QEMU for three seconds and reports QEMU smoke test passed. If that line is there and the exit status is 0, the image at least gets through its bootloader.

One warning you can ignore

Early in the run, obinstall tries to update itself and gives up:

WARNING update.failed  reason="[...] makepkg [...]"

It is not fatal, the build carries on and the image is fine.

Choosing the theme

THEME defaults to minimal. Any theme the installed obinstall package provides works, and the name ends up in the image name so two themes do not collide:

# THEME=jwm ./scripts/build-iso.sh
THEME Image
minimal obarun-<date>-x86_64.iso
jwm obarun-JWM-<date>-x86_64.iso

List what your machine offers:

$ ls /var/lib/obarun/obinstall/themes/

How the build works

iso-builder ships no package list and no theme of its own. It creates an empty ext4 image, mounts it, and calls obinstall-cli on it, exactly the way an operator would call it on a disk.

The live medium and a disk install come out of the same code path. Package installation, the theme overlay, the 66 services, the users and the initramfs are all the installer's work in both cases. What iso-builder adds is only what a bootable medium needs:

  1. Create and mount the ext4 image.
  2. Run obinstall-cli against it with the chosen theme.
  3. Release the chroot bind mounts.
  4. Install the live-only packages from iso-live/extra_packages.
  5. Overlay iso-live/rootfs/<theme>/ and fix ownership.
  6. Build the archiso initramfs with mkinitcpio.
  7. Wipe caches, logs and /etc/machine-id.
  8. Write version markers, then the squashfs.
  9. Assemble the BIOS and UEFI boot paths, then xorriso.
  10. Boot the result headless for three seconds.

Customising what the image ships

Where a change goes depends on whether it belongs to the installed system or to the medium:

What you want to change Where it goes
A package, a service, a dotfile, a user The obinstall theme, in the obinstall repository, not in iso-builder
A package only the live medium needs iso-live/extra_packages
A file only the live medium needs, such as the /etc/issue announcing the live credentials iso-live/rootfs/<theme>/
A repository or a pacman option the rootfs is built with iso-live/pacman.conf
A boot menu entry or a kernel command line iso-live/syslinux/, iso-live/grub/, iso-live/isolinux/

So for an image with your own package set, the thing to write is an obinstall theme, not a fork of iso-builder. A theme is a package list, a set of 66 services to enable, files to overlay, and a customizeChroot hook. See docs/theme-authoring.md.

That way your customisation works identically for a disk install and for an image.

Two pacman configs, not interchangeable

pacman.conf at the top of the clone is the build environment's config. iso-live/pacman.conf is the ISO rootfs's config, passed explicitly to obinstall-cli --pacconf. Leave the second one out and obinstall falls back to the host's /etc/pacman.conf, which is not what the image should be built with.

The older tool

obarun-mkiso, in obextra, is the standalone script that predates this pipeline. It is still packaged, but the official images are no longer built with it, and its shipped default configuration still points at a git host that no longer exists. Prefer iso-builder and an obinstall theme.