A GNOME desktop on Btrfs with systemd-boot, zram swap and a firewall took 7 minutes and 2 seconds on the test box, measured by archinstall itself from the install confirmation to the “Installation completed” screen, and mirror ranking alone consumed about two of those minutes. The guided installer on the current Arch ISO ships unified kernel images by default, a firewall picker, Snapper and Timeshift integration, a split firmware menu, and an encrypted credentials file for unattended installs.
This archinstall guide walks every menu in the order the installer presents it, with the choice that was made on the test box and the reason for it. It then repeats the same Arch Linux install from a saved config file with --silent, which is where one real bug surfaced: a Snapper selection stored in the config file is silently ignored. The workaround is included and was verified on the installed system. For a fully manual Arch Linux install guide with LVM, the manual LVM on UEFI walkthrough covers the pacstrap route.
Tested October 2026 on archinstall 4.5 (Arch ISO 2026.10.01).
What the current archinstall menus default to
Several defaults differ from what older archinstall guides describe, including the earlier version of this one, which recommended GRUB. The table lists what the current ISO presents, taken from the screens captured during the test runs.
| Area | Current ISO | Practical effect |
|---|---|---|
| Default bootloader | systemd-boot, unified kernel images enabled | One EFI binary per kernel, no loader entry files |
| Bootloader choices | systemd-boot, GRUB, EFISTUB, Limine, rEFInd | GRUB still needed for BIOS or grub-btrfs |
| Btrfs snapshots | Snapper or Timeshift submenu | Appears only when subvolumes are created |
| Firewall | ufw or firewalld under Applications | Inbound traffic denied from first boot |
| Firmware | Separate menu for six split firmware packages | None installed unless picked |
| base-devel | Not installed | Add it before planning AUR builds |
| NVIDIA option | nvidia-open-dkms | Turing and newer GPUs only |
| Desktop profiles | 21, including Cosmic, niri and Labwc | Package list previewed per profile |
| Credentials file | Optional argon2id encryption with a key | Safe to store next to the config |
Prerequisites
- A UEFI machine, because the default layout and systemd-boot assume UEFI; legacy BIOS installs need GRUB or Limine.
- 4 GB of RAM for a GNOME install, since the installed desktop used 1.38 GiB at idle on the test box.
- At least 20 GB of disk, because the finished GNOME system occupied 4.4 GB on a 32 GB Btrfs volume with zstd compression.
- A USB stick of 2 GB or larger and a wired connection, or Wi-Fi that
iwctlcan join. - Lab used here: a Proxmox VM (OVMF firmware, 4 vCPU, 4 GB RAM, 32 GB VirtIO SCSI disk) named
bench01.
Step 1: Write the Arch ISO to a USB drive
Download the current ISO from the Arch Linux download page. Every mirror also publishes the release under iso/latest/ with the stable name archlinux-x86_64.iso, which keeps the command below valid month to month. Identify the USB device with lsblk first, because dd overwrites whatever device it is given:
sudo dd if=archlinux-x86_64.iso of=/dev/sdX bs=4M status=progress oflag=sync
On Windows, Rufus in DD mode or Ventoy produce a bootable stick from the same file.
Step 2: Boot the live environment and check UEFI and the network
Boot from the stick and wait for the root shell, then confirm the boot mode: a 64-bit UEFI boot exposes the firmware platform size, and the value must read 64 before continuing with the default layout:
cat /sys/firmware/efi/fw_platform_size
If that file does not exist, the machine booted in legacy BIOS mode. Fix the boot order in the firmware setup and boot the stick again. Wired networks come up through DHCP on their own; Wi-Fi needs the wireless device name first, because it is not always wlan0, and then one iwctl call with the SSID of the access point:
iwctl device list
iwctl station wlan0 connect "HomeNetwork"
Confirm that the package mirrors are reachable before starting the installer:
ping -c 3 archlinux.org
Step 3: Launch archinstall
The installer is preinstalled on the ISO and checks for a newer release of itself on start:
archinstall
After “Fetching Arch Linux package database…” the main menu appears. The right pane previews the current value of whichever entry is highlighted, so every setting can be reviewed without opening it. Arrow keys move, Enter opens an entry, Space toggles items in multi-select lists, and / starts a filter.

