Files
2026-08-04 02:45:25 +00:00

6.3 KiB

arch-turnkey

Build

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 firstbootyadm 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:

  1. Base — archinstall: LUKS + btrfs subvolumes + snapper + yay. Generic; works from a fresh clone with no configuration at all.
  2. 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 gitignored site.env that 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 point ARCH_TURNKEY_BASE at a fork with a different config); =skip opts 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 with gpg --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 with write:public_key; custom field archive-pass = dotfiles archive passphrase) to collapse all secret entry behind one master-password prompt

Support

If arch-turnkey is useful to you, consider supporting development via GitHub Sponsors or Ko-fi.