# Complete Guide to Creating a Custom Arch Linux ISO [![Scheduled ArchISO Build](https://git.vilaythong.us/alvinlollo/AlvinArch/actions/workflows/build-iso.yaml/badge.svg?branch=main)](https://git.vilaythong.us/alvinlollo/AlvinArch/actions?workflow=build-iso.yaml) [![Tests](https://git.vilaythong.us/alvinlollo/AlvinArch/actions/workflows/tests.yaml/badge.svg?branch=main)](https://git.vilaythong.us/alvinlollo/AlvinArch/actions?workflow=tests.yaml) DistroTube: Dotfiles install file structure: ```bash AlvinArch/ ├── airootfs/ │ ├── etc/skel/ │ │ ├── .config/bat/ # bat + catppuccin theme │ │ ├── .config/fish/ # 20 files, quickshell guard │ │ ├── .config/nvim/ # LazyVim (14 files) │ │ ├── .config/hypr/custom/ # 8 files (kdeconnectd + cursor only) │ │ ├── .config/starship.toml │ │ ├── .zshrc # adapted (no hardcoded paths) │ │ └── .fzf.zsh │ └── root/customize_airootfs.sh # pre-installs oh-my-zsh, lazy.nvim, plugins ├── localrepo/ │ ├── build-end4-deps.sh # non-interactive AUR build (all deps) │ ├── end4-pkgbuilds/ # microtex-git, quickshell-git, bibata │ ├── illogical-impulse-quickshell-git-*.pkg.tar.zst │ ├── illogical-impulse-bibata-modern-classic-bin-*.pkg.tar.zst │ └── update.sh # existing, regenerates repo db └── packages.x86_64 # 420 packages (65+ end-4 deps added) ``` ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites](#prerequisites) 3. [Directory Structure](#directory-structure) 4. [Core Configuration Files](#core-configuration-files) 5. [User Configuration (skel/)](#user-configuration-skel) 6. [System Services & Networking](#system-services--networking) 7. [Local Repository (localrepo/)](#local-repository-localrepo) 8. [Boot Configuration](#boot-configuration) 9. [Automated Installer (alvin-install)](#automated-installer-alvin-install) 10. [Build & Test](#build--test) 11. [Planned Additions](#planned-additions) 12. [Troubleshooting](#troubleshooting) --- ## Overview **AlvinArch** is a custom Arch Linux live ISO built on the [archiso](https://wiki.archlinux.org/title/Archiso) framework. It ships a Hyprland desktop with [end-4 dots-hyprland](https://github.com/end-4/dots-hyprland), a curated set of ~400 packages, and a local repository of pre-built AUR packages for packages not available in official repos. The ISO targets both real hardware and VMs (VirtualBox/Hyper-V/VMware guest services are enabled out of the box). ### Key Features - **Hyprland + end-4** — Wayland compositor with illogical-impulse dotfiles (quickshell, bibata cursors, microtex) - **Automated installer (`alvin-install`)** — gum TUI: LUKS with systemd initramfs or experimental touchscreen unl0kr, optional TPM2 auto-unlock, GitHub SSH key import, login manager choice (none/greetd/sddm/gdm), optional sshd (on by default), autologin; the build stamp identifies the live ISO via its `/etc/motd` - **LazyVim** — Pre-configured Neovim with lazy.nvim and curated plugins - **Fish + Zsh + Starship** — Dual shell setup with oh-my-zsh and a cross-shell prompt - **Local AUR repo** — Pre-built packages (end-4 deps, wallust, grub-customizer, etc.) that aren't in official repos - **Curated packages** — ~400 packages across ccache, networking, dev tools, and desktop utilities - **Guest VM support** — VirtualBox, Hyper-V, and VMware tools auto-enabled --- ## Prerequisites ### Required Tools ```bash # Install archiso and dependencies sudo pacman -S archiso git base-devel # Optional but recommended sudo pacman -S arch-install-scripts squashfs-tools ``` ### Knowledge Requirements - Basic Linux system administration - Understanding of Arch Linux package management - Shell scripting basics - Git version control (for managing your project) --- ## Directory Structure ### Complete Project Layout ``` AlvinArch/ ├── profiledef.sh # ISO metadata, compression, boot modes, file permissions ├── packages.x86_64 # ~400 live-environment packages (Hyprland, end-4, dev tools) ├── bootstrap_packages # Minimal build-time packages (base, arch-install-scripts) ├── pacman.conf # Repos: cachyos → core → extra → multilib → chaotic-aur → local-aur ├── airootfs/ # Root filesystem overlay (copied into live ISO) │ ├── etc/ │ │ ├── os-release # NAME="Alvin Arch Linux", ID=arch │ │ ├── hostname # Default hostname: archiso │ │ ├── motd # Welcome text with install instructions and color bar │ │ ├── locale.conf # LANG=en_AU.UTF-8 │ │ ├── locale.gen # Locale generation file │ │ ├── localtime → ../usr/share/zoneinfo/... # Timezone symlink │ │ ├── resolv.conf # DNS resolver config │ │ ├── passwd # System user accounts │ │ ├── shadow # Password hashes (mode 400) │ │ ├── clamav/ # ClamAV antivirus config (certs, clamd, freshclam, milter) │ │ ├── skel/ # Home directory template for new users │ │ │ ├── .config/ │ │ │ │ ├── bat/ # bat + Catppuccin Macchiato theme │ │ │ │ ├── fish/ # Fish shell config (~20 files, nvm, fisher, hyprland helper) │ │ │ │ ├── nvim/ # LazyVim setup (init.lua, plugins/, config/) │ │ │ │ ├── hypr/custom/ # Hyprland dotfiles (keybinds, rules, execs, env, general, scripts) │ │ │ │ └── starship.toml # Starship cross-shell prompt │ │ │ ├── .zshrc # Zsh config (oh-my-zsh compatible) │ │ │ └── .fzf.zsh # Fzf shell integration │ │ ├── systemd/ │ │ │ ├── system/ # Service files and enablement symlinks │ │ │ │ ├── choose-mirror.service # Auto mirror selection │ │ │ │ ├── pacman-init.service # Keyring initialization │ │ │ │ ├── livecd-alsa-unmuter.service # Unmute ALSA on boot │ │ │ │ ├── livecd-talk.service # LiveCD talk helper │ │ │ │ ├── multi-user.target.wants/ # Enabled services (iwd, NetworkManager, sshd, vboxservice, │ │ │ │ │ # hv_kvp/vss/fcopy, vmtoolsd, ModemManager, etc.) │ │ │ │ ├── sysinit.target.wants/ # systemd-timesyncd, systemd-time-wait-sync │ │ │ │ ├── sockets.target.wants/ # systemd-networkd, pcscd │ │ │ │ ├── network-online.target.wants/ # systemd-networkd-wait-online │ │ │ │ └── sound.target.wants/ # livecd-alsa-unmuter │ │ │ ├── networkd.conf.d/ # IPv6 privacy extensions │ │ │ ├── resolved.conf.d/ # systemd-resolved config │ │ │ ├── journald.conf.d/ # Volatile journal storage │ │ │ └── logind.conf.d/ # Do-not-suspend policy │ │ ├── pacman.d/ │ │ │ ├── hooks/ │ │ │ │ ├── uncomment-mirrors.hook # Enable mirrors after install │ │ │ │ └── zzzz99-remove-custom-hooks-from-airootfs.hook │ │ │ ├── chaotic-mirrorlist # Chaotic AUR mirrors │ │ │ └── cachyos-mirrorlist # CachyOS mirrors │ │ ├── ssh/sshd_config.d/ # SSH live-environment config (10-archiso.conf) │ │ ├── modprobe.d/ # broadcom-wl.conf (Broadcom WiFi driver) │ │ └── mkinitcpio.conf.d/ # archiso initramfs config │ ├── usr/ │ │ ├── lib/os-release # usr-merge os-release │ │ ├── local/bin/ # alvin-install, choose-mirror, Installation_guide, livecd-sound │ │ ├── local/share/livecd-sound/ # ALSA config (asound.conf.in) │ │ └── share/pacman/keyrings/ # CachyOS GPG keyring + trust data │ └── root/ │ ├── .automated_script.sh # Automated install entry point │ ├── .zlogin # Zsh login hook │ ├── .gnupg/scdaemon.conf # GPG smartcard config │ ├── .live-pacman.conf # Pacman config for live session │ └── customize_airootfs.sh # Build-time chroot script (oh-my-zsh, lazy.nvim, plugins) ├── localrepo/ # Pre-built AUR packages (indexed as local-aur repo) │ ├── build-end4-deps.sh # Non-interactive AUR builder for end-4 dependencies │ ├── update.sh # Regenerates repo DB + syncs names to packages.x86_64 │ ├── end4-pkgbuilds/ # PKGBUILDs: quickshell-git, microtex-git, bibata-modern-classic-bin │ ├── *.pkg.tar.zst # Pre-built packages (illogical-impulse-*, wallust, grub-customizer, │ │ # snapd, upscayl, meshy-git, pacserve, etc.) │ └── local-aur.db{,.tar.gz} # Repo database files ├── efiboot/ # UEFI boot (systemd-boot) │ └── loader/ │ ├── loader.conf │ └── entries/ │ ├── 01-archiso-linux.conf │ ├── 02-archiso-speech-linux.conf │ └── 03-archiso-memtest86+x64.conf ├── grub/ # GRUB boot (BIOS + UEFI) │ ├── grub.cfg │ └── loopback.cfg └── syslinux/ # BIOS boot (syslinux) ├── syslinux.cfg ├── archiso_head.cfg ├── archiso_pxe-linux.cfg ├── archiso_sys.cfg ├── archiso_sys-linux.cfg ├── archiso_tail.cfg └── splash.png ``` ## Core Configuration Files ### **profiledef.sh** Defines the entire ISO build. Key settings: `iso_label="AlvinArch_..."`, `bootmodes` (BIOS syslinux + UEFI systemd-boot), `airootfs_image_type="squashfs"` with xz compression. Edit first to change branding or compression. ### **packages.x86_64** The main package list (~400 packages). Packages from allowed repos (`cachyos`, `extra`, `multilib`, `chaotic-aur`) are installed directly. Packages in `localrepo/*.pkg.tar.zst` are pulled from the `[local-aur]` repo. Keep sorted alphabetically. ### **bootstrap_packages** Minimal packages for the mkarchiso bootstrap phase: `base` and `arch-install-scripts`. ### **pacman.conf** Defines the repo order: **cachyos → core → extra → multilib → chaotic-aur → local-aur**. The `[local-aur]` repo points to the local `localrepo/` directory for pre-built AUR packages. --- ## User Configuration (skel/) Dotfiles in `airootfs/etc/skel/` are copied into every new user's home directory on the live ISO. ### **.config/bat/** [bat](https://github.com/sharkdp/bat) with a Catppuccin Macchiato color theme. Provides `bat` as a `cat` replacement. ### **.config/fish/** Fish shell configuration (~20 files). Includes nvm integration, fisher plugin manager, Hyprland auto-detection (`auto-Hypr.fish`), frozen theme/keybindings, and custom functions (`edit`, `update`, `lzd`). ### **.config/nvim/** Full LazyVim setup powered by lazy.nvim. Includes `init.lua`, plugin specs for Obsidian, Arduino, and Harper (grammar), plus standard LazyVim config/options/keymaps/autocmds. ### **.config/hypr/custom/** Hyprland dotfiles from [end-4 dots-hyprland](https://github.com/end-4/dots-hyprland): | File | Purpose | |------|---------| | `keybinds.lua` | Window manager keybindings | | `general.lua` | Appearance, gaps, border settings | | `rules.lua` | Window rules (floating, size, class matching) | | `execs.lua` | Autostart commands | | `env.lua` | Environment variables | | `variables.lua` | Shared variable definitions | | `scripts/` | Helper scripts (video wallpaper restore, paste-as-type) | ### **.config/starship.toml** Cross-shell prompt config for [Starship](https://starship.rs/). Works with both Fish and Zsh. ### **.zshrc / .fzf.zsh** Zsh configuration adapted for oh-my-zsh (installed by `customize_airootfs.sh`) and fzf shell integration. --- ## System Services & Networking ### Services (enabled via symlinks) | Service | Purpose | |---------|---------| | `iwd` | WiFi management | | `NetworkManager` | Network connections | | `sshd` | SSH access to live environment | | `vboxservice` | VirtualBox guest additions | | `hv_kvp_daemon`, `hv_vss_daemon`, `hv_fcopy_daemon` | Hyper-V integration | | `vmtoolsd`, `vmware-vmblock-fuse` | VMware guest tools | | `ModemManager` | Mobile broadband | | `pacman-init` | Initializes GPG keyring before package operations | | `choose-mirror` | Auto mirror selection (triggered by `mirror` kernel param) | | `livecd-alsa-unmuter` | Unmutes audio on live boot | ### Network Config - **systemd-networkd** with IPv6 privacy extensions enabled - **systemd-resolved** for DNS - DHCP auto-configured for wired (ethernet) and wireless (iwd) ### Other System Config - **ClamAV** — Antivirus daemon config included in `airootfs/etc/clamav/` - **mkinitcpio** — Archiso-specific initramfs config - **logind** — No-suspend policy for live session - **journald** — Volatile (in-memory) log storage --- ## Local Repository (localrepo/) Pre-built AUR packages that aren't available in any allowed repo. The `local-aur` repo is configured in `pacman.conf` and picked up automatically by mkarchiso. ### Scripts - **`build-end4-deps.sh`** — Non-interactive AUR build for all end-4 (illogical-impulse) dependencies. Run `--quick` to skip large packages. - **`update.sh`** — Regenerates the repo database and syncs new package names to `packages.x86_64`. ### end4-pkgbuilds/ Custom PKGBUILDs for end-4 metapackages: - `quickshell-git` — Quickshell (Qt6/QML shell for Wayland) - `microtex-git` — LaTeX previewer - `bibata-modern-classic-bin` — Bibata cursor theme ### Pre-built Packages Packages like `illogical-impulse-quickshell-git`, `illogical-impulse-bibata-modern-classic-bin`, `wallust`, `grub-customizer`, `snapd`, `upscayl-bin`, `meshy-git`, `xwaylandvideobridge`, `pacserve`, etc. --- ## Boot Configuration ### UEFI — systemd-boot (`efiboot/`) Three boot entries: standard Arch Linux, speech/accessibility, and MemTest86+. ### BIOS — GRUB (`grub/`) `grub.cfg` for BIOS/CSM boot, `loopback.cfg` for booting the ISO from a mounted image. ### BIOS — syslinux (`syslinux/`) Legacy BIOS boot with custom splash screen. Includes PXE network boot config. --- ## Automated Installer (alvin-install) The ISO ships an interactive installer at `/usr/local/bin/alvin-install` (source: `airootfs/usr/local/bin/alvin-install`, ~1,880 lines of bash; the live `/etc/motd` advertises it). It partitions, encrypts, installs, and fully configures a system — including the end-4 dotfiles — without network access to this repo. ### Running it ```bash alvin-install # as root on the live medium (root autologins on tty1) ``` Requirements: **UEFI boot** (it exits if `/sys/firmware/efi` is missing — BIOS/CSM is not supported by the installer) and `gum`. A network connection is required and verified by pinging `archlinux.org` before any disk is touched; the network screen can open `nmtui` for WiFi. Every step is logged to `/var/log/alvin-install.log`, which is copied into the installed system. ### What it asks (gum TUI screens) Keymap → network → target disk (the live USB is hidden from the list) → LUKS encryption (optional; **the LUKS passphrase is the same as your login password**) → experimental unl0kr touchscreen unlock → filesystem (`ext4`/`btrfs`) → username/password/hostname → git identity + GitHub username (both optional) → timezone/locale → login manager → SSH server (enabled by default) → summary. ### What it does 1. GPT partitioning (1 GiB ESP) on the chosen disk, optional LUKS2 on the rest, format + mount at `/mnt` 2. `pacstrap` with the package list derived from the medium's `pkglist.x86_64.txt` (`pacman -Q` of the live ISO) — `archiso`, `syslinux`, `memtest86+` etc. are dropped, hardcoded essentials (kernel, GRUB, NetworkManager, cryptsetup, microcode) always appended 3. User account, sudo, git config, GitHub SSH keys (`ssh-import-id`, falling back to `github.com/.keys`) 4. end-4 dotfiles from the staged `/opt/dots-hyprland` via `./setup install -f -s --skip-alldeps` 5. Initramfs in one of two modes: **Mode A** — systemd initramfs + `sd-encrypt` (default); **Mode B** — busybox hooks + unl0kr touchscreen unlock (experimental, mutually exclusive with TPM2; falls back to Mode A on failure) 6. Optional **TPM2 auto-unlock** (`systemd-cryptenroll`, PCR 0+7; passphrase slot kept as fallback; warns if Secure Boot is off) 7. GRUB (UEFI, boot entry `ALVIN`), login manager — `none` (plain tty, Hyprland autostarts), `greetd`+tuigreet, `sddm`, or `gdm` — falls back to `none` only if the **package** can't be resolved/installed; a failed `systemctl enable` keeps `getty@tty1` as the login path (the summary still reports the chosen manager). Optional autologin (forced off when TPM2 is enrolled), and sshd enabled at boot when selected (default: on) — Arch's stock config allows **password login with the same password as LUKS**, so only accept the default on networks you trust (or harden `sshd_config` afterwards). The final banner reports whether the enable actually succeeded, not just what was selected On failure an `EXIT` trap unmounts `/mnt` and closes the `cryptroot` mapping, so re-running is safe. If the end-4 step reports "incomplete", see the repository's `AGENTS.md` Gotchas for the chroot/systemd limitations that cause it. --- ## Build & Test ### Building the ISO ```bash # Build the ISO (repo root IS the profile directory) sudo mkarchiso -v -w work -o out/ . # Build AUR deps for end-4 (run locally before ISO build if localrepo/ is stale) ./localrepo/build-end4-deps.sh # build all ./localrepo/build-end4-deps.sh --quick # skip large AUR pkgs # Refresh localrepo DB after adding/moving .pkg.tar.zst files ./localrepo/update.sh # re-indexes existing packages ./localrepo/update.sh pkg1.pkg.tar.zst # add files, then re-index ``` ### CI Checks (must pass before merge) - **ShellCheck**: all `.sh` files checked with `shellcheck -x -s bash` - **yamllint**: all `.yml`/`.yaml` files (relaxed config) - **packages.x86_64 syntax**: one package per line, no trailing whitespace, `[a-zA-Z0-9._@+\-]+` only - **Package repo check**: every package in `packages.x86_64` must be resolvable via `pacman -Si` in allowed repos (`cachyos`, `extra`, `multilib`, `chaotic-aur`), OR have a pre-built `.pkg.tar.zst` in `localrepo/`, OR appear in `localrepo/build-end4-deps.sh` ### Recommended VirtualBox Settings (for testing) - RAM: 4GB minimum, 8GB recommended - Video Memory: 128MB - 3D Acceleration: Enabled - Storage: 40GB minimum Guest additions (`vboxservice`), Hyper-V (`hv_*`), and VMware (`vmtoolsd`) tools are all auto-enabled in the live environment. --- ## Planned Additions The following dotfiles are not yet included but are planned for future versions: | Component | Status | Notes | |-----------|--------|-------| | **Alacritty** terminal config | 🚧 Planned | Catppuccin Macchiato theme to match bat | | **GTK 3.0/4.0** theme config | 🚧 Planned | Consistent theming across GTK apps | | **Fontconfig** custom settings | 🚧 Planned | Font rendering and fallback rules | | **Dunst** notification daemon | 🚧 Planned | Notification appearance and behavior | --- ## Troubleshooting ### ISO won't boot - Verify the ISO was built with `sudo mkarchiso` (not as a regular user) - Check that UEFI or BIOS/CSM is correctly set in VM firmware settings - For Ventoy: use the `.iso` directly, don't extract ### No network in live environment - `iwd` handles WiFi — run `iwctl` to connect manually if NetworkManager doesn't detect networks - Ethernet via systemd-networkd with DHCP should work automatically ### Package installation fails during build - Run `./localrepo/update.sh` to refresh the localrepo database - Ensure `localrepo/*.pkg.tar.zst` files are valid and not corrupted - Check that the package name in `packages.x86_64` matches exactly ### LazyVim / oh-my-zsh not installed - `customize_airootfs.sh` clones repos at build time — requires network access during `mkarchiso` - Check build logs for git clone failures --- ## Acknowledgements - [Arch Linux](https://archlinux.org/) and the [archiso](https://wiki.archlinux.org/title/Archiso) project - [end-4 dots-hyprland](https://github.com/end-4/dots-hyprland) for the illogical-impulse Hyprland config - [LazyVim](https://www.lazyvim.org/) for the Neovim starter config - [DistroTube](https://www.youtube.com/watch?v=tSGGBbJBgvk&t=1228s) for the archiso walkthrough that inspired this project