Two keyboard habits avoid most of the mistakes made during the first test run. Many Yes/No prompts open with “Yes” highlighted, so a stray Down arrow lands on “No”. Pressing Enter in a multi-select list with nothing checked selects the highlighted row, which is how an unwanted firmware package was picked up, and Ctrl+c resets the current setting after a confirmation prompt.
Step 4: Set locales, mirrors and optional repositories
Archinstall language only changes the installer interface, not the installed system. Locales holds four values: keyboard layout (us), locale language (en_US.UTF-8), encoding (UTF-8) and console font (default8x16), and all four defaults were kept on the test box.
Mirrors and repositories splits into four submenus that control where packages come from: Select regions, Add custom servers, Optional repositories and Add custom repository. Selecting a single region (Germany for the test box) still pulled 103 mirror URLs into the config, which archinstall then ranks by download speed. That ranking took 1 minute 57 seconds of the unattended run, and several mirrors were skipped with “HTTP Error 403: Forbidden” during the test. The ranked list put a plain HTTP mirror first, which is acceptable because pacman verifies package signatures regardless of transport. Optional repositories offers multilib, multilib-testing, core-testing and extra-testing. Enable multilib for Steam, Wine or any 32-bit library; leave the testing repositories off on a workstation.
Step 5: Configure the disk layout, Btrfs subvolumes and snapshots
Disk configuration wipes the selected drive, so confirm the device name twice. Partitioning offers three modes: “Use a best-effort default partition layout”, “Manual Partitioning” and “Pre-mounted configuration”. The default layout on a 32 GB disk produced a 1 GiB FAT32 /boot (the EFI system partition) and a 31 GiB root partition. The filesystem choices are btrfs, ext4, xfs and f2fs, and only btrfs unlocks the subvolume and snapshot options below.
Choosing btrfs triggers two follow-up prompts, and the first asks whether to create subvolumes with a default structure, and the answer should be Yes. The first test run answered No by accident: the result was a single top-level volume mounted as subvol=/, with no @home and no way to add snapshot support from the menu. The second prompt picks compression or disabled copy-on-write; compression adds compress=zstd to the mount options. With both answered, the layout preview shows the four subvolumes:

Separate @log and @pkg subvolumes keep logs and the pacman package cache out of root snapshots, so a rollback does not resurrect old logs or bloat snapshots with cached packages. The installed system mounted all four with compress=zstd:3,ssd,discard=async,space_cache=v2.
Once subvolumes exist, a “Btrfs snapshots” entry appears in the disk submenu with Snapper and Timeshift as choices, and Snapper was selected for the test:

The same submenu holds LVM (it creates a volume group named ArchinstallVg) and Disk encryption, which offers LUKS only. Encryption prompts for a passphrase at every boot, so it suits laptops more than headless boxes. The LUKS encryption install guide covers the manual equivalent with more control over the cipher and key slots.
Step 6: Choose swap, bootloader, kernels and firmware
Swap is zram only, with no swap partition or swap file option. The algorithm list offers zstd, lzo-rle, lzo, lz4 and lz4hc; zstd was kept. On the 4 GB test box this produced a 1.9 GiB /dev/zram0 at priority 100, and archinstall added zswap.enabled=0 to the kernel command line so the two compressed-swap mechanisms do not stack.
The bootloader list now has five entries, and systemd-boot is the default and the right choice for a UEFI machine; GRUB remains the option for BIOS systems or for grub-btrfs snapshot boot entries.

Unified kernel images are enabled by default, which means the kernel, initramfs and command line are bundled into one EFI binary at /boot/EFI/Linux/arch-linux.efi, and systemd-boot discovers it without a loader entry file. The Plymouth submenu asks before enabling a boot splash and lists the two known failure cases:

Plymouth was left disabled on the test box. The kernel list covers linux, linux-hardened, linux-lts, linux-rt, linux-rt-lts and linux-zen. Installing linux-lts next to linux gives a second UKI to boot when a mainline update misbehaves, at the cost of one more image on /boot (the test UKI measured 30.5 MB).
Additional firmware is a new menu that exists because Arch split several vendor blobs out of linux-firmware, and the menu title states plainly that “none are pulled in by linux-firmware”:

Desktops and laptops rarely need any of these packages, while Mellanox Spectrum switches, QLogic and Marvell adapters, Netronome flow processors and Qualcomm SoC devices usually do. Nothing was selected on the test box.
Step 7: Set the hostname and create a sudo user
Hostname defaults to archlinux, and the test box used bench01 to match its role. Authentication holds three entries: Root password, User account and U2F login setup. The U2F entry reported “No U2F devices found” on the VM. Inside User account, add a user, set a password, and answer Yes to the superuser question:

