Building must happen on a Debian (or Debian-derived, e.g. Ubuntu)
machine — live-build bootstraps a chroot of the target Debian suite using
debootstrap and apt, both of which assume a Debian-family host. If
you're on something else (macOS, Arch, Windows), run the build inside a
Debian VM or container.
sudo apt update
sudo apt install live-build qemu-system-x86 ovmf imagemagick gitDrop your real logo/wallpapers into branding/ (see the README's
"Rebranding" section), then:
cd branding && ./generate-assets.sh && cd ..scripts/build-iso.sh also runs this automatically before every build, so
this step is only needed if you want to preview the generated files first.
sudo ./scripts/build-iso.shWhat this does:
- Regenerates branding assets.
- Runs
lb configto set up a live-build project targeting Debiantrixie(stable) by default — override withDEBIAN_SUITE=testingfor newer GNOME (but see "Known rough edges" below first) orDEBIAN_SUITE=sidfor unstable — with GNOME's full desktop task, Flatpak, Fastfetch, and Calamares in the package list,iso-hybridoutput (boots from USB or optical media, UEFI only for now — see "Known rough edges" below), and nodebian-installer(Calamares replaces it). - Runs
lb build, which debootstraps a chroot, installs packages, applies everything underconfig/includes.chroot/(this is how the branding, Fastfetch config, dconf defaults, and Calamares config get into the image), runs the hooks inconfig/hooks/live/, then squashes it all into a bootable hybrid ISO. - Moves the result to
out/vertex-linux-<version>-amd64.iso.
A full build downloads and installs a complete GNOME desktop from Debian's repositories, so budget for real time and ~8-10GB of disk/network I/O the first time. Subsequent builds reuse live-build's apt cache and are faster.
Pass --clean to wipe previous live-build state first if you've changed
lb config options (suite, architecture, etc.) between builds:
sudo ./scripts/build-iso.sh --clean./scripts/run-vm.sh --uefi # UEFI boot (needs ovmf installed)The ISO is UEFI-only for now (see below), so it won't boot with a plain
BIOS QEMU invocation — always pass --uefi.
This boots the newest ISO in out/ against a persistent virtual disk
(out/vertex-vm-disk.qcow2, created on first run), so you can actually run
the Calamares installer end-to-end and then reboot into the installed
system. Delete that .qcow2 file to start over with a fresh disk.
config/package-lists/vertex.list.chroot— every package installed into the live filesystem: GNOME (task-gnome-desktop), the bootloader, Plymouth, Flatpak, Fastfetch, and Calamares.config/includes.chroot/— a literal overlay copied onto the chroot's root filesystem after packages are installed (so these files win over any package defaults with the same path). This is where all the branding, Fastfetch config, GNOME dconf defaults, and Calamares config live.config/hooks/live/*.hook.chroot— shell scripts run inside the chroot near the end of the build, in filename order: adding the Flathub remote, installing Vertex's own tools (see below), installing the MacTahoe GTK/icon/cursor theme, installing extra GNOME Shell extensions not packaged in Debian, removing Debian branding, compiling the dconf database, activating the Plymouth theme and rebuilding the initramfs, refreshing icon/desktop caches, and enabling core services.config/hooks/live/0130-install-mactahoe-theme.hook.chroot— clones and runs the installers for MacTahoe-gtk-theme and MacTahoe-icon-theme (dark + purple accent, the blur-optimized GTK variant, libadwaita/GTK4 patched directly since it doesn't support theme switching at all). Every flag choice and the resulting installed folder names (MacTahoe-Dark-purplefor GTK,MacTahoe-purple-darkfor icons,MacTahoe-darkfor cursors — genuinely different names, confirmed against the installers' own naming logic, not a typo) are explained inline in the hook itself. No dedicated MacTahoe cursor theme repo exists; the icon theme's installer bundles matching cursors automatically as part of the same script run.config/hooks/live/0140-install-extensions.hook.chroot— installs three GNOME Shell extensions straight from extensions.gnome.org (none are packaged in Debian): Logo Menu (logomenu@aryan_k), Hide Activities Button (Hide_Activities@shay.shayel.org), and Top Bar Organizer (top-bar-organizer@julian.gse.jsts.xyz, used to pin the clock to the far right — see theright-box-orderdconf override). Detects the installedgnome-shellversion and downloads the matching build via the real extensions.gnome.org API (verified working end to end, including schema compilation, before ever going into the ISO).config/includes.chroot/usr/local/sbin/update-vertex-apps.sh— fetches VPKG, Vertex Updater, and Vertex Driver Downloader straight from their GitHub releases (Vertex-Linux/vertex-toolsfor the first two, distinguished only by tag naming —vpkg-<version>vs<version>-vu— andVertex-Linux/vertex-graphics-installerfor the third), strips the Rust target-triple suffix from the downloaded binaries so they run as plainvpkg/vertex-update/vertex-drivers, and creates.desktoplaunchers for the latter two if they don't already exist. Run once at build time by0120-install-vertex-apps.hook.chroot, but it also ships in the image itself at that same path — since none of these are apt packages, re-running it later (by hand, or eventually from within Vertex Updater itself) is the actual update mechanism for all three.config/includes.chroot/etc/calamares/— Calamares installs by copying the live squashfs onto the target disk (unpackfsmodule pointing at/run/live/medium/live/filesystem.squashfs), then runs the usual partition/users/bootloader/locale steps.settings.confdefines the step sequence;branding/vertex/defines the installer's look. Becauseunpackfsclones the entire live filesystem verbatim, the finalshellprocessstep (modules/shellprocess.conf) cleans up everything that's only relevant to the live session — the installer launcher itself, GDM's live-session autologin config, and the calamares/live-boot/live-config packages — so the installed system doesn't boot straight past the login screen or keep an "Install Vertex Linux" icon around.
- Calamares's module configuration is inherently something you tune while
watching a real install run in the VM — the shipped config follows
standard, well-documented Calamares conventions but hasn't been run
against a built ISO yet. If a step misbehaves, check
/var/log/Calamares.loginside the live session and compare against Calamares's module documentation. - UEFI only, no BIOS/legacy boot —
grub-pcandgrub-efi-amd64Conflict:each other in Debian, so both the live media and anything Calamares installs only support UEFI right now. Add BIOS support back later by dropping--bootloaders grub-efifromscripts/build-iso.sh(restoring live-build's BIOS+UEFI default) and shipping a separate, legacy-only variant, since a single image can't offer both without the packaging conflict. - No Secure Boot support yet (
grub-efi-amd64only, unsigned) — addgrub-efi-amd64-signed+shim-signedto the package list later if you need it. - Plymouth boot screen shows an unbranded gray/dots fallback, on both
QEMU and real hardware (tested on a Lenovo ThinkPad E14). Everything
checkable from the outside is confirmed correct:
Theme=vertexis set inplymouthd.confon both the root filesystem and baked into the initramfs itself; the theme's files (including ananimation-0001.pngframe —two-step's primary boot visual, confirmed by comparing against Debian's own bundledspinnertheme, which has nowatermark.pngat all and relies solely onanimation-*.png/throbber-*.png) are present in the initramfs; the kernel command line correctly hassplash quiet(/etc/default/grubandscripts/build-iso.sh's--bootappend-live); andplymouthd/plymouth show-splashboth exit withstatus=0/SUCCESSin the boot log with no errors. So Plymouth is genuinely choosing to render in a degraded fallback mode rather than failing outright — likely a DRM/KMS renderer availability issue at the point Plymouth actually runs (early boot, before the full desktop's graphics stack is up), though/dev/dri/card0and a real framebuffer (virtio_gpudrmfbin the VM case) are confirmed present once the system finishes booting, so it's specifically a timing question, not a missing-driver one. Deliberately parked here rather than chased further — it's cosmetic only, and the next real step is booting withplymouth.debugon the kernel command line and reading/var/log/plymouth-debug.logfor Plymouth's own internal reasoning, which needs a full rebuild+boot cycle to get. - GDM 50's login screen doesn't show a user list at all on
DEBIAN_SUITE=testing(this is why the default istrixie— see above). After a fresh Calamares install, instead of a normal login prompt you get dropped into a full graphical session under GDM's own internalgdm-greeterservice account (visible viagetent passwd 60578-style dynamic UIDs,/run/gdm3/home/gdm-greeteras its home, and/sbin/nologinas its shell) — locking the screen then prompts to re-authenticate as "GDM Greeter" itself, which always fails since it has no real password. This is a confirmed upstream GDM 50 regression (see the Arch Linux bug report "[SOLVED] After GNOME 50 Update — GDM bypasses login-greeter"), not a bug in this project's config. Ruled out before concluding that, in order:gnome-initial-setupnot being purged (it was, no change); a danglingAutomaticLogin=target in/etc/gdm3/daemon.confor/var/lib/gdm3/.config/gdm/custom.conf(neither file has one; the latter doesn't even exist); our customorg.gnome.login-screenlogodconf key crashing greeter UI construction (removed it, no change);/var/lib/AccountsService/users/being empty even thoughbusctl call org.freedesktop.Accounts /org/freedesktop/Accounts org.freedesktop.Accounts ListCachedUserscorrectly lists the account live (pre-seeded the cache file inconfig/includes.chroot/etc/calamares/modules/shellprocess.confanyway — cheap and still worth keeping even though it didn't fix this — note that module's variable-naming gotcha documented inline: its script strings are pre-scanned for any$wordpattern, including positional parameters like$1, and treated as an undefined Calamares GlobalStorage placeholder unless avoided by using$(...)command substitution inline instead of storing values in any variable); and/usr/share/gdm/generate-config(Debian's own greeter-dconf-defaults compiler) producing a corrupt result (confirmed fine — it compiles from/usr/share/gdm/dconf, an entirely separate lockdown-settings mechanism from our own/etc/dconf/db/gdm.d/customization, and isn't the cause). Should GDM ship a fix upstream,DEBIAN_SUITE=testingis worth retrying.