The guard exists so a stranger's install is never pointed at the maintainer's forge — a property of what an install executes and ships (bootstrap.sh, firstboot.sh, configs/, iso/), not of documentation. Grep only those paths, so README/docs may link to the canonical forge (fixes the red master from the build-status badge).
arch-turnkey
One-command, reproducible Arch Linux installs for a family of machines (laptop / desktop / handheld). LUKS-encrypted Btrfs with snapshots, hardware auto-detection, and a two-phase flow built entirely on mature tools — archinstall for the base, yadm for dotfiles, snapper for snapshots.
Personal config and all secrets live in a separate private repo (yadm) and in Vaultwarden. This repo is the installer only — no keys, tokens, or passwords.
Quick start
Boot the current Arch ISO, connect to the network, then run one line:
curl -fsSL https://raw.githubusercontent.com/sudolulo/arch-turnkey/master/bootstrap.sh | bash
That preflight-checks the machine, installs archinstall, pulls the config, and
launches the guided installer. In its menu:
- Hostname — name the machine
- Disk → best-effort → btrfs with subvolumes and LUKS encryption
- User — create your account
Install and reboot — first-boot setup runs automatically on the first boot
(installed + enabled by the config). If it's ever skipped or fails, log in and run
firstboot to retry.
How it works
Two phases, each a real tool — the only bespoke code is a ~120-line launcher.
| Phase | Tool | What it does |
|---|---|---|
| 1 · Base | archinstall | LUKS-encrypted Btrfs root, base packages, user. Its menu is the GUI. |
| 2 · Configure | firstboot → yadm bootstrap | machine class, packages, services, dotfiles, snapshots |
firstboot:
- detects the machine class from the chassis (laptop/desktop/handheld) — override at the prompt
- unlocks Vaultwarden once (via
rbw) to fetch a Gitea token + the dotfiles archive passphrase, then auto-registers this machine's SSH key with Gitea (no manual paste) — falls back to a manual paste if Vaultwarden is unavailable - clones the dotfiles, decrypts secrets, and runs
yadm bootstrap
yadm bootstrap then, per machine:
- installs
common+class-<class>+ per-host package lists - auto-detects the GPU (Intel/AMD/NVIDIA) and installs the right drivers
- enables services (backups, firewall, smartd, zram, fstrim, time-sync, mDNS, firmware updates); power management only where a battery exists
- on Btrfs, configures snapper + snap-pac for automatic pre/post-pacman snapshots
Because the config lives in git, nothing goes stale — a machine built next year gets your current setup.
Machine classes
firstboot sets yadm config local.class <class>, which drives package tiers,
##class.<class> yadm alt-files, and conditional services. GPU and battery are
auto-detected, so the same flow works unchanged on any machine.
Zero-touch ISO (optional)
A thin custom Arch ISO auto-runs the bootstrap on boot — flash a USB, boot, walk
away. Thin means it pulls the current bootstrap.sh from git at boot, so the ISO
never goes stale.
Build it yourself (~10 min):
sudo pacman -S archiso
sudo iso/build.sh # ISO lands in iso/out/
No prebuilt ISO is published here — an Arch ISO is ~1 GB and goes stale the moment upstream moves, so building it locally against the current archiso is both smaller and more correct than downloading a months-old image. docs/CI.md shows how to automate the build if you want it on a schedule.
To make the ISO provision your machine unattended — dotfiles, secrets, packages — drop a
site.env at iso/site.env before building; build.sh bakes it in. See
docs/SITE.md.
Layout
bootstrap.sh # ISO entrypoint (the one-liner runs this)
firstboot.sh # Phase 2 launcher (installed to the target)
configs/archinstall.json # archinstall base config
iso/ # archiso profile + build script (zero-touch ISO)
docs/ # detailed install guide + nginx redirect snippet
Making it yours
Nothing installation-specific is committed here. The install runs in two phases:
- Base — archinstall: LUKS + btrfs subvolumes + snapper + yay. Generic; works from a fresh clone with no configuration at all.
- Personal — unlock a vault, register the new machine's SSH key with your forge, clone
your dotfiles, run
yadm bootstrap. This needs your infrastructure, so it reads a gitignoredsite.envthat you supply. Without one, firstboot says what it's skipping and leaves you a complete, clean Arch system.
Copy configs/site.env.example to site.env, fill it in, and either bake it into a custom
ISO (iso/build.sh) for a near-unattended install or drop it at /root/site.env on the live
ISO. Set ARCH_TURNKEY_BASE if you want the install to fetch from your own forge rather than
this repo's GitHub mirror. Full details: docs/SITE.md.
Config integrity
bootstrap.sh verifies the fetched archinstall.json before running it — its
custom-commands execute as root during install, so a poisoned config from a compromised
mirror would be root RCE. By default it checks the config against a SHA-256 pinned in the
script (kept in lockstep with the file). Two knobs:
ARCH_TURNKEY_CONFIG_SHA256=<sha>— verify against your own hash (needed if you pointARCH_TURNKEY_BASEat a fork with a different config);=skipopts out.ARCH_TURNKEY_PUBKEY=/path/to/pubkey— require and verify a detached GPG signature (configs/archinstall.json.sig) instead. This is the only option that resists a fully compromised upstream, since the key is yours and supplied out of band (bake it into a custom ISO). Sign a config withgpg --detach-sign configs/archinstall.json.
Requirements
- UEFI machine, current Arch ISO
- For the personal phase only (all optional — omit and you get the base install):
- A yadm dotfiles repo with a
~/.config/pkglist/(common + class-* + per-host) - A Vaultwarden/Bitwarden item
arch-install(password = a forge token withwrite:public_key; custom fieldarchive-pass= dotfiles archive passphrase) to collapse all secret entry behind one master-password prompt
- A yadm dotfiles repo with a
Support
If arch-turnkey is useful to you, consider supporting development via GitHub Sponsors or Ko-fi.