An existing user can be promoted later from the user list through “Promote/Demote user”. With the root password left unset, the installed system shipped root locked (passwd -S root reported L), and archinstall wrote /etc/sudoers.d/00_klinsmann containing klinsmann ALL=(ALL) ALL. A locked root with one sudo user is a sensible default for a desktop.
Step 8: Pick the desktop profile, graphics driver and greeter
Profile types are Desktop, Minimal, Server and Xorg, and the Desktop list now carries 21 environments and window managers, and the right pane shows exactly which packages each one installs. GNOME pulls in gnome and gnome-tweaks:

The Desktop profile type also installs a small common set: htop, nano, openssh, smartmontools, vim, wget and xdg-utils. In the graphics driver menu that follows, the NVIDIA entry installs the open kernel module through DKMS, which supports Turing (GTX 16xx and RTX 20xx) and newer cards only:

Maxwell, Pascal and Volta cards are no longer covered by the official repositories; those need nvidia-580xx-dkms from the AUR after first boot (Kepler and older need the 470xx branch), which the Arch Linux post-install guide covers in its GPU section. “All open-source” installs Mesa plus the AMD, Intel and nouveau user-space drivers and is the safe choice for a VM or mixed hardware; it was used on the test box. The greeter list offers gdm, sddm, lightdm-gtk-greeter, lightdm-slick-greeter, ly, cosmic-greeter, dms-greeter and plasma-login-manager. GDM is preselected when the GNOME profile is chosen.
Step 9: Select applications, networking and extra packages
Applications covers Bluetooth, Audio, Print service, Firewall and Additional fonts. PipeWire was selected for audio, and the font list offered noto-fonts, noto-fonts-emoji, noto-fonts-cjk, ttf-liberation and ttf-dejavu (the first two were picked). The firewall choice is new and has two options:

Choosing ufw installs it, enables ufw.service and activates a default deny-incoming policy. That policy matters for anyone who adds sshd later: the service starts, but connections time out until port 22 is allowed. The troubleshooting entry near the end shows the exact symptom.
Network configuration lists five backends and prints its own recommendation at the top:

“Use Network Manager (default backend)” was selected because GNOME manages Wi-Fi and VPNs through NetworkManager. “Copy ISO network configuration to installation” is the highlighted default, and it suits a server that should keep the systemd-networkd setup the live ISO negotiated. The Pacman entry exposes only the Color toggle, although the saved config also carries parallel_downloads: 5.
Additional packages opens a searchable picker over the full repository and prints a warning that is easy to miss: “Note: base-devel is no longer installed by default. Add it here if you need build tools.” Without base-devel, makepkg fails on the first AUR build, so add it along with git if an AUR helper is planned. The test box added base-devel, fastfetch and git through the filter. Timezone was set to Europe/Berlin through the filter, and automatic time sync (NTP) stayed enabled.
Step 10: Save the configuration and start the install
Save configuration writes the choices to disk before anything is installed. It offers “Save user configuration (including disk layout)”, “Save user credentials” and “Save all”, then asks for a directory and whether to encrypt user_credentials.json. Choosing encryption prompts for a key and stores the user password hashes as an argon2id-protected blob. Save all, encrypt the credentials, and copy both files somewhere off the live ISO; Step 12 replays them.
Install shows the full JSON that is about to be applied. The prompt defaults to No, so an accidental Enter aborts instead of wiping the disk:

After Yes, a five second countdown precedes the disk wipe, and the installer then waits for time sync and for archlinux-keyring-wkd-sync, ranks mirrors, pacstraps the base system, installs the bootloader, profile and services, and generates fstab. The interactive run ended on this menu:

The chroot option opens a shell inside /mnt for last-minute changes, such as editing /etc/kernel/cmdline or adding a second user; otherwise choose Reboot system and remove the USB stick.
Step 11: First boot into the new Arch Linux system
The boot captures and measurements below come from the third test run (the silent install with Snapper, described in Step 12), which used the same menu choices as the interactive session plus working subvolumes. The loader timeout is 3 seconds, so the systemd-boot menu flashes past; pressing an arrow key during the countdown stops it. It lists one entry for the UKI plus the firmware setup shortcut; the default preset builds no fallback image:

The UKI shows the Arch splash bitmap while systemd starts, and graphical.target was reached 5.7 seconds after the kernel started:

GNOME opens the welcome tour on the first login, and Skip closes it for good:

GNOME Console is the default terminal (search for “Console”, not “Terminal”). fastfetch, added under Additional packages, summarises the result:

