Arch Linux: Base Install in Virtual Machine

Pre-install steps

Download and verify Arch Linux media

Download the current version of Arch Linux to a location on your filesystem. See the Acquire an installation image and Verify Signature sections of the Arch Linux installation guide for instructions on how to retrieve and verify the current version of Arch Linux.

I used archlinux-2020.08.01-x86_64.iso with kernel 5.7.12-arch1-1 for this gist.

Create Virtual Machine

I'm going to trust that you have a good understanding of your hypervisor of choice for this bit =)

There are a handful of VM settings that really matter for this gist:

  • Guest operating system:
    • VMware Workstation: Other Linux 6.x or later kernel 64-bit (other6xlinux-64)
    • Oracle VirtualBox: Arch Linux x64
  • Optical drive for mounting install media
  • 20GB virtual disk (commands in this gist are geared toward this size)
  • SCSI or SATA disk controller (VMware Paravirtual and LSI Logic were tested, as well as VirtualBox SATA)
  • UEFI boot mode:
    • VMware Workstation: VM > Settings > Options > Advanced > UEFI
    • Oracle VirtualBox: VM > Settings > System > Motherboard > Enable EFI (special OSes only)
  • Network adapter connected to a network that provides DHCP (NAT, or usually Bridged)
  • Display may need to be configured to accelerate 3D graphics - try this if you get a black screen after an otherwise successful looking boot

Aside from the above, configure the virtual machine to your liking.

Boot system to Arch Linux live distro

Mount the ISO image in the optical drive, start the virtual machine, and boot into the live Arch Linux installation distro.

Install steps

Check system capabilities

Verify boot mode

List contents of the efivars directory to verify boot mode is UEFI:

ls /sys/firmware/efi/efivars

If you get an error indicating that the directory does not exist, the system did not boot using UEFI, and you need to enable UEFI boot in VM settings.

Verify network availability

Use the following commands to check interface status and IP addressing:

ip link
ip addr

Example output:

root@archiso ~ # ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:98:d8:51 brd ff:ff:ff:ff:ff:ff
    altname enp2s1

root@archiso ~ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:98:d8:51 brd ff:ff:ff:ff:ff:ff
    altname enp2s1
    inet 192.168.5.141/24 brd 192.168.5.255 scope global dynamic ens33
       valid_lft 1630sec preferred_lft 1630sec
    inet6 fe80::20c:29ff:fe98:d851/64 scope link
       valid_lft forever preferred_lft forever

Your interface name will vary depending on factors, but lo is not the one you're looking for - if that's the only interface you see, you'll need to troubleshoot. Some common primary interface names are eth0, ens33, ens160, and enp0s3. The default in VMware Workstation 15 is ens33.

Sync network time

Ensure the system clock is synced (this is important for encrypted connections to succeed):

timedatectl set-ntp true

Configure storage

Write partition table

In this step we'll use fdisk to write a GUID partition table (GPT) to local disk (/dev/sda).

Launch fdisk:

fdisk /dev/sda

In fdisk, enter the following commands, pressing enter after each:

g
w

The g command stages the change to GPT for the disk, the w command writes it.

Create primary partitions

In this step we'll use parted to create, name, and set options against the primary partitions required to boot the system and define LVM volumes.

Launch parted:

parted /dev/sda

In parted, enter the following commands, pressing enter after each:

mkpart ESP fat32 1MiB 513MiB
mkpart primary 513MiB 1025MiB
mkpart primary 1025MiB 100%
name 1 efi
name 2 boot
name 3 lvm
set 1 boot on
set 3 lvm on
quit

There's a lot to unpack above.

The mkpart command creates a new partition. The EFI partition is the first partition we define, and it has an ESP type, will have a fat32 filesystem, and has a 512 MiB total size (starting at the 1MiB offset, ending at the 513MiB offset). The next partition is our boot partition, type primary, size 512 MiB. The last partition is our LVM partition, type primary, filling the remaining portion of the disk.

With the above explanation, name should be evident - these are labels that will be useful when managing partitions on the system, and describe the function of each partition.

We then set the boot flag to on for the EFI partition (partition 1), and the lvm flag to on for the LVM partition (partition 3)

You can run parted -l and lsblk to verify these changes:

root@archiso ~ # parted -l
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sda: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name  Flags
 1      1049kB  538MB   537MB                efi   boot, esp
 2      538MB   1075MB  537MB                boot
 3      1075MB  21.5GB  20.4GB               lvm   lvm

root@archiso ~ # lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop0    7:0    0 549.2M  1 loop /run/archiso/sfs/airootfs
sda      8:0    0    20G  0 disk
├─sda1   8:1    0   512M  0 part
├─sda2   8:2    0   512M  0 part
└─sda3   8:3    0    19G  0 part
sr0     11:0    1   671M  0 rom  /run/archiso/bootmnt

