archinstall gets Arch Linux onto a disk in under seven minutes, and the system it boots into is missing more than most people expect. Our fresh KDE Plasma install came up with Btrfs subvolumes and zram swap in place, but with no git, no man pages, no firmware updater, no firewall, no snapshot tool and no AUR helper. That gap is what an Arch Linux post install pass is for.
Below are the things to do after installing Arch Linux with archinstall, in the order we run them. They cover pacman and mirrors, yay, microcode and fwupd, Snapper on the archinstall subvolume layout, GPU drivers and power management, and most apply to a hand-built install too. Every command below was run on a fresh archinstall 4.5 install of Arch Linux with KDE Plasma and systemd-boot in October 2026, except the GPU driver installs and firmware updates, which a VM cannot exercise.
Check What archinstall Left Out
Before you add anything, look at what the installer actually built, because two later steps (snapshots and TRIM) depend on it. We ran the archinstall guided installer with its default Btrfs layout and compression turned on, which gives you four subvolumes mounted from one partition:
findmnt -t btrfs -o TARGET,SOURCE,OPTIONS
Root lives in @, and home, logs and the package cache each get their own subvolume so a root snapshot does not drag them along. Your device will be nvme0n1p2 or similar rather than sdb2:
TARGET SOURCE OPTIONS
/ /dev/sdb2[/@] rw,relatime,compress=zstd:3,discard=async,space_cache=v2,subvolid=256,subvol=/@
|-/home /dev/sdb2[/@home] rw,relatime,compress=zstd:3,discard=async,space_cache=v2,subvolid=257,subvol=/@home
|-/var/cache/pacman/pkg /dev/sdb2[/@pkg] rw,relatime,compress=zstd:3,discard=async,space_cache=v2,subvolid=259,subvol=/@pkg
`-/var/log /dev/sdb2[/@log] rw,relatime,compress=zstd:3,discard=async,space_cache=v2,subvolid=258,subvol=/@log
Notice what is not there. There is no @snapshots subvolume, even though the layout is clearly built for one. archinstall also wrote a 1 GiB FAT32 ESP mounted at /boot with systemd-boot on it. Swap is a 2.9 GB zram device using zstd, and zswap.enabled=0 sits on the kernel command line so the two do not compete. Those are good defaults, so leave them alone.

The package list is where the surprises are. Out of 761 packages on our install, these were absent, and every one of them comes up later in this guide:
gitandbase-devel, so no AUR buildsintel-ucodeandamd-ucode, but only because our lab was a VMfwupd,snapperandsnap-pacreflectorandpacman-contribufworfirewalld, andman-db- any power manager (
power-profiles-daemon,tunedortlp)
archinstall 4.5 does have menu entries for a firewall, power management and Btrfs snapshots. They all default to off, so unless you went looking for them during the install, you have none of them now. Microcode is the exception that depends on your hardware: archinstall adds it on bare metal and skips it when it detects a virtual machine.
Tune pacman and Enable Multilib
The shipped /etc/pacman.conf is already better than it used to be: Color is on and ParallelDownloads = 5 is uncommented. Older guides tell you to uncomment #ParallelDownloads, and that sed now matches nothing. These two edits raise the download count to 10, turn on the old/new version columns in transaction summaries, add the Pac-Man progress bar, and enable the 32-bit [multilib] repository you’ll need for Steam and for 32-bit GPU libraries:
sudo sed -i -e 's/^#\?ParallelDownloads.*/ParallelDownloads = 10/' -e 's/^#VerbosePkgLists/VerbosePkgLists/' -e '/^Color$/a ILoveCandy' /etc/pacman.conf
sudo sed -i '/^#\[multilib\]/{s/^#//;n;s/^#Include/Include/}' /etc/pacman.conf
The second sed only touches the [multilib] header and the line right after it, so [multilib-testing] stays commented out. Refresh the databases and check that a third repository now syncs:
sudo pacman -Syu
Right after the install, a plain pacman -Syu had nothing to do, since archinstall pulls current packages. After the two seds, the next sync downloaded multilib next to core and extra, and pacman -Sl multilib listed 182 packages. If pacman ever refuses to run with a lock error after an interrupted update, the pacman database lock fix covers the safe way to clear it. We hit exactly that on the cloud image we used to run archinstall: a stale zero-byte /var/lib/pacman/db.lck with no pacman process behind it.
Rank Mirrors and Prune the Package Cache
archinstall speed-tests mirrors once during the install and never again. Mirrors drift out of sync over months, so put reflector on a timer and let paccache trim the package cache, which had already reached 1.5 GB on our KDE install before we had installed a single app. Both come from the official repositories:
sudo pacman -S reflector pacman-contrib
The timer doesn’t take command-line flags. It reads /etc/xdg/reflector/reflector.conf, whose default sorts the five most recently synced HTTPS mirrors by age, not by speed. Open the file:
sudo vim /etc/xdg/reflector/reflector.conf
Replace its contents with the countries closest to you. We used Germany and the Netherlands:
--save /etc/pacman.d/mirrorlist
--protocol https
--country Germany,Netherlands
--latest 10
--sort rate
Run the service once now, then enable both weekly timers:
sudo systemctl start reflector.service
sudo systemctl enable --now reflector.timer paccache.timer
The service took 24 seconds here because --sort rate downloads a database file from every candidate mirror. One Dutch mirror timed out during a manual run (WARNING: failed to rate http(s) download ... Download timed out after 5 second(s)), which is harmless: reflector just drops it from the list. Check the header of the new mirrorlist to confirm the timer will use your config file:
head -7 /etc/pacman.d/mirrorlist
The With: line names the config file rather than a list of flags:
################################################################################
################# Arch Linux mirrorlist generated by Reflector #################
################################################################################
# With: reflector @/etc/xdg/reflector/reflector.conf
# When: 2026-10-29 13:31:22 UTC
# From: https://archlinux.org/mirrors/status/json/
paccache.timer runs weekly and keeps the three most recent versions of each package, which is enough to roll one back with downgrade later. For country codes and other filters, our older guide to generating a mirrorlist by country still applies.
Install an AUR Helper
You can’t build anything from the AUR on a stock archinstall system, because git is not installed and neither is the base-devel group. The first clone fails with bash: git: command not found. Install both first; on our system that was 22 packages, a 74.82 MiB download and 294.30 MiB on disk, most of it gcc:
sudo pacman -S --needed git base-devel
We use yay here. If you’re still deciding between helpers, our yay vs pacman vs paru comparison covers where paru wins. Build yay as your normal user, never as root, and use -r so makepkg removes the Go toolchain it pulls in for the build:
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -sri
The build took 33 seconds. makepkg installed go (226.64 MiB), compiled yay, removed go again, and then installed two packages, not one:
Package (2) New Version Net Change
yay 13.1.0-1 11.87 MiB
yay-debug 13.1.0-1 23.18 MiB
That second package comes from the debug flag in the default OPTIONS array of /etc/makepkg.conf. Every compiled AUR package you build will ship a matching -debug package full of symbols you will not use, and the one for yay was twice the size of yay itself. Turn it off and remove the one you already have:
sudo sed -i 's/ purge debug lto)/ purge !debug lto)/' /etc/makepkg.conf
sudo pacman -Rns yay-debug
After that change we rebuilt yay from a fresh clone and makepkg produced only yay-13.1.0-1-x86_64.pkg.tar.zst. A future pacman update can leave the new default as /etc/makepkg.conf.pacnew, so keep the !debug edit when you merge it.
The first AUR package worth installing is downgrade, which lets you pick an older version from the pacman cache or the Arch archive when an update breaks something. With yay in place it built and installed in 8 seconds:
yay -S downgrade
Both now show up as foreign packages, the ones pacman did not get from a repository:

Read the PKGBUILD diff when yay offers it on AUR packages you haven’t used before, because the AUR is user-submitted and nothing checks it for you. Our separate yay install guide goes through yay’s day-to-day flags.
Verify CPU Microcode Loads Early
On bare metal, archinstall already installed the microcode package for your CPU vendor. It leaves microcode out only when it detects a virtual machine, which is why our lab had none, and the Arch wiki agrees with that call: in a VM the microcode belongs on the host. Check what you have:
pacman -Q intel-ucode amd-ucode
On real hardware one of the two is listed and the other reports was not found, which is normal. If both are missing on a physical machine, find the vendor with lscpu | grep 'Vendor ID': GenuineIntel means intel-ucode, and AuthenticAMD means amd-ucode. We installed the Intel package on the VM to see what the install does:
sudo pacman -S intel-ucode
You don’t need to touch the boot entry. The default /etc/mkinitcpio.conf lists the microcode hook right after autodetect, so the install triggered an initramfs rebuild that packed the microcode into an early, uncompressed image at the front of initramfs-linux.img:
-> Running build hook: [base]
-> Running build hook: [udev]
-> Running build hook: [autodetect]
-> Running build hook: [microcode]
-> Running build hook: [modconf]
-> Running build hook: [kms]
-> Running build hook: [keyboard]
-> Running build hook: [keymap]
-> Running build hook: [consolefont]
-> Running build hook: [block]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating zstd-compressed initcpio image: '/boot/initramfs-linux.img'
-> Early uncompressed CPIO image generation successful
That’s why the systemd-boot entry still has a single initrd /initramfs-linux.img line and needs no separate intel-ucode.img line, which many older guides still tell you to add. The same install also printed warning: directory permissions differ on /boot/ with filesystem: 700 package: 755 on the next line. It is harmless. archinstall mounts the ESP with restrictive permissions, which is also why lsinitcpio --early /boot/initramfs-linux.img reports No such file unless you run it with sudo. After a reboot, check the kernel log:
sudo journalctl -k -b | grep -i microcode
On real hardware you’ll see the microcode revision the kernel loaded. Our lab ran on a virtual KVM CPU, which takes no microcode updates, so the kernel printed x86/CPU: Model not found in latest microcode list instead. On a physical machine, keep the package installed and updated: it’s how security fixes for your CPU arrive between firmware releases.
Update Firmware With fwupd
fwupd pulls UEFI, SSD, dock and peripheral firmware from the Linux Vendor Firmware Service, and on many laptops it’s the only way to update the BIOS without booting Windows. It isn’t installed by archinstall either:
sudo pacman -S fwupd
Refresh the metadata, then list what can be updated:
sudo fwupdmgr refresh --force
fwupdmgr get-updates
Our VM only exposes QEMU disks and a virtual CPU, so both commands came back empty, and get-updates exits with status 2 when there’s nothing to do. That matters if you script it:
Successfully downloaded new metadata:
• No devices are updatable
No updatable devices
On a laptop or desktop, fwupdmgr get-devices lists the system firmware, the NVMe drive and often a Thunderbolt controller. Apply what it offers with fwupdmgr update, stay on AC power, and expect a reboot for UEFI capsule updates. If you turned on Secure Boot, the fwupd page on the Arch wiki covers signing the fwupd EFI binary, because an unsigned one won’t load.
Set Up Btrfs Snapshots With Snapper
Snapper plus snap-pac takes a Btrfs snapshot before and after every pacman transaction, so a bad update becomes a two-command undo. The catch on the archinstall layout is that the default Snapper config creates its .snapshots subvolume nested inside @. We move it to a top-level @snapshots subvolume, which is the layout the Snapper page on the Arch wiki suggests. Install the packages and create the root config:
sudo pacman -S snapper snap-pac
sudo snapper -c root create-config /
sudo btrfs subvolume list /
ID 262 is the nested subvolume, with top level 256 (that’s @) as its parent:
ID 256 gen 51 top level 5 path @
ID 257 gen 50 top level 5 path @home
ID 258 gen 51 top level 5 path @log
ID 259 gen 51 top level 5 path @pkg
ID 260 gen 14 top level 256 path var/lib/portables
ID 261 gen 14 top level 256 path var/lib/machines
ID 262 gen 52 top level 256 path .snapshots
Delete it, then mount the top of the Btrfs volume (subvolume ID 5) and create @snapshots beside @. Replace /dev/sdb2 with the source device findmnt showed you in the first step:
sudo btrfs subvolume delete /.snapshots
sudo mount -o subvolid=5 /dev/sdb2 /mnt
sudo btrfs subvolume create /mnt/@snapshots
sudo umount /mnt
sudo mkdir /.snapshots
Now mount it permanently. Open fstab:
sudo vim /etc/fstab
Copy your existing / line, keep its UUID and options, and change only the mount point and the subvol= value. Ours ended up as:
UUID=467c4d43-60c3-49ea-9723-ba599dd27775 /.snapshots btrfs rw,relatime,compress=zstd:3,discard=async,space_cache=v2,subvol=/@snapshots 0 0
Reload systemd so it sees the new fstab entry, mount it and lock the directory down:
sudo systemctl daemon-reload
sudo mount -a
sudo chmod 750 /.snapshots
findmnt /.snapshots
The source should end in [/@snapshots], not [/@/.snapshots]:
TARGET SOURCE FSTYPE OPTIONS
/.snapshots /dev/sdb2[/@snapshots] btrfs rw,relatime,compress=zstd:3,discard=async,space_cache=v2,subvolid=263,subvol=/@snapshots
The default timeline keeps 10 hourly, 10 daily, 10 monthly and 10 yearly snapshots, which is far more than a desktop needs on top of the pacman pairs. Cut it down and enable the timers:
sudo sed -i -e 's/^TIMELINE_LIMIT_HOURLY=.*/TIMELINE_LIMIT_HOURLY="5"/' -e 's/^TIMELINE_LIMIT_DAILY=.*/TIMELINE_LIMIT_DAILY="7"/' -e 's/^TIMELINE_LIMIT_WEEKLY=.*/TIMELINE_LIMIT_WEEKLY="0"/' -e 's/^TIMELINE_LIMIT_MONTHLY=.*/TIMELINE_LIMIT_MONTHLY="0"/' -e 's/^TIMELINE_LIMIT_YEARLY=.*/TIMELINE_LIMIT_YEARLY="0"/' /etc/snapper/configs/root
sudo systemctl enable --now snapper-timeline.timer snapper-cleanup.timer
Prove it works before you trust it. We installed tree, and snap-pac printed the pre and post snapshot numbers inside the pacman transaction:
sudo pacman -S tree
Look for the two ==> root: lines:
:: Running pre-transaction hooks...
(1/1) Performing snapper pre snapshots for the following configurations...
==> root: 3
:: Processing package changes...
installing tree...
:: Running post-transaction hooks...
(1/2) Arming ConditionNeedsUpdate...
(2/2) Performing snapper post snapshots for the following configurations...
==> root: 4
Our numbers start at 3 because an earlier test transaction took 1 and 2; on a fresh config yours start at 1. Then we undid the install with sudo snapper -c root undochange 3..4. It reported create:0 modify:1 delete:6, the binary and man page were gone, and pacman no longer knew about the package. The pacman database lives in /var/lib/pacman on @, so it rolled back along with the files and stayed consistent:

This setup undoes package changes well, but it has limits you should know before the day you need it:
/var/logis its own subvolume, sopacman.logstill records the install you undidsnapper rollbackwon’t boot you into the old snapshot, since the boot entry pinsrootflags=subvol=@- systemd-boot shows no snapshot entries; that needs GRUB with
grub-btrfs, or Limine
The Btrfs default subvolume on our install was still ID 5, so a full root restore means booting the Arch ISO, moving @ aside and snapshotting a good snapshot back into @. That is the manual procedure the wiki describes for this layout, and it is worth reading once while the system still boots.
Install the Right GPU Driver
If you picked “All open-source” in archinstall’s graphics menu, Intel and AMD cards are already covered: our install had mesa, vulkan-intel, vulkan-radeon, intel-media-driver and the xf86-video-* drivers. What is missing is the 32-bit side for Steam and Wine, and for NVIDIA the proprietary userspace. Pick the line for your card:
| GPU | Install | Notes |
|---|---|---|
| Intel | sudo pacman -S mesa vulkan-intel intel-media-driver lib32-mesa lib32-vulkan-intel | Video decode through intel-media-driver on Broadwell and newer |
| AMD | sudo pacman -S mesa lib32-mesa vulkan-radeon lib32-vulkan-radeon | Open driver, nothing else to configure |
| NVIDIA Turing and newer (GTX 16, RTX 20 and later) | sudo pacman -S nvidia-open nvidia-utils lib32-nvidia-utils | Use nvidia-open-lts with linux-lts, or nvidia-open-dkms plus headers for any other kernel |
| NVIDIA Maxwell, Pascal, Volta | yay -S nvidia-580xx-dkms nvidia-580xx-utils | Legacy branch from the AUR, needs linux-headers; add lib32-nvidia-580xx-utils for Steam |
The plain nvidia package doesn’t exist anymore, and sudo pacman -S nvidia fails with error: target not found: nvidia. nvidia-open replaced it, which is why older guides now point you at a dead name. Our lab VM has no GPU, so we only resolved these transactions with pacman -S --print: the NVIDIA line pulled in 56 packages, most of them lib32-* from multilib, which is the step you enabled earlier. Check which driver the kernel bound after a reboot:
lspci -k | grep -A3 -E 'VGA|3D|Display'
The Kernel driver in use: line should read nvidia, amdgpu, i915 or xe. If it says nouveau on an NVIDIA card, the proprietary module didn’t load; on our VM it read bochs-drm, the emulated display. The Arch wiki NVIDIA page has the full card-to-package table if you’re not sure which generation you own.
Pick One Power Profile Manager
Run exactly one power manager. On KDE Plasma or GNOME the simplest choice is power-profiles-daemon, because the battery applet in both desktops talks to it directly. PowerDevil and UPower were already installed on our archinstall system, but the daemon they need wasn’t:
sudo pacman -S power-profiles-daemon
powerprofilesctl
The service is D-Bus activated, so systemctl is-enabled reports disabled even while it runs. On our VM it offered only two profiles:
* balanced:
PlatformDriver: placeholder
power-saver:
PlatformDriver: placeholder
placeholder means no hardware driver backs the profiles, so switching them changes nothing. On a laptop you want to see a real platform driver or a CPU driver such as amd_pstate or intel_pstate there, and a performance profile in the list.
TLP is the alternative for people who want per-setting control, and on Arch it no longer costs you the desktop applet. The tlp-pd package in extra implements the same D-Bus interface and declares a conflict with power-profiles-daemon, so installing it while power-profiles-daemon is present makes pacman offer to remove the old daemon:
sudo pacman -S tlp tlp-pd
Answer y at :: tlp-pd-1.10.2-1 and power-profiles-daemon-0.30-1 are in conflict. Remove power-profiles-daemon? [y/N]. Arch does not enable either TLP service for you, so enable both and reboot:
sudo systemctl enable tlp.service tlp-pd.service
sudo reboot
Don’t skip the reboot. The old power-profiles-daemon process keeps running after pacman removes its package. In our lab we first enabled both units with --now instead: tlp.service started, but systemctl enable --now tlp-pd.service failed with Job for tlp-pd.service failed because a timeout was exceeded, and tlp-stat warned that power saving would not apply on boot. After the reboot, check the status section:
sudo tlp-stat -s
Both lines should read enabled, and tlp-pd should be running:
+++ TLP Status
tlp = enabled, last run: 13:52:38, 5 sec(s) ago
tlp-rdw = not installed
tlp-pd = enabled, running
TLP profile = performance/AC
Power source = unknown
Plain tlp on its own is the trap: it declares a conflict only with tuned, so pacman -S tlp resolved without any conflict prompt next to power-profiles-daemon in our lab, and tlpui actually installed TLP alongside it as a dependency. TLP then skips the settings power-profiles-daemon owns, and the TLP FAQ on power-profiles-daemon says to uninstall power-profiles-daemon when you use TLP, or mask power-profiles-daemon.service if removing it would take other packages with it. The archinstall power menu offers power-profiles-daemon or tuned. If you go the tuned route on a desktop, add tuned-ppd so the battery applet keeps working: it does conflict with power-profiles-daemon in pacman, so it can’t end up doubled.
Turn On the Firewall
archinstall ships no firewall, and once you enable sshd, KDE Connect or a dev server, that matters. ufw is the shortest path. Allow SSH before you enable it if you manage the box remotely:
sudo pacman -S ufw
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw enable
sudo systemctl enable ufw
Over SSH, ufw enable asks Command may disrupt existing ssh connections. Proceed with operation (y|n)? and answer y. In a script with no terminal it prints Aborted and leaves the firewall off, so scripts need sudo ufw --force enable. Check the result with ufw itself:
sudo ufw status verbose
Default deny inbound, allow outbound, and port 22 open on IPv4 and IPv6:
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)
Don’t use systemctl is-active ufw as your check. On our system it printed inactive right after the output above, because systemctl enable does not start the unit and ufw enable loads the rules without it. The unit takes over from the next boot.
Fill the Remaining Gaps
Three smaller items finish the job. The first is man pages: archinstall installs none, so man pacman fails until you add them:
sudo pacman -S man-db man-pages
Bluetooth is installed (bluez came with the KDE profile) but the service is disabled. Enable it:
sudo systemctl enable --now bluetooth
On a machine with no adapter the unit stays inactive (dead) and the journal says Bluetooth service skipped, unmet condition check ConditionPathIsDirectory=/sys/class/bluetooth. That is the condition doing its job, not a failure, and it starts on its own once an adapter appears.
For TRIM you can mostly skip fstrim.timer. The Btrfs mounts archinstall wrote already carry discard=async, which the kernel has enabled by default on SSDs since Linux 6.2, as the Btrfs page on the Arch wiki notes. We enabled the weekly timer anyway with sudo systemctl enable --now fstrim.timer, since it costs nothing and covers any non-Btrfs filesystem you add later.
Keep Arch Healthy Week to Week
With the timers above, most of the routine runs itself: reflector refreshes mirrors weekly, paccache trims the cache, and Snapper prunes old snapshots. What’s left for you is a short habit before each update:
- read the archlinux.org news page for manual interventions
- run
yayon its own, which updates repo and AUR packages together - check that snap-pac printed its two
==> root:lines - reboot after any
linux,systemdor driver update
Clear orphaned dependencies now and then. The usual one-liner errors out when there’s nothing to remove, which confused us the first time:
sudo pacman -Rns $(pacman -Qdtq)
With no orphans, pacman -Qdtq prints nothing and pacman gets no arguments, so it answers error: no targets specified (use -h for help). That message means the system is clean. If you later want a tuned Arch base with most of this preconfigured, compare what you built here with our CachyOS post-install checklist, which starts from a system that already ships Snapper, an AUR helper and its own kernels.
Yo, the Atom repo keeps saying the DB is corrupted. what gives?
Try clean the database,then update the system to synchronize database