Background

This post is rather selfish in nature and often does not describe what each command does. It is not meant to be a post that “just works” for anyone, but rather so that others can see how I installed Arch Linux on my machine (i.e., I use my device names, etc.). Also, I am far from an Arch or Linux expert, most of what follows has been taken from other sources. I link to where I found the information.

Next, keep in mind that Arch installations may vary slightly depending on the machine you are trying to install it on. I strongly suggest trying to search the Arch wiki for your target machine (e.g., Dell XPS 13 7390 Arch Wiki.

Also, I acknowledge that I could have copied my existing Arch installation to the target machine rather than manually setting everything up. However, I was interested in documenting the steps of a fresh install as well as build up my scripts to make it easier to do again in the future.

Installing Arch on a USB from Another Arch Machine

USB Storage Arch

Resource(s):

  1. Find the device with lsblk -f
  2. mkdir /mnt/usb
  3. mount -U <UUID> /mnt/usb
  4. When done, umount /mnt/usb

Downloading the Arch .iso

Download the *.iso file from a mirror listed on the Arch Downloads page (HTTP Direct Downloads).

Creating Bootable Arch image

Resource(s):

$ dd bs=4M if=$HOME/Downloads/archlinux.*.iso of=/dev/sda status=progress && sync

Plug the USB into the Target Machine

Resource(s):

  1. Enter the machine’s BIOS menu. For me, smash <F12> while booting.

CRITICAL: in the BIOS, do the following:

  1. Disable UEFI Secure Boot
  2. Change the boot order to have the USB first
  3. Change the SATA operating mode from RAID to AHCI Your SSD should show when you boot arch with lsblk -f

Warmup complete. Level 1 unlocked.


On the target machine

Connect to WiFi

  1. Make sure that the wireless drivers in the ISO were included and are supported

  2. Connect to the WiFi

$ lspci -k | grep -A3 'Network controller'
$ iw dev
$ ip link set wlp2s0 up
$ iw dev wlp2s0 scan | grep 'SSID:'
$ wpa_supplicant -i wlp2s0 -c <(wpa_passphrase 'your_network_ssid' 'password')

Once a connection is established, fork the process to the background by pressing [CTRL]+z and running bg.

  1. Lease an IP address with dhcpcd wlp2s0
  2. Sync system time with timedatectl set-ntp true

Partitioning the Drive

  1. lsblk to get the drive’s name.

CRITICAL: Use the UEFI/GPT partitioning layout. Specific details here and example layouts here. I originally was following this post, but my target machine would not allow “Legacy” boot mode (i.e., BIOS/MBR) in the BIOS.

Resources(s):

  1. Partition the drive with fdisk. You could also use parted or gdisk.
$ fdisk /dev/nvme0n1
$ (fdisk) g
$ (fdisk) n (skip, skip, +512M)
$ (fdisk) t (1, 1)
$ (fdisk) n (skip, skip, skip)

Formatting the Partitions

Resource(s):

# Should show "esp" in Flags:
$ parted /dev/nvme0n1 print

# Initialize it as FAT32
$ mkfs.fat -F32 -nESP /dev/nvme0n1p1

# Initialize the root filesystem
$ mkfs.ext4 /dev/nvme0n1p2

Encrypt the Drive

Resource(s):

$ cryptsetup -c aes-xts-plain64 -y --use-random luksFormat /dev/nvme0n1p2
$ cryptsetup luksOpen /dev/nvme0n1p2 luks

Logical Volume Manager (LVM) Partitions

Resource(s):

$ pvcreate /dev/mapper/luks
$ vgcreate vg0 /dev/mapper/luks
$ lvcreate -L 8G vg0 -n swap
$ lvcreate -L 25G vg0 -n root
$ lvcreate -l 100%FREE vg0 -n home

Format Partitions

Resource(s):

$ mkfs.ext4 /dev/mapper/vg0-root
$ mkfs.ext4 /dev/mapper/vg0-home
$ mkswap /dev/mapper/vg0-swap

Mount the Filesystem

Resource(s)

$ mount /dev/mapper/vg0-root /mnt
$ mkdir /mnt/{boot,home}
$ mount /dev/nvme0n1p1 /mnt/boot
$ mount /dev/mapper/vg0-home /mnt/home
$ swapon /dev/mapper/vg0-swap

Install Packages that Go with You to the Root System

Resource(s)

CRITICAL: Install any packages that will help you with setup (e.g., packages for connecting to WiFi). You could minimally install base and base-devel.

$ pacstrap -i /mnt \
  base \
  base-devel \
  linux \
  lvm2 \
  linux-firmware \
  nano \
  iw \
  dialog \
  dhcpcd \
  wpa_supplicant \
  git \
  vim \
  openssh \
  intel-ucode

Generate fstab

Resource(s):

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

Enter the Root System

Resource(s):

$ arch-chroot /mnt /bin/bash
  1. Set timezone
# ln -s /usr/share/zoneinfo/America/New_York /etc/localtime
  1. Set locale
$ vim /etc/locale.gen # (uncomment en_US.UTF-8 UTF-8)
$ locale-gen
$ echo LANG=en_US.UTF-8 > /etc/locale.conf
$ export LANG=en_US.UTF-8
  1. Set hardware clock
$ hwclock --systohc --utc
  1. Set hostname

Resource(s):

$ echo dell-arch > /etc/hostname

/etc/hosts

127.0.0.1	localhost
::1        localhost
127.0.1.1	dell-arch.localdomain	dell-arch
  1. Create a user

Resource(s):

$ useradd -m -g users -G wheel -s /bin/bash mccurdyc # the shell must be listed in $(cat /etc/shells)
$ passwd mccurdyc
$ visudo # uncomment %wheel ALL=(ALL) ALL
  1. Configure mkinitcpio with modules needed for the initrd image
$ vim /etc/mkinitcpio.conf # Add 'encrypt' and 'lvm2' to HOOKS before 'filesystems'
$ mkinitcpio -p linux
  1. Setup the systemd-boot boot manager

CRITICAL: grub didn’t work

Resource(s):

# Make sure your ESP partition (described earlier) is mounted at /boot
$ mount -l | grep boot
$ bootctl --path=/boot install

# Get the UUID of your root partition
$ touch /boot/loader/entries/arch-encrypted-lvm.conf
$ blkid -s UUID -o value /dev/nvme0n1p2 >> /boot/loader/entries/arch-encrypted-lvm.conf

Add the following to /boot/loader/entries/arch-encrypted-lvm.conf

title Arch Linux Encrypted LVM
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options cryptdevice=UUID=<UUID>:vg0 root=/dev/mapper/vg0-root quiet rw

Reboot

  1. Leave the root system with exit
  2. reboot

If everything works, you’ve made it passed the hardest part! Level 2 unlocked.


After Rebooting

Resource(s):

Make sure some things start on startup (and start them now).

$ systemctl enable dhcpcd.service
$ systemctl start dhcpcd.service
$ systemctl enable wpa_supplicant.service
$ systemctl start wpa_supplicant.service

Reconnect to the WiFi

$ iw dev # the device name probably changed
$ sudo ip link set wlp2s0 up
$ sudo su -c 'wpa_supplicant -i wlp2s0 -c <(wpa_passphrase "your_network_ssid" "password")'

Install Necessary and Helpful Packages

Resource(s):

$ pacman -S
  networkmanager \
  iw \
  zsh \
  wpa_supplicant \
  dialog \
  wireless_tools \
  base-devel \
  xclip \
  rofi \
  alacritty \
  netctl \
  fzf \
  tree \
  scrot \
  xdg-utils \
  bluez \
  bluez-utils \
  pulseaudio-bluetooth \
  pulseaudio-alsa

Make sure some more things start on startup (and start them now).

$ systemctl enable NetworkManager.service
$ systemctl start NetworkManager.service
$ systemctl enable bluetooth.service
$ systemctl start bluetooth.service

Clone Helpful git Repositories

Set zsh as your default shell because the tools in mccurdyc/dotfiles expect this.

$ sudo chsh -s /bin/zsh mccurdyc

CRITICAL: This step creates the necessary symlinks and does the installations and configuring described below.

$ git clone --recursive https://github.com/mccurdyc/dotfiles.git # ssh isn't configured yet
$ cd dotfiles && git submodule update --init
$ make run-minimal # does a bunch of necessary symlinking
$ export TOOLS_DIR=$HOME/tools
$ mkdir $TOOLS_DIR

Install yay

  • My favorite AUR package manager
  • Written in Go (I’d love to contribute)!
  • Actively maintained
  • Similar API to pacman (i.e., -S to install, -R to remove)
$ git clone https://aur.archlinux.org/yay.git $TOOLS_DIR/yay
$ cd $TOOLS_DIR/yay
$ makepkg -si

Install Video Drivers

Resource(s):

CRITICAL: xf86-video-intel caused video to be super super laggy in the next step!!!

$ pacman -S vulkan-intel vulkan-mesa-layer

Install i3 Window Manager (and some other helpful packages)

$ pacman -S \
  xorg \
  xorg-xclock \
  xorg-twm \
  xorg-xinit \
  xterm \
  i3 \
  tmux \
  adobe-source-code-pro-fonts
$ yay -S \
  brave-bin \
  ttf-font-awesome # used by the i3status bar

CRITICAL: Important keystrokes. Remember these for when you are in the X window system.

  • The Windows key is set as the i3 modifier key (Mod)
    • Mod+Shift+Backspace - locks the screen (just start typing to login)
    • Mod+Shift+r - reloads the i3 config
    • Mod+<ENTER> - opens a terminal
    • Mod+Backslash (i.e., \) - opens a browser
    • Mod+<TAB> - brings up an application fuzzy searcher

To see all i3 keystrokes, check out ~/.config/i3/config.

$ startx # BAM! No longer limited to a shell.

If your video is super laggy or only updates on mouse movement, the xf86-video-intel package is probably still installed. See the Install Video Drivers section.

$ pacman -R xf86-video-intel

If you have video and it’s not laggy, you’ve made it passed the second most difficult part! Final level unlocked. Time for the fun stuff (i.e., making it your own).

Install Drivers

Resource(s):

$ pacman -S xorg-drivers

After Device is Setup

Go ahead and reboot now with sudo reboot -h now.

Adjusting Font Sizes

Resource(s):

  1. To adjust them in GTK applications (e.g., Chrome, Brave Browser, etc.), edit the font size in ~/.config/gtk-3.0/settings.ini.

  2. To adjust them globally and the actual browser content, update the font size in ~/.Xresources, but more importantly, the Xft.dpi setting.

Examples:

Note: either export QT_SCALE_FACTOR=... or alias the commands to include the application-specific scale factor.

$ QT_SCALE_FACTOR=2 zoom

Or, more likely, you will have to update the command that gets run by /usr/local/share/application/<app-name>.desktop. To add user overrides, do the following:

$ export XDG_DATA_HOME="$HOME/.local/share"
$ cp /usr/local/share/applications/<application>.desktop $XDG_DATA_HOME

In order to prepend an environment variable — like above — you will have to do the following, as noted in the “Modify environment variables” section in this Arch Wiki page.

...
Exec=env QT_SCALE_FACTOR=2 /usr/bin/zoom %U
...

To read more about the XDG Base Directory, check out this post on the Arch Wiki.

Setup ssh for git clone

  1. Generate an SSH key
$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_rsa
$ xclip -sel clip < ~/.ssh/id_rsa.pub
  1. Add the SSH key to your GitHub account

Other Necessary Installs

Install vim-plug Plugin Manager for NeoVim

$ curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
$ nvim +PlugInstall +UpdateRemotePlugins +qall > /dev/null

tmux plugin manager

$ $(HOME)/.tmux/plugins/tpm/scripts/install_plugins.sh
$ tmux source $(HOME)/.tmux.conf
$ sudo npm i -g bash-language-server

To list packages on another Arch machine, run the following:

Official packages:

pacman -Qqne

AUR packages:

pacman -Qqme

Optional Installs

$ pacman -S \
  docker \
  wget \
  imagemagick \
  htop \
  pavucontrol

$ yay -S \
  neovim \          # a "better" Vim editor
  hugo \            # static site generate written in Go
  python-grip \     # GitHub-flavored markdown previewer
  pyhton-pip \
  terraform \
  google-cloud-sdk

Other Notes

Setting Default Applications

$ xdg-mime query default inode/directory

Fixing Zoom SSO

$ cat $HOME/.config/zoomus.conf
...
embeddedBrowserForSSOLogin=false
...

Then, the SSO link worked and I got a valid token, but the redirect back to open Zoom via the zoommtg protocol didn’t work. I checked my mimeapps.list and everything looked fine, so I just tried xdg-opening via the shell and everything worked!

$ xdg-open zoommtg://[blah].zoom.us/sso?token=[blah]

Screen Brightness

Resource(s):

This one ultimately solved it for me

If you find that changing the acpi_video0 backlight does not actually change the brightness, you may need to use acpi_backlight=none.

$ ls /sys/class/backlight/
intel_backlight

Updated /boot/loader/entries/arch-encrypted-lvm.conf from above to include acpi_backlight=none in the options.

...
options ... acpi_backlight=none quiet rw

Content Adaptive Brightness Control

Resource(s):

Since I am on the Dell XPS 13 (7390), disabling Content Adaptive Brightness was as easy as disabling it in the BIOS menu, under the Video settings.

Bluetooth Audio

$ bluetoothctl
$ (bluetoothctl) power on
$ (bluetoothctl) agent on
$ (bluetoothctl) scan on
$ (bluetoothctl) devices
$ (bluetoothctl) pair <MAC>
$ (bluetoothctl) connect <MAC>

Chrome Extensions

I wanted to also be able to install Chrome Extensions via the command line, but it appears that this is not possible according to this StackOverflow post. The Chrome Extensions that I use are as follows:

Additional Resources