Zillowe FoundationZillowe Documentation

system.lua Reference

Comprehensive guide to ZoiOS system-wide declarative configuration.

Edit this page on GitLab

The /etc/zoi/system.lua file is the primary configuration file for ZoiOS. It allows administrators to define the entire state of the operating system, from kernel parameters to user accounts as code.

Core Functions

system(table)

Defines global system metadata and high-level configuration.

FieldTypeDescription
hostnamestringThe network hostname for the machine.
timezonestringThe system timezone (e.g. "UTC", "America/New_York").
localestringThe primary system locale (e.g. "en_US.UTF-8").
kernel_paramsstringAppended to the kernel command line in the bootloader.
desktopstringThe desired desktop environment (e.g. "kde", "gnome", "sway").
system({
    hostname = "parlex-station",
    timezone = "America/Los_Angeles",
    locale = "en_US.UTF-8",
    kernel_params = "quiet loglevel=3 mitigations=off",
})

bootloader(table)

Configures the system bootloader.

FieldTypeDescription
typestringRequired. One of "systemd-boot", "grub2", or "limine".
efi_dirstringPath to the EFI partition mount point (defaults to "/boot/efi").
timeoutnumberBoot menu timeout in seconds.
bootloader({
    type = "systemd-boot",
    timeout = 5,
})

Leaving type unset lets Zoi detect the bootloader from the filesystem, probing systemd-boot first, then GRUB 2, then Limine.

efi_dir is only consulted for limine, whose entries use ESP-relative paths. systemd-boot writes to /boot/loader/entries and probes the ESP mount point (/boot, /boot/efi, /efi) itself, because the entries have to land on the filesystem the firmware actually reads.

One boot entry is written per kernel, including kernels preserved as fallbacks by kernel_retention, so several remain selectable after an update.


packages(table)

Defines the base set of packages installed in the system scope. You can provide a simple list of names or a table for advanced pinning and option selection.

packages({
    -- Simple list
    "@core/linux",
    "@core/systemd",

    -- Pinning to a specific version or range
    ["@main/neovim"] = { version = "^0.10" },

    -- Pinning to a channel
    ["@core/zoi"] = { version = "@stable" },

    -- Advanced pinning and options
    ["@core/base"] = {
        options = {
            "zoi:@core/systemd",
            "zoi:@core/linux",
            "zoi:@core/sudo"
        }
    }
})

users(table)

Manages system user accounts.

FieldTypeDescription
password_hashstringA one-way hash generated by zoi system secret hash.
groupslistSupplementary groups for the user.
shellstringPath to the login shell (e.g. "/usr/bin/zsh").
homestringCustom path for the home directory.
users({
    zaid = {
        password_hash = "$argon2id$v=19$m=65536,t=3,p=4$...",
        groups = { "wheel", "video", "docker" },
        shell = "/usr/bin/bash",
    }
})

Accounts are written directly to /etc/passwd, /etc/shadow, /etc/group and /etc/gshadow. The behaviour worth knowing:

  • Idempotent. Re-applying an unchanged system.lua performs no writes, so zoi system apply is safe to run on a schedule.
  • Deterministic. Users are processed in sorted order, so two machines applying the same configuration end up with identical UIDs.
  • Existing UIDs are never reassigned. Changing a UID under a populated home directory would orphan every file in it.
  • Home directories are created from /etc/skel with mode 0700.
  • An account with no password_hash is locked, not passwordless. It cannot be logged into with a password, but root su and SSH key login still work.
  • ZOISEC: values are decrypted automatically, so an encrypted secret works directly as password_hash.
  • A missing group reference warns rather than failing, so one typo does not abort an apply.

groups(table)

Manages system groups.

FieldTypeDescription
gidnumberExplicit Group ID (optional).
groups({
    docker = { gid = 999 }
})

Groups are reconciled before users, because a user's supplementary memberships have to resolve to real GIDs.

Accounts the base packages already ship (root, nobody, and so on) are left alone. system.lua is the source of truth for whether a group exists, not for every attribute of it, because the packages that own those groups have deliberate GIDs and memberships that a declarative file would otherwise fight on every apply.


services(table)

Controls system-wide background services.

FieldTypeDescription
enablebooleanWhether to enable and start the service via systemctl.
services({
    NetworkManager = { enable = true },
    sshd = { enable = true },
    docker = { enable = true },
})

filesystems(list)

Defines the disk layout and mount points for /etc/fstab.

ZoiOS does not automatically partition or format your drives. You must manually partition and format your target disk before installation. The filesystems block is used exclusively to generate a valid /etc/fstab for the target system.

FieldTypeDescription
devicestringDevice path or Label (e.g. "/dev/sda1", "LABEL=root").
mountstringThe target mount point (e.g. "/", "/home").
typestringFilesystem type (e.g. "btrfs", "ext4", "vfat").
optionsstringMount options (e.g. "subvol=@root,compress=zstd:3").
filesystems({
    {
        device = "LABEL=zoios_root",
        mount = "/",
        type = "btrfs",
        options = "subvol=@root,compress=zstd:3,ssd",
    },
    {
        device = "LABEL=zoios_root",
        mount = "/home",
        type = "btrfs",
        options = "subvol=@home,compress=zstd:3",
    }
})

Applying Changes

To apply your changes to the live system:

sudo zoi system apply

Zoi will resolve the packages, install them to the store, create a new System Generation, update the FHS view, reconcile the declared users and groups, regenerate /etc/fstab, enable or disable services, run the kernel pipeline, and write bootloader entries.

The apply is idempotent: running it twice with no change in between performs no writes, which is what makes it safe to schedule.


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