💿 Install Workstation
This is the clean-hardware installation runbook for the workstation.
The flow is destructive: the selected disk is repartitioned and formatted. The installer asks for the target disk and requires an exact disk-path confirmation before it applies the storage layout.
⚠️ Before Booting the Installer
Prepare the machine before starting this guide:
- Back up important data from the target disk.
- Download the official Minimal NixOS ISO.
- Prepare a bootable USB drive. Ventoy is recommended for this.
- Leave Secure Boot disabled or in setup/custom mode for the first install. Enable it only after the installed system boots and
sbctlkeys are enrolled. - Make sure the machine can reach the network from the live ISO.
- Identify which physical disk will be erased.
This guide assumes those preparation steps are done by the user. The detailed procedure starts from the booted NixOS live environment.
🌐 Boot and Network
Boot the official Minimal NixOS ISO in UEFI mode.
If the machine needs Wi-Fi, connect with NetworkManager:
nmcli device wifi connect SSID_NAME hidden yes --askVerify network access:
ping -c 3 nixos.orgExpected result:
- DNS resolves
nixos.org. - The command receives replies.
⚡ Start the Installer
Run the installer from the live environment:
curl -sL https://github.com/arttet/nixos-config/raw/main/install.sh | sudo bashThe command runs the installer with root privileges. If the live console renders colors poorly, use the same command with colors disabled:
curl -sL https://github.com/arttet/nixos-config/raw/main/install.sh | sudo NO_COLOR=1 bashThe entrypoint clones or updates the repository under /root/.cache/nixos-config-installer/repo, then starts the Nushell installer:
nu scripts/install/bootstrap.nu --applyThe entrypoint provides nushell, mkpasswd, and gum through a temporary nix shell. gum is used only for the interactive TUI. Tests and plain logs can disable it with:
NIX_CONFIG_INSTALL_PLAIN_UI=1 NO_COLOR=1 nu scripts/install/bootstrap.nu --dry-runThe installer defaults to the main branch. The command below pins that branch explicitly. To test a development branch from the live ISO, replace both occurrences of main with the branch name:
curl -sL https://github.com/arttet/nixos-config/raw/main/install.sh | sudo BRANCH=main bashNIX_CONFIG_INSTALL_REPO_BRANCH is also supported as a longer compatibility alias.
💬 Interactive Setup Wizard
The wizard collects the local account, machine identity, timezone, and target disk. No disk is formatted until the final exact disk-path confirmation.
The wizard asks for:
| Prompt | Meaning |
|---|---|
| Session | Local name for this installer run; files are stored under $HOME/.cache/nixos-config-installer/state/<session>/ |
| Profile | System profile to install; keep default for the normal workstation |
| User | Human-readable account description, such as User or Default User |
| Username | Initial local Linux username, lowercase only, such as user; User is invalid |
| User password | Initial login password for that user; input is hidden and repeated once |
| Hostname | Machine hostname |
| Timezone | NixOS timezone, such as Etc/UTC |
| Disk | Target disk device to repartition and format |
| Action | dry-run or apply |
When apply starts, the installer also asks for the LUKS passphrase that will protect the encrypted root container. This passphrase is separate from the local user login password.
Keep the profile as default for a normal workstation install. The flake default points to the graphical workstation. Do not switch to another profile unless you are deliberately testing a development workflow.
Disk discovery prints available disks from lsblk and matching /dev/disk/by-id/ entries when available. Review the disk carefully. The installer will ask you to type the exact selected disk path before formatting.
⚙️ What Apply Does
When apply is selected and disk confirmation succeeds, the installer:
- Runs pre-flight checks for UEFI mode, required commands, free
/mnt, and network access tocache.nixos.org. - Generates a temporary disko config under
/run/nixos-config-installer/<session>/runtime/. - Runs
diskoto partition and format the disk. - Runs
nixos-generate-config --root /mnt. - Writes the local user overlay to
/mnt/root/.nix-config-local/user.nix. - Writes the local password hash file to
/mnt/root/.nix-config-local/user.passwd. - Installs the default workstation target with
nixos-install --impure --no-root-passwd.
Before each external command, the installer prints the exact command and asks for y or yes approval. It then checks the command exit code before continuing:
| Command | Comment |
|---|---|
disko | Runs after disk confirmation with a temporary LUKS passwordFile generated by the installer. |
nixos-generate-config --root /mnt | Runs only after disko exits successfully. |
nixos-install --impure --no-root-passwd | Runs only after hardware configuration and local installer state are in place. |
The temporary LUKS password file and generated user password hash are kept under /run/nixos-config-installer/<session>/secrets/ and removed during the apply flow. The installed system still receives the hashed user password at /mnt/root/.nix-config-local/user.passwd, because the generated local overlay references /root/.nix-config-local/user.passwd for future rebuilds.
The installer writes non-secret command and exit-code diagnostics to:
/run/nixos-config-installer/<session>/runtime/install.logThe disko and nixos-generate-config stdout/stderr streams are copied into that log after each command completes. nixos-install keeps live output on the console and logs command start plus exit code.
The generated local overlay contains the hostname, timezone, initial user, user shell, wheel membership, and a reference to the password hash file. The password hash itself is stored separately in user.passwd, not inside the Nix overlay.
Both target files are local machine state and must not be committed to the repository.
Unprivileged Wireshark packet capture needs the account to be in the wireshark group. Add it to users[].extraGroups in the local overlay (state.json) — it is not granted automatically the way wheel is.
After the installer completes, reboot:
reboot✅ After Installation
After booting the installed workstation, log in as the local user created by the installer.
🧭 First Boot Checks
Run each check separately from the installed system. The default login shell is Nushell, so the shell-specific checks below use Nushell syntax.
| Check | Command | Expected result |
|---|---|---|
| UEFI boot | See the Nushell command below | Output is true |
| Mounted filesystems | findmnt | Installed filesystems are visible |
| Swap | swapon --show | Swap is available |
| Network | systemctl status NetworkManager | NetworkManager is active |
| Login manager | systemctl status display-manager | SDDM is active |
| Session type | See the Nushell command below | Output is wayland |
| Hyprland version | hyprctl version | Hyprland responds |
| Displays | hyprctl monitors | Connected monitors are listed |
| Desktop portal | systemctl --user status xdg-desktop-portal-hyprland | User portal is available |
| Audio | wpctl status | PipeWire/WirePlumber devices are listed |
| Privilege escalation | doas true | doas prompts and exits successfully |
Nushell commands for the shell-specific checks:
"/sys/firmware/efi" | path exists
$env.XDG_SESSION_TYPE? | default ""🔑 Prepare Git Access
The installed system does not contain a working copy of this repository. Before running flake rebuilds, export the resident SSH keys from the YubiKey and make sure GitHub knows the public key.
Insert the YubiKey, then run:
^mkdir -p ~/.ssh
ssh-keygen -K
chmod 700 ~/.ssh
glob "~/.ssh/id_*" | where {|path| not ($path | str ends-with ".pub") } | each {|path| chmod 600 $path }
glob "~/.ssh/*.pub" | each {|path| chmod 644 $path }If the clone step below fails with a permission error, add the exported public SSH key from ~/.ssh/*.pub to GitHub and try again.
📦 Clone the Repository
Clone the repository into the local home directory:
git clone git@github.com:arttet/nixos-config.git ~/nixos-config
cd ~/nixos-config
just setup🔄 Rebuild from the Repository
Build and test the graphical workstation target before switching:
just build
just test
just switchThe default profile points to the graphical workstation. To build or test a specific profile explicitly, pass it as an argument:
just build workstation-gui
just test workstation-guiThe rebuild uses the local repository and creates a new bootable generation.
🔐 Enable Secure Boot
Secure Boot is configured through sbctl, while the system intentionally keeps GRUB for future custom boot UX work. Enable firmware Secure Boot only after the installed system boots, Git access works, and a rebuild from the local repository succeeds.
Prepare the firmware for key enrollment first. In many firmware menus this means switching Secure Boot to setup/custom mode or clearing the existing Secure Boot keys. Keep a known-good firmware recovery path before changing keys.
Create and enroll the local Secure Boot keys:
doas sbctl status
doas sbctl create-keys
doas sbctl enroll-keys -mThe -m flag keeps Microsoft certificates enrolled alongside the local keys, which is useful for common firmware, GPU option ROMs, and removable boot media.
Rebuild once more from the repository. The GRUB install hook signs EFI artifacts under /boot/efi automatically when sbctl keys exist:
just switch
doas sbctl verifyIf sbctl verify reports unsigned EFI files that belong to the NixOS boot path, do not enable Secure Boot yet. Rebuild again and inspect the reported paths.
After verification succeeds, enable Secure Boot in firmware and reboot.
Advanced GRUB locking is a separate hardening step. GRUB can also be configured to require a password for unsafe edits and to verify detached signatures for files loaded from /boot, but that workflow needs local GRUB password and signing-key material and is not required for the baseline Secure Boot setup.
🧪 Runtime Validation
After rebooting with Secure Boot enabled in firmware, run:
doas sbctl status
doas sbctl verify
bootctl statusExpected result:
- Secure Boot is enabled.
- EFI boot artifacts are signed.
sbctl verifyreports no unsigned EFI artifacts relevant to the NixOS boot path.- GRUB still boots the current NixOS generation normally.