Arch Linux

Things to Do After Installing Arch Linux: Post-Install Guide

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.

Original content from computingforgeeks.com - post 609

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.

Arch Linux post install check of archinstall Btrfs subvolumes, zram swap and missing git and yay

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:

  • git and base-devel, so no AUR builds
  • intel-ucode and amd-ucode, but only because our lab was a VM
  • fwupd, snapper and snap-pac
  • reflector and pacman-contrib
  • ufw or firewalld, and man-db
  • any power manager (power-profiles-daemon, tuned or tlp)

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:

yay AUR helper on Arch Linux with pacman -Qm listing yay and downgrade as foreign packages

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:

Snapper snapshot list on Arch Linux with snap-pac pre and post pairs and undochange removing a package

This setup undoes package changes well, but it has limits you should know before the day you need it:

  • /var/log is its own subvolume, so pacman.log still records the install you undid
  • snapper rollback won’t boot you into the old snapshot, since the boot entry pins rootflags=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:

GPUInstallNotes
Intelsudo pacman -S mesa vulkan-intel intel-media-driver lib32-mesa lib32-vulkan-intelVideo decode through intel-media-driver on Broadwell and newer
AMDsudo pacman -S mesa lib32-mesa vulkan-radeon lib32-vulkan-radeonOpen driver, nothing else to configure
NVIDIA Turing and newer (GTX 16, RTX 20 and later)sudo pacman -S nvidia-open nvidia-utils lib32-nvidia-utilsUse nvidia-open-lts with linux-lts, or nvidia-open-dkms plus headers for any other kernel
NVIDIA Maxwell, Pascal, Voltayay -S nvidia-580xx-dkms nvidia-580xx-utilsLegacy 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 yay on its own, which updates repo and AUR packages together
  • check that snap-pac printed its two ==> root: lines
  • reboot after any linux, systemd or 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.

Keep reading

Install KVM and Virt-Manager on Arch Linux Virtualization Install KVM and Virt-Manager on Arch Linux Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift Things to Do After Installing CachyOS Arch Linux Things to Do After Installing CachyOS Things to Do After Installing Fedora 42 Workstation Desktop Things to Do After Installing Fedora 42 Workstation Things to Do After Installing EndeavourOS: 10 Tested Steps Arch Linux Things to Do After Installing EndeavourOS: 10 Tested Steps How To Install Slack on CentOS 8 / RHEL 8 Desktop How To Install Slack on CentOS 8 / RHEL 8

2 thoughts on “Things to Do After Installing Arch Linux: Post-Install Guide”

Leave a Comment

Press ESC to close