Create LVM volumes

First run the pvcreate command to create a physical volume:

pvcreate /dev/sda3

Next, run the vgcreate command to create a volume group on our new physical volume:

vgcreate arch /dev/sda3

Last, run the following sequence of lvcreate commands to create logical volumes on our new volume group:

lvcreate arch -n root -L 5G
lvcreate arch -n var -L 5G
lvcreate arch -n home -L 7G
lvcreate arch -n swap -l 100%FREE

Arguments in the lvcreate command mean:

  • arch: The name of the volume group to create the logical volume on
  • -n name: The name of the logical volume being created
  • -L #G: The size of the logical volume
  • -l #%FREE: What percentage of free extents to allocate to the logical volume

You can use the pvs, vgs, lvs, and lsblk commands to verify these changes:

root@archiso ~ # pvs
  PV         VG   Fmt  Attr PSize   PFree
  /dev/sda3  arch lvm2 a--  <19.00g    0

root@archiso ~ # vgs
  VG   #PV #LV #SN Attr   VSize   VFree
  arch   1   4   0 wz--n- <19.00g    0

root@archiso ~ # lvs
  LV   VG   Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  home arch -wi-a-----  7.00g
  root arch -wi-a-----  5.00g
  swap arch -wi-a----- <2.00g
  var  arch -wi-a-----  5.00g

root@archiso ~ # lsblk
NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop0           7:0    0 549.2M  1 loop /run/archiso/sfs/airootfs
sda             8:0    0    20G  0 disk
├─sda1          8:1    0   512M  0 part
├─sda2          8:2    0   512M  0 part
└─sda3          8:3    0    19G  0 part
  ├─arch-root 254:0    0     5G  0 lvm
  ├─arch-var  254:1    0     5G  0 lvm
  ├─arch-home 254:2    0     7G  0 lvm
  └─arch-swap 254:3    0     2G  0 lvm
sr0            11:0    1   671M  0 rom  /run/archiso/bootmnt

Format partitions and enable swap

Format the efi partition as FAT32 (also known as vfat):

mkfs.fat -F32 /dev/sda1

Format the boot, root, var, and home partitions as ext4:

mkfs.ext4 /dev/sda2
mkfs.ext4 /dev/arch/root
mkfs.ext4 /dev/arch/var
mkfs.ext4 /dev/arch/home

Format and enable the swap partition:

mkswap /dev/arch/swap
swapon /dev/arch/swap

You can use blkid -o list to verify these changes:

root@archiso ~ # blkid -o list
device                            fs_type      label         mount point                           UUID
----------------------------------------------------------------------------------------------------------------------------------------
/dev/sda3                         LVM2_member                (in use)                              yjpORe-gJ4d-L2wU-Ybz3-uUne-c9JY-VMXaG7
/dev/sr0                          iso9660      ARCH_202008   /run/archiso/bootmnt                  2020-08-01-09-10-05-00
/dev/loop0                        squashfs                   /run/archiso/sfs/airootfs
/dev/sda1                         vfat                       (not mounted)                         1F44-D088
/dev/sda2                         ext4                       (not mounted)                         1c4bc21a-be8c-44ec-9bf8-98d19f62fa8f
/dev/mapper/arch-root             ext4                       (not mounted)                         9159c405-4203-47a8-8801-9514fe0b0e08
/dev/mapper/arch-var              ext4                       (not mounted)                         7f9a80e7-a1b0-4634-b6ea-39dde1a1ee7e
/dev/mapper/arch-home             ext4                       (not mounted)                         1ec85e3d-78af-45a9-84e8-d14188bda4f5
/dev/mapper/arch-swap             swap                       [SWAP]                                c21cf436-2af1-46de-848f-8d886885b31e

Mount filesystems

Mount the root filesystem:

mount /dev/arch/root /mnt

Create directories for the efi, boot, var, and home filesystems:

mkdir /mnt/efi /mnt/boot /mnt/var /mnt/home

Mount the efi, boot, var, and home filesystems:

mount /dev/sda1 /mnt/efi
mount /dev/sda2 /mnt/boot
mount /dev/arch/var /mnt/var
mount /dev/arch/home /mnt/home

You can use mount|tail -n 5 or lsblk to verify these mounts:

root@archiso ~ # mount|tail -n 5
/dev/mapper/arch-root on /mnt type ext4 (rw,relatime)
/dev/sda1 on /mnt/efi type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro)
/dev/sda2 on /mnt/boot type ext4 (rw,relatime)
/dev/mapper/arch-var on /mnt/var type ext4 (rw,relatime)
/dev/mapper/arch-home on /mnt/home type ext4 (rw,relatime)

root@archiso ~ # lsblk
NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop0           7:0    0 549.2M  1 loop /run/archiso/sfs/airootfs
sda             8:0    0    20G  0 disk
├─sda1          8:1    0   512M  0 part /mnt/efi
├─sda2          8:2    0   512M  0 part /mnt/boot
└─sda3          8:3    0    19G  0 part
  ├─arch-root 254:0    0     5G  0 lvm  /mnt
  ├─arch-var  254:1    0     5G  0 lvm  /mnt/var
  ├─arch-home 254:2    0     7G  0 lvm  /mnt/home
  └─arch-swap 254:3    0     2G  0 lvm  [SWAP]
sr0            11:0    1   671M  0 rom  /run/archiso/bootmnt

Install packages and chroot into system

Install essential packages

Run the following commands to install essential packages into the mounted partitions:

pacstrap /mnt base linux linux-firmware grub efibootmgr lvm2 vim

Don't worry about any localization related errors you may receive - these will be resolved by future steps.

While the installation guide only includes the base, linux and linux-firmware packages in this command, these packages are the minimum required to progress with installation. The extra packages specified in this guide are:

  • grub: The boot loader we'll be using
  • efibootmgr: A utility used to modify the EFI boot manager
  • lvm2: Utilities for logical volume management (LVM)
  • vim: A text editor

These are the same packages that would install using pacman -S packagename. You may want to install additional packages - go for it!

Update system's filesystem table

Use the following command to generate fstab format mount entries and append those entries to the new system's /etc/fstab file:

genfstab -U /mnt >> /mnt/etc/fstab

Chroot into system

Jump into the new system to proceed with configuration steps:

arch-chroot /mnt

Configure System

Set root password

Change the root user's password so login is possible on first boot into the new system:

passwd

Set time zone and synchronize time

Set the time zone (US/Pacific time in this example):

ln -sf /usr/share/zoneinfo/US/Pacific /etc/localtime

Additional time zones can be found in /usr/share/zoneinfo, just look around with ls to find the most relevant one.

Synchronize your system clock and your hardware clock:

hwclock --systohc

Set localization

Edit /etc/locale.gen and uncomment en_US.UTF-8 UTF-8 and other needed locales (source).

Generate locales with:

locale-gen

Create /etc/locale.conf and save the following line (or your preferred locale) to it:

LANG=en_US.UTF-8

Configure networking

Run ip link to identify your interface name (ens33 in this case):

[root@archiso /]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:98:d8:51 brd ff:ff:ff:ff:ff:ff
    altname enp2s1

Edit /etc/systemd/network/20-wired.network to configure DHCP for your interface:

[Match]
Name=ens33

[Network]
DHCP=yes

Enable systemd-networkd and sytemd-resolved systemd services to start at boot:

systemctl enable systemd-networkd systemd-resolved

If you would prefer to set static addressing, or have multiple network interfaces, I recommend reviewing the systemd-networkd documentation in the Arch Wiki.

Set hostname and hosts aliases

Edit /etc/hostname and save the following line (or your preferred hostname) to it:

arch.localdomain

Edit /etc/hosts and save the following lines to it (modifying hostname as needed):

127.0.0.1   localhost
::1         localhost
127.0.1.1   arch.localdomain arch

Update initial ramdisk hooks

The mkinitcpio program generates the initial ramdisk (initramfs) image based on configuration specified in /etc/mkinitcpio.conf. Because this gist uses LVM partitions, we need to include lvm2 (the name of our LVM package) to HOOKS, before filesystems is specified (these hooks load in order).

Edit /etc/mkinitcpio.conf and find the line:

HOOKS=(base udev autodetect modconf block filesystems keyboard fsck)

Change the line to include lvm2 before filesystems:

HOOKS=(base udev autodetect modconf block lvm2 filesystems keyboard fsck)

Generate boot image and configure boot loader (GRUB)

Update initial ramdisk

In order for the /etc/mkinitcpio.conf change to apply on system boot, we need to update our initial ramdisk (initramfs).

Run the following command to generate a new initramfs image:

mkinitcpio -P

Install GRUB

Run the following command to install the GRUB with an EFI target system/directory:

grub-install --target=x86_64-efi --efi-directory=/efi

Configure GRUB

Generate GRUB configurations under the /boot and /efi partitions:

grub-mkconfig -o /boot/grub/grub.cfg
grub-mkconfig -o /efi/EFI/arch/grub.cfg

Post-install steps

Reboot into system

At this point you can exit chroot, issue reboot, and log into the system as root for further configuration. Yay!

References

I used the following references to learn how to install Arch Linux in a virtual machine:

This runbook started as a gist on GitHub - view it here: https://gist.github.com/miliarch/dd19af34679417b18f1bcdb680728a45