The boot time breakdown for the first boot comes from systemd-analyze:
systemd-analyze
Userspace dominated, and ldconfig.service (1.196 s) was the slowest unit on the first boot only:
Startup finished in 1.852s (kernel) + 3.836s (userspace) = 5.688s
graphical.target reached after 3.835s in userspace.
Check that no unit failed, that zram is active and how much space the install used:
systemctl --failed --no-legend; zramctl; df -h / /boot
No failed units were listed, zram reported its 1.9 GiB device, and the GNOME install used 4.4 GB of the root volume:
NAME ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram0 zstd 1.9G 4K 64B 20K [SWAP]
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 31G 4.4G 27G 15% /
/dev/sda1 1022M 46M 977M 5% /boot
From here, the things to do after installing Arch Linux cover mirrors, microcode, codecs and hardening. Package management day to day is in the pacman cheat sheet.
Step 12: Repeat the install unattended from an archinstall config file
The two files saved in Step 10 turn the whole menu session into one command. The flags that matter are --config and --creds (each also has a -url variant for files served over HTTP), --creds-decryption-key, --silent and --dry-run. With the files copied to /root/cfg on a fresh live ISO, start the unattended install:
archinstall --config /root/cfg/user_configuration.json --creds /root/cfg/user_credentials.json --silent
Passing --creds-decryption-key on the command line works, but it leaves the key in shell history. Omitting it makes archinstall prompt for the key even in silent mode, which is the better habit. A wrong key is rejected before the disk is touched:

Adding --dry-run to the same command parses the files, checks the bootloader against the disk layout, prints “Fetching Arch Linux package database…” and exits. The disk on the test box was unchanged afterwards. It catches broken JSON and a wrong key, but misspelled keys are ignored silently, so compare an edited file with diff against the saved one as shown below. The silent run printed the same progress as the interactive one and finished with:
Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers
Enabling service systemd-timesyncd
Updating /mnt/etc/fstab
Syncing the system...
Installation completed without any errors.
Log files temporarily available at /var/log/archinstall.
You may reboot when ready.
The saved config is plain JSON, so jq reads the decisions back. The archinstall documentation describes every key:
jq -c '{version, bootloader_config, network_config, swap, snapshot: .disk_config.btrfs_options.snapshot_config, subvolumes: [.disk_config.device_modifications[0].partitions[1].btrfs[].name]}' user_configuration.json | jq .
The file from the test run records systemd-boot with UKI, NetworkManager, zstd zram, Snapper and the four subvolumes:
{
"version": "4.5",
"bootloader_config": {
"bootloader": "Systemd-boot",
"removable": false,
"uki": true
},
"network_config": {
"type": "nm"
},
"swap": {
"algorithm": "zstd",
"enabled": true
},
"snapshot": {
"type": "Snapper"
},
"subvolumes": [
"@",
"@home",
"@log",
"@pkg"
]
}
Snapper selected in the config file, but not installed
The silent install from that file completed without errors, mounted all four subvolumes, and installed no Snapper at all: no snapper package, no /etc/snapper/configs, no timers. The cause is in the config loader, where subvolume names read from JSON are stored as strings, while the check that detects the default @ layout compares the name against a Path('@') object. The comparison is always false, so archinstall concludes the layout is not the default one, never parses btrfs_options, and skips the Snapper setup step. The same check exists on the upstream master branch at the time of testing. Going by the source, the interactive menu builds those objects as paths, so a Snapper choice made in the menus does not pass through this loader path; the test runs here covered the config-file path only.
The workaround uses custom_commands, which archinstall runs inside the target chroot after the profile and services are installed. Edit the saved file on the live ISO:
vim /root/cfg/user_configuration.json
Replace the empty custom_commands list and append snap-pac and snapper to packages. The diff between the saved file and the one used for the third test run shows the only two changes:
23c23,27
< "custom_commands": [],
---
> "custom_commands": [
> "snapper --no-dbus -c root create-config /",
> "snapper --no-dbus -c home create-config /home",
> "systemctl enable snapper-timeline.timer snapper-cleanup.timer"
> ],
248c252,254
< "git"
---
> "git",
> "snap-pac",
> "snapper"
These are the same three steps the Snapper routine inside archinstall performs. --no-dbus is required because the chroot has no running D-Bus. Running snapper list-configs through arch-chroot without the flag printed Failure (org.freedesktop.DBus.Error.ServiceUnknown) on the test box. The install log confirmed the commands ran:
[2026-10-02 13:47:22] - INFO - Installing packages: ['base-devel', 'fastfetch', 'git', 'snap-pac', 'snapper']
[2026-10-02 13:47:38] - INFO - Executing custom command "snapper --no-dbus -c root create-config /" ...
[2026-10-02 13:47:38] - INFO - Executing custom command "snapper --no-dbus -c home create-config /home" ...
[2026-10-02 13:47:38] - INFO - Executing custom command "systemctl enable snapper-timeline.timer snapper-cleanup.timer" ...
[2026-10-02 13:47:38] - INFO - Installation completed without any errors.
On the booted system, snap-pac wraps every pacman transaction in a pre and post snapshot. Install any package with pacman and then list the snapshots of the root config:
sudo pacman -S --noconfirm htop
sudo pacman -S --noconfirm tree
sudo snapper -c root list
Each of the two transactions produced a numbered pair (htop was already present, so pacman reinstalled it), with the pacman command line as the description:
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
--+--------+-------+--------------------------+------+---------+----------------------------+---------
0 | single | | | root | | current |
1 | pre | | Fri Oct 2 19:35:16 2026 | root | number | pacman -S --noconfirm htop |
2 | post | 1 | Fri Oct 2 19:35:17 2026 | root | number | htop |
3 | pre | | Fri Oct 2 19:35:19 2026 | root | number | pacman -S --noconfirm tree |
4 | post | 3 | Fri Oct 2 19:35:19 2026 | root | number | tree |
snapper places .snapshots as a nested subvolume inside @ rather than as a separate top-level @snapshots. Snapshot creation and the timeline and cleanup timers work with that layout, as the listing above shows. A full root rollback by booting a snapshot needs the flat layout described in the Arch Wiki Snapper page, or GRUB with grub-btrfs.
Error: “ssh: connect to host 10.0.1.50 port 22: Connection timed out” after installing
This is the ufw default policy, not a broken sshd: with the firewall chosen in Step 9 and sshd enabled later, the service listens but ufw drops the packets, so the client times out instead of getting “Connection refused”. Linux clients print “Connection timed out”; the macOS client used against the test box printed this:
ssh: connect to host 10.0.1.50 port 22: Operation timed out
Allow SSH from a local terminal on the machine and check the active rules:
sudo ufw allow ssh
sudo ufw status verbose
The rule set now admits port 22 over IPv4 and IPv6, and the default stays deny for everything else:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22 ALLOW IN Anywhere
22 (v6) ALLOW IN Anywhere (v6)
Install timings measured on the test box
Three installs ran on the same VM against the same mirror region. Run 1 was the interactive session (without subvolumes, because of the mistaken No). Run 2 replayed the saved config silently, and run 3 replayed the edited config with the Snapper workaround.
| Run | Method | Wall time | Packages | Root used | Snapper |
|---|---|---|---|---|---|
| 1 | Interactive menus | 7m 2s | 811 | 4.2 GB | No (no subvolumes) |
| 2 | --config --creds --silent | 7m 46s | 811 | 4.2 GB | No (loader bug) |
| 3 | Silent plus custom_commands | 6m 35s | 813 | 4.4 GB | Yes, root and home |
Run 1 is the timer built into archinstall, which starts at the install confirmation. Runs 2 and 3 were timed with the shell time builtin, so they also include the package database fetch, typing the decryption key and the five second countdown. The install log from run 3 breaks the work down by phase, with shares taken over the 6m 16s between the disk wipe and the final log line:
| Phase (run 3) | Duration | Share |
|---|---|---|
| Partition and format, start installation | 3 s | 1% |
| Mirror ranking | 1m 57s | 31% |
| Base pacstrap (base, linux, linux-firmware, mkinitcpio, btrfs-progs, sudo) | 1m 20s | 21% |
| zram, bootloader, NetworkManager, PipeWire, ufw, fonts, drivers | 1m 13s | 19% |
| GNOME profile and GDM | 1m 27s | 23% |
| Extra packages, custom commands, fstab | 16 s | 4% |
The number that matters is the mirror ranking share: almost a third of the install went to timing mirrors, not to downloading packages. Choosing one or two nearby mirrors under “Add custom servers” instead of a whole region removes most of that wait on repeat installs. Once the desktop is up, an AUR helper is usually the next step; the yay install guide needs the base-devel and git packages added in Step 9, and the yay vs pacman vs paru comparison helps decide between the two helpers.