Building a ZoiOS Distro
A guide on how to bootstrap and build a ZoiOS distribution (like Parlex Linux) from scratch using Zoi.
Zoi provides a powerful set of tools to act as a "Universal Distro Engine." This allows you to construct an entire, bootable, declarative operating system (ZoiOS) from a standard Linux environment using the zoi system distro build command.
This guide walks you through bootstrapping a ZoiOS system (e.g. Parlex Linux) onto a new disk, assuming the required base packages are available in your configured Zoi registry.
Prerequisites
- A Host Linux System: You need a running Linux environment (like an Ubuntu Live USB or a Fedora host) to perform the initial installation.
- Zoi CLI Installed: The host system must have the Zoi CLI installed.
- Root Privileges: Formatting disks and installing bootloaders requires
sudo. - A Configured Registry: Your Zoi instance must be configured to sync with a registry that provides the essential ZoiOS packages (e.g.
@parlex/linux,@parlex/glibc,@parlex/systemd).
Step 1: Prepare the system.lua Configuration
The entire state of a ZoiOS machine is defined in a system.lua file. This file acts as the blueprint for your new distribution.
Create a file named target-system.lua:
-- target-system.lua
system({
hostname = "my-zoios-machine",
timezone = "UTC",
locale = "en_US.UTF-8",
-- Optional kernel parameters
kernel_params = "quiet splash rw",
})
-- Essential base packages required for a bootable system
packages({
"@parlex/base",
"@parlex/linux",
"@parlex/glibc",
"@parlex/systemd",
"@parlex/bash",
"@parlex/coreutils",
"@parlex/filesystem",
"@parlex/dracut",
"@parlex/grub",
"@parlex/shadow",
"@parlex/sudo",
})
services({
["systemd-journald"] = { enable = true },
["systemd-networkd"] = { enable = true },
["systemd-resolved"] = { enable = true },
})
filesystems({
{
device = "LABEL=ZOIOS_ROOT",
mount = "/",
type = "ext4",
options = "noatime",
},
})
-- Accounts. Created on the target, not the build host.
groups({
wheel = {},
video = {},
})
users({
admin = {
groups = { "wheel", "video" },
shell = "/bin/bash",
-- Generate with: sudo zoi system secret hash 'your-password'
password_hash = "$argon2id$v=19$m=65536,t=3,p=4$...",
}
})Step 2: Partition and Format the Target Disk
This part is deliberately manual. Partitioning a disk is destructive, and which
layout you want depends on whether you need a separate /boot, LVM, LUKS, or a
plain single partition. zoi system distro build refuses to guess.
Disclaimer: Replace /dev/sdX with your actual target device. Be extremely
careful not to format your host machine's drive!
A minimal UEFI layout, using cfdisk or any tool you prefer:
sudo cfdisk /dev/sdXCreate two partitions in cfdisk:
- EFI System Partition - 512MiB, type
EFI System - Linux filesystem - the remainder of the disk
Write the table and quit, then format both:
sudo mkfs.vfat -F 32 -n ZOI_BOOT /dev/sdX1
sudo mkfs.ext4 -L ZOIOS_ROOT /dev/sdX2Filesystem choice
ZoiOS needs hard links and Unix permissions, so ext4, btrfs or xfs. A
FAT-formatted ESP is fine for /boot/efi but cannot hold the root.
Then mount it. distro build operates on whatever is mounted at --target:
sudo mount /dev/sdX2 /mnt
sudo mkdir -p /mnt/boot/efi
sudo mount /dev/sdX1 /mnt/boot/efiPreview first
zoi system distro build --dry-run prints the full build plan, including the
filesystems and packages it will touch, without modifying anything.
Step 3: Run the Universal Distro Builder
Now run the builder against the mounted target. It validates that every package
exists before touching anything, drops the ZoiOS marker, installs the base set,
applies the declared users and groups, generates /etc/fstab, and records the
first generation.
# Orchestrate the entire build
sudo zoi system distro build --target /mnt --config ./target-system.lua
# Or preview it first
sudo zoi system distro build --target /mnt --config ./target-system.lua --dry-runWhat happens under the hood?
In this single step, Zoi:
- Pre-flight: Validates that every package in
system.luaexists in your registry, before touching the disk. - Initializes Sysroot: Drops the
/etc/os-releasemarker (ID=zoios) so the disk is recognized as ZoiOS. - Bootstrap Installation: Directly installs the entire base package set into the target Zoi store and then into the system root.
- Configures Accounts: Reconciles the declared
usersandgroupsagainst the target's/etc/passwd,/etc/shadow,/etc/groupand/etc/gshadow. - Generates fstab: Writes
/etc/fstabfrom the declaredfilesystems. - Builds the First Generation: Creates a record of the installation and links it to the package transaction.
What is not done yet
The bootloader and initramfs are still installed by hand in Step 4, because
the target has no bootloader on it yet. Once the machine is running, zoi system apply and any zoi install/zoi update that touches a kernel handle
the whole pipeline automatically.
Step 4: Finalize the Bootloader and Initramfs
Once the build is complete, you must ensure the bootloader is properly installed and the initramfs (initial ramdisk) is generated. Zoi makes this easy by allowing you to enter an ephemeral shell that is redirected into your new system's root using the --root flag.
1. Generate the Initramfs
ZoiOS systems require an initramfs to handle early boot tasks (like mounting the root partition). You can use any tool you've packaged, such as dracut, mkinitcpio, or booster.
# Example: Generating initramfs using dracut
sudo zoi system distro chroot /mnt
dracut --kver 7.1.4-arch1-1 --force /boot/initramfs-linux.img2. Install the Bootloader
Now install your bootloader (e.g. GRUB) to the disk:
# Finalize the bootloader using Zoi chroot
# This automatically handles mounting /dev, /proc, and /sys into the target
sudo zoi system distro chroot /mnt
grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable
grub-mkconfig -o /boot/grub/grub.cfg3. Post-Build Package Management
Once the OS is bootstrapped, you can continue to manage its package database and configuration from the host using the --root flag. This is useful for installing extra tools or setting up custom registries inside the new OS before first boot.
# Set a custom registry inside the target OS
sudo zoi sync set /path/to/my-registry --root /mnt
# Sync the package database into the target root's system store (/var/lib/zoi)
# Zoi automatically uses --scope system when --root is specified.
sudo zoi sync --root /mnt
# Install additional packages directly into the target OS
sudo zoi install @parlex/neovim @parlex/git --root /mntIsolated State
When using --root, Zoi is fully isolated. System package data is stored
within the target's /var/lib/zoi; user state follows the target user's XDG
or platform-native directories. By default, metadata is synced to the
system-wide store (/var/lib/zoi/pkgs/db), ensuring the new OS is ready for
use even without a pre-configured user home.
Pro Tip
Using zoi system distro chroot /mnt is preferred over manual chroot
because it leverages Zoi's sandbox engine to safely bridge your host's system
devices into the target environment automatically.
Step 5: Reboot
Unmount the filesystems and reboot into your newly built ZoiOS system!
sudo umount -R /mnt
sudo rebootCreating a Bootable USB Stick
You can use Zoi to create a fully persistent, declarative ZoiOS USB stick. This is perfect for a portable "Swiss Army Knife" OS or a secure live environment.
1. Prepare the Configuration
Create a usb-system.lua file. It should look like a standard ZoiOS configuration but optimized for USB (e.g. using ext4 or btrfs with noatime).
2. Partition and Format the USB
Assuming your USB is /dev/sdX:
# Create EFI and Root partitions
sudo parted /dev/sdX -- mklabel gpt
sudo parted /dev/sdX -- mkpart ESP fat32 1MiB 512MiB
sudo parted /dev/sdX -- set 1 esp on
sudo parted /dev/sdX -- mkpart primary ext4 512MiB 100%
# Format and Label
sudo mkfs.vfat -F 32 -n ZOI_BOOT /dev/sdX1
sudo mkfs.ext4 -L ZOI_ROOT /dev/sdX23. Mount and Build
sudo mkdir -p /mnt/zoi-usb
sudo mount /dev/sdX2 /mnt/zoi-usb
sudo mkdir -p /mnt/zoi-usb/boot/efi
sudo mount /dev/sdX1 /mnt/zoi-usb/boot/efi
# Bootstrap the OS
sudo zoi system distro build --target /mnt/zoi-usb --config ./usb-system.lua4. Finalize the Bootloader
Use Zoi chroot to install GRUB to the USB's EFI partition in removable mode:
sudo zoi system distro chroot /mnt/zoi-usb
grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable
grub-mkconfig -o /boot/grub/grub.cfg5. Cleanup and Boot
sudo umount -R /mnt/zoi-usb
syncHow It Works: The ZoiOS Guard
You might wonder why you can't run zoi system apply on a standard Ubuntu or macOS host.
Zoi includes a ZoiOS Environment Guard. Commands like zoi system apply, list, and rollback check for specific OS markers (like ID=zoios or ID=parlex in /etc/os-release). If the system is not a recognized ZoiOS distribution, these commands are safely disabled to prevent accidental damage to a standard generic Linux environment.
The zoi system distro build command is explicitly allowed on generic systems so that you can bootstrap new disks, and its primary job is to establish that initial marker so the target disk becomes a valid ZoiOS environment.
2026 © All Rights Reserved.
- All the content is available under CC BY-SA 4.0, expect where otherwise stated.
- Source code is available on GitLab, licensed under Apache 2.0.
Last updated on
