DALI is an opinionated, single-binary installer for Arch Linux. Where
archinstall exposes every option,
DALI commits to one specific configuration: download one static binary, run it
from the live ISO, answer a handful of questions, reboot into a working system.
⚠️ DALI erases the target disk. It is meant to be run from the official Arch Linux live ISO on a machine you intend to install onto.
DALI installs exactly one well-trodden configuration:
| Area | Choice |
|---|---|
| Firmware | UEFI + GPT |
| Bootloader | systemd-boot |
| Filesystem | Btrfs with @, @home, @log, @pkg, @snapshots subvols |
| Compression | zstd, noatime |
| Kernel | linux |
| Network | NetworkManager |
| Swap | zram (zstd, sized to RAM, capped at 8 GiB) — optional |
| Admin | a sudo-enabled user in the wheel group; root locked by default |
The core choices are the disk, hostname, username + password, optional root password, locale, keymap, timezone, the zram toggle and any extra packages — plus a handful of optional knobs (SSH-key import, the provisioning/shell toggles, DNS and mirror country) covered in Configuration file. Everything else is fixed by the stack above — see What every install sets up.
DALI runs from an Arch-based live environment booted in UEFI mode, as
root — the Arch Linux live ISO or
SystemRescue. A real install refuses
to proceed otherwise, and also fails fast — before touching the disk — if the
network is unreachable, since pacstrap and provisioning need it. (On any other
Linux box you can still rehearse the whole plan with --dry-run, which changes
nothing — see Usage.)
Each GitHub release ships a static x86-64 binary. From the live ISO:
curl -fLO https://github.com/davlgd/dali/releases/latest/download/dali-linux-x86_64-musl
chmod +x dali-linux-x86_64-musl
./dali-linux-x86_64-muslVerify it against the published SHA256SUMS if you like. A glibc build
(dali-linux-x86_64-gnu) is also attached for environments that prefer it.
# Interactive: a single-screen TUI with sensible defaults pre-filled.
./dali
# See exactly what would happen, changing nothing.
./dali --dry-run --config myconfig.toml
# Fully automated, no prompts (scripted / repeatable installs).
./dali --config myconfig.toml --yesWhen a real install finishes, DALI reboots into the new system by default
(immediately with --yes, after a confirmation otherwise); pass --no-reboot
to stay on the live environment. Set default_apps = false for a bare bootable
system, or disable individual post-install steps through the [provision] and
[shell] config tables (see below).
| Flag | Effect |
|---|---|
--config <FILE> |
Install non-interactively from a TOML config (see below). |
--dry-run |
Print the exact plan of actions and exit without changes. |
--yes |
Skip the final "erase the disk" confirmation. Requires --config. |
--save-config <F> |
Write the effective config (from --config, or from the wizard if none) to a file and exit. Conflicts with --dry-run/--yes. |
--no-reboot |
Do not reboot at the end (default is to reboot into the new system). |
--completions <shell> prints a shell completion script and --man prints a
man page, both to stdout:
./dali --completions bash | sudo tee /usr/share/bash-completion/completions/dali
./dali --man | sudo tee /usr/share/man/man1/dali.1 > /dev/nullThe configuration is TOML. Every field except disk and user has a
sensible default and may be omitted. The smallest useful config
(examples/minimal.toml):
disk = "/dev/vda"
[user]
username = "arch"
password = "changeme"A fully specified config (examples/full.toml):
disk = "/dev/vda"
hostname = "dali-test"
timezone = "Europe/Paris"
locale = "en_US.UTF-8"
keymap = "fr"
root_password = ""
github_user = "davlgd"
# sshd password auth. Omit to auto-decide (off when github_user keys are
# imported, on otherwise); set explicitly to force it.
# ssh_password_auth = false
extra_packages = ["neovim"]
zram_swap = true
default_apps = true
# Resolvers used inside the chroot; defaults to Quad9 + Cloudflare, [] keeps
# whatever pacstrap copied.
dns_servers = ["9.9.9.9", "1.1.1.1"]
# Restrict reflector's mirror ranking to a country; empty ranks worldwide.
# mirror_country = "France"
# custom_commands run as your user at the end of provisioning:
# custom_commands = ["git config --global pull.rebase true"]
# Post-install provisioning (best-effort). Omit a key to keep it on; set
# enabled = false to skip provisioning entirely.
[provision]
enabled = true
v = true # build the V compiler from source
tools = true # mise + its global tool set + Claude Code (AI/dev CLIs)
[shell]
aliases = true # write the DALI alias/helper block into ~/.bashrc
# Tables come after the bare keys (TOML forbids bare keys after a table); the
# [user] table is kept last.
[user]
username = "david"
password = "changeme"- An empty
root_passwordlocks the root account; administration then happens exclusively through the sudo-enabled user. - A config file may contain plaintext passwords — treat it as a secret.
--save-config foo.tomlkeeps them out of the shareable file: it writesfoo.tomlwithout passwords and a siblingfoo.credentials.toml(mode0600) holding only the secrets.--config foo.tomllater merges the sidecar back in automatically; a single file with inline passwords also works.
The interactive TUI gathers the disk, hostname, identity, root state,
locale/keymap/timezone, the mirror country, the zram and Dev/AI tools
toggles, extra packages and the optional GitHub user, re-asking each password
for confirmation. Locale, keymap, timezone and the mirror country are picked
from a filterable list of what the system actually supports (press Enter on
the field, type to filter, arrow-select) — no need to remember exact
identifiers. The remaining knobs — [shell].aliases, the finer
provision.v/provision.tools sub-toggles, dns_servers and
ssh_password_auth — are configuration-file only.
- ESP: FAT32, 1 GiB, mounted at
/boot. - Base packages:
base,base-devel,btrfs-progs,curl,git,linux,linux-firmware,networkmanager,snap-pac,snapper,sudo,vim— plus the matching CPU microcode (intel-ucode/amd-ucode),zram-generatorwhen zram is on, and any extras you list. - Default app set (
default_apps, on by default):atuin,avahi,bash-completion,bash-preexec,bat,docker,docker-buildx,fastfetch,ffmpeg,glab,htop,impala,jless,jq,lazydocker,lazygit,less,minio-client,nano,openssh,ufw,uv,whois,yt-dlp,zellij— withdocker.service,avahi-daemon.serviceandsshd.serviceenabled and the user added to thedockergroup. - Base services:
NetworkManager,systemd-timesyncd,systemd-boot-update,fstrim.timer.
snapperis configured for the root subvolume only (on the@snapshotssubvol), andsnap-pactakes automatic pre/post snapshots around everypacmantransaction — so a bad upgrade can be rolled back./homeis deliberately not snapshotted.
- Shell environment: the user's
~/.bashrcgets~/.local/binonPATH,miseandatuinactivation (the latter viabash-preexec), helper functions (check,clean_cargo,f,mkcd,up,w) and aliases (add/list/remove/searchfor pacman,gac/gl/gst/gsw/…,dps,myip,pgen).upupdates the system, mise tools, global bun/uv packages and the V compiler in one go. The block is marker-delimited, so re-running replaces it in place (with a one-time~/.bashrc.dali.bakbackup). Skip it with[shell] aliases = false— thePATHentry is kept regardless. - Provisioning (
[provision], on by default, best-effort):provision.vbuilds the V compiler into~/.local/bin, andprovision.toolsruns themiseand Claude Code installers and installsbun,codex,gemini,node,opencodeandpiglobally viamise. Anycustom_commandsyou list run as your user at the end of this step. Setprovision.enabled = falseto skip it all. Network-bound; failures are warnings, never aborting the (already bootable) install. - SSH keys (optional,
github_user): that GitHub account's public keys (https://github.com/<user>.keys) are imported into~/.ssh/authorized_keys.
- Mirrors:
reflectorranks the mirrorlist by speed beforepacstrap— so both the install and the installed system pull from fast mirrors. The ranking is worldwide by default, or restricted tomirror_country(a country name or code) when you set it. - pacman tuning:
Color,ParallelDownloads = 5andVerbosePkgLists, applied to the live system (faster install) and the target. - Network carry-over: the live environment's connections are carried into
the target (mode
0600) so a Wi-Fi install reconnects after reboot — NM profiles as-is, and iwd profiles (the ISO's Wi-Fi backend) converted to NetworkManager keyfiles so they work under NM's default backend. - Wireless regdom: the regulatory domain is derived from the timezone's
country (e.g.
Europe/Paris→FR), so Wi-Fi uses the right channels/power. - GnuPG keyservers:
/etc/gnupg/dirmngr.confgets several keyservers and a short timeout, sopacman-keydoesn't hang on a dead server.
- System tuning:
fs.inotify.max_user_watches = 524288, a systemdDefaultLimitNOFILE = 65536:524288bump, andnet.ipv4.tcp_mtu_probing = 1. - Hardening (with the app set): an sshd drop-in disables root SSH and caps
auth tries; password authentication follows
ssh_password_auth(when unset, it is disabled if SSH keys were imported and kept otherwise, so a keyless box is never locked out).ufwis configured on first boot (deny incoming, allow outgoing, keep SSH reachable). - Login banner: a NetworkManager dispatcher keeps
/etc/issueshowing the machine's LAN IPv4 at the console login prompt — so you can see where to SSH in (the egress address, never thedocker0bridge). - Provenance:
/etc/dali-releaserecords that DALI provisioned the system (and which version), and/etc/os-releasegains additiveDALI_*fields while keepingID=arch/PRETTY_NAME="Arch Linux"— it stays Arch. - Install log: the full transcript is written to
/var/log/dali-install.logand a per-step completion map to/var/log/dali-steps.toml, for diagnosis.
Omitted config fields default to hostname=arch, timezone=UTC,
locale=en_US.UTF-8, keymap=us, zram_swap=true, default_apps=true,
provisioning and shell aliases on ([provision]/[shell] all true),
dns_servers = Quad9 + Cloudflare, a worldwide mirror ranking, and a locked
root (empty root_password).
DALI targets the Rust 2024 edition (minimum supported Rust: 1.85).
cargo build --release # dynamic glibc build
rustup target add x86_64-unknown-linux-musl # for a portable, fully
cargo build --release --target x86_64-unknown-linux-musl # static binary"Fully static" refers only to dali's own linkage: at runtime it still drives
the Arch live ISO toolchain (sgdisk, mkfs.btrfs, pacstrap, bootctl,
genfstab, …) as subprocesses, so it is meant to run from that environment.
The quality gate, architecture, and how to add an install step are in
CONTRIBUTING.md. DALI is tested at three levels — unit, a
process-level dry-run integration test, and a real bootable QEMU/KVM install —
documented in docs/TESTING.md.
The quickest check, on any Linux box (no Arch, root or hardware needed, changes nothing):
cargo run -- --dry-run --config examples/minimal.tomlApache-2.0 — see LICENSE. Copyright 2026 davlgd.