Zillowe FoundationZillowe Documentation

Building a ZoiOS Distro

A guide on how to bootstrap and build a ZoiOS distribution (like Parlex Linux) from scratch using Zoi.

Edit this page on GitLab

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

  1. A Host Linux System: You need a running Linux environment (like an Ubuntu Live USB or a Fedora host) to perform the initial installation.
  2. Zoi CLI Installed: The host system must have the Zoi CLI installed.
  3. Root Privileges: Formatting disks and installing bootloaders requires sudo.
  4. 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/sdX

Create two partitions in cfdisk:

  1. EFI System Partition - 512MiB, type EFI System
  2. 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/sdX2

Filesystem 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/efi

Preview 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-run

What happens under the hood?

In this single step, Zoi:

  1. Pre-flight: Validates that every package in system.lua exists in your registry, before touching the disk.
  2. Initializes Sysroot: Drops the /etc/os-release marker (ID=zoios) so the disk is recognized as ZoiOS.
  3. Bootstrap Installation: Directly installs the entire base package set into the target Zoi store and then into the system root.
  4. Configures Accounts: Reconciles the declared users and groups against the target's /etc/passwd, /etc/shadow, /etc/group and /etc/gshadow.
  5. Generates fstab: Writes /etc/fstab from the declared filesystems.
  6. 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.img

2. 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.cfg

3. 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 /mnt

Isolated 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 reboot

Creating 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/sdX2

3. 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.lua

4. 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.cfg

5. Cleanup and Boot

sudo umount -R /mnt/zoi-usb
sync

How 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.


A software organization

2026 © All Rights Reserved.

  • All the content is available under CC BY-SA 4.0, expect where otherwise stated.

Last updated on