end-4's ./setup sudo keepalive orphans a `sleep 60` child as ii-builder; userdel then exits 8 and aborts the ISO build (CI exit 8). Sweep uid-matching processes inside the build chroot before userdel, mirroring shadow's user_busy_processes() checks (real/effective/saved uid + root device/inode). Because arch-chroot mounts proc before `unshare --fork --pid`, /proc entry numbers are outer-namespace pids: signal the last NSpid field instead, require matching NSpid depth, and surface kill errors instead of swallowing them. userdel keeps its stderr and, on final failure, dumps the leftover process list before aborting.
Complete Guide to Creating a Custom Arch Linux ISO
DistroTube: https://www.youtube.com/watch?v=tSGGBbJBgvk&t=1228s
Dotfiles install file structure:
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
- Overview
- Prerequisites
- Directory Structure
- Core Configuration Files
- User Configuration (skel/)
- System Services & Networking
- Local Repository (localrepo/)
- Boot Configuration
- Build & Test
- Planned Additions
- Troubleshooting
Overview
AlvinArch is a custom Arch Linux live ISO built on the archiso framework. It ships a Hyprland desktop with 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)
- 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
# 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/ # 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 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:
| 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. 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--quickto skip large packages.update.sh— Regenerates the repo database and syncs new package names topackages.x86_64.
end4-pkgbuilds/
Custom PKGBUILDs for end-4 metapackages:
quickshell-git— Quickshell (Qt6/QML shell for Wayland)microtex-git— LaTeX previewerbibata-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.
Build & Test
Building the ISO
# 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
.shfiles checked withshellcheck -x -s bash - yamllint: all
.yml/.yamlfiles (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_64must be resolvable viapacman -Siin allowed repos (cachyos,extra,multilib,chaotic-aur), OR have a pre-built.pkg.tar.zstinlocalrepo/, OR appear inlocalrepo/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
.isodirectly, don't extract
No network in live environment
iwdhandles WiFi — runiwctlto 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.shto refresh the localrepo database - Ensure
localrepo/*.pkg.tar.zstfiles are valid and not corrupted - Check that the package name in
packages.x86_64matches exactly
LazyVim / oh-my-zsh not installed
customize_airootfs.shclones repos at build time — requires network access duringmkarchiso- Check build logs for git clone failures
Acknowledgements
- Arch Linux and the archiso project
- end-4 dots-hyprland for the illogical-impulse Hyprland config
- LazyVim for the Neovim starter config
- DistroTube for the archiso walkthrough that inspired this project