Arch Linux X1 Carbon 6

View post history

I recently picked up a 6th gen X1 Carbon so of course I wanted to install Arch Linux on it. This post documents the steps I took in case I ever have to do this again. I used ejmg’s guide guide, HardenedArray’s gist guide, and the Arch Linux wiki page as references.

Note: This was my setup as of July 2020ish. Things have changed since then.


Prepare Installation Media

This part is relatively straightforward. Check out the arch wiki page.

Prepare BIOS

BIOS -> Security -> Secure Boot -> Disable BIOS -> Config -> Thunderbolt(TM) 3 -> Thunderbolt BIOS Assist Mode: Enabled

Configure boot order to boot off USB BIOS -> Startup -> Boot -> Move USB HDD to the top of the list (also moved USB FDD to 2nd since I wasn’t sure which one I needed

Plug in USB

Live Environment Setup

Connect to WiFi Network

I was able to get everything set up with iwctl. Once you’re in the iwctl prompt, use the help command to see available commands.

# iwctl
[iwd]# device list

# Shows devices installed. Mine was wlan0

[iwd]# station wlan0 get-networks

# Shows available networks

[iwd]# station wlan0 connect $SSID

# Wrap your SSID in quotes if it has spaces
# Enter passphrase when prompted

[iwd]# exit

Partition Drive

My device had two SSDs installed. lsblk showed them as nvme0n1 and nvme1n1. My primary SSD was nvme1n1 so I ran gdisk /dev/nmve1n1. You can enter ? to get a list of commands. I went ahead and deleted (d) all the existing partitions. Created an EFI partition (n) on partition 1 with a size of 100 MiB (chose first sector and then +100M for the last sector) with hex code EF00 (EFI partition). I created partition 2 to span the rest of the device. I tried having a separate boot partition but ran into issues getting my system to boot up properly. It’s probably possible to have a separate boot partition but it probably makes the setup more complex. So, unless you know what you’re doing, don’t create any other partitions on this drive.

For my second drive I ran gdisk /dev/nvme0n1 and left a single partition spanning the entire device with hex code 8300 (Linux FS). This drive can be partitioned however you like.

I should zero my devices but I’m not that paranoid so I didn’t. This could be done with ddrescue or with cat like so cat /dev/zero > /dev/nvme1n1 && cat /dev/zero /dev/nme0n1.

Setup filesystems

Encrypting Devices

Encrypt all partitions except for the EFI partition. This is done with cryptsetup’s luksFormat subcommand. luksFormat will prompt for a password. Do not forget these passwords or you’ll be locked out of your drives and be forced to reformat. The passwords don’t have to match. In fact, it’s better to have a unique password for each one but do not forget the passwords. Once the drives are encrypted, they need to be opened with the luksOpen subcommand. The last part of the luksOpen (EncryptedBoot and Secondary below) subcommand is just a label and can be any value (just be sure to remain consistent – these labels will be used later on).

These are the commands I ran:

cryptsetup -c aes-xts-plain64 -h sha512 -s 512 --use-random --type luks1 luksFormat /dev/nvme1n1p2
cryptsetup -c aes-xts-plain64 -h sha512 -s 512 --use-random --type luks1 luksFormat /dev/nme0n1p1
cryptsetup luksOpen /dev/nvme1n1p2 EncryptedBoot
cryptsetup luksOpen /dev/nvme0n1p1 Secondary

When I first tried setting this up I realized I had accidentally encrypted the EFI partition (saw an error when I tried to mount it later on). Fixing this is easy though, just close the partition with cryptsetup luksClose EncryptedBoot. Replace EncryptedBoot with whatever label was given (this can be checked with lsblk). Once the partition is closed, reformat it with FAT32 again (see the Create FileSystems section).


Use the Linux Volume Manager (LVM) to create a swap volume on the primary drive (labeled EncryptedBoot). Setup volumes for the secondary drive (labeled Secondary) while we’re at it.

pvcreate /dev/mapper/EncryptedBoot
vgcreate Arch /dev/mapper/EncryptedBoot
lvcreate -L 16G -n swap
lvcreate -l 100%FREE Arch -n root
pvcreate /dev/mapper/Secondary
vgcreate Data /dev/mapper/Secondary
lvcreate -l 100%FREE Data -n root

Create Filesystems

Create a FAT32 filesystem for the EFI partition, set up the swap partition, and format the rest with ext4.

mkfs.vfat -F 32 /dev/nvme1n1p1
mkswap /dev/mapper/Arch-swap
mkfs.ext4 /dev/mapper/Arch-root
mkfs.ext4 /dev/mapper/Data-root



Now that the drives are ready, the actual installation can begin. Mount the drives first.

mount /dev/mapper/Arch-root /mnt
swapon /dev/mapper/Arch-swap
mkdir /mnt/boot
mkdir -p /mnt/mnt/data
mount /dev/mapper/Data-root /mnt/mnt/data
mkdir /mnt/efi
mount /dev/nvme1n1p1 /mnt/efi

Install a base set of packages. More will be installed later on, this is just a minimal set of packages.

pacstrap /mnt base base-devel grub efibootmgr dialog wpa_supplicant linux linux-headers vim dhcpcd netctl lvm2 linux-firmware iwd
man-db man-pages

Note: Later on when I was configuring my network after Arch had been installed I realized I didn’t use netctl or dhcpcd. These can probably be left out. Not sure if wpa_supplicant needs to be installed here either. vim could be replaced with a different editor like emacs or nano.

One last step before chroot’ing into the Arch installation is to write an /etc/fstab file. This can be generated with genfstab.

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

Before continuing, review /mnt/etc/fstab and make any necessary changes (I didn’t need to make any changes but it’s a good idea to check). It’s finally time to chroot.

arch-chroot /mnt /bin/bash

The root is now the same as the Arch install’s root.


Find the local timezone in /usr/share/zoneinfo and set the system timezone.

ln -s /usr/share/zoneinfo/America/Los_Angeles /etc/localtime

Set the hostname. I decided on naming my computer carbon.

echo carbon > /etc/hostname

Set the locale. Go through /etc/locale.gen and uncomment the relevant lines. I only uncommented en_US.UTF-8 UTF-8. After that, generate localization files.

echo LANG=en_US.UTF-8 > /etc/locale.conf

Set the root password and create a user account (bad practice to run as root).

useradd -m -G wheel -s /bin/bash alejandro

Replace alejandro with your username. sudo will later be configured to allow users in the wheel group.

More Encryption Configuration

When the system boots up, the bootloader (I’ll be using grub) will need to read /boot and the system will need access to any other volumes specified in the fstab file. Without any extra configuration, there will be a passphrase prompt for every volume. LUKS devices have multiple “key slots.” It’s possible to use a key file to fill in one of the key slots and later pass that file in to open (decrypt) a LUKS device. This makes it possible to have grub handle decryption of root and swap without requiring the user to enter multiple passphrases (which is clunky and error-prone). Other volumes (my data root volume) can be configured in /etc/crypttab (similar to /etc/fstab) to also be automatically opened.

Generate a random keyfile.

cd /
dd bs=512 count=4 if=/dev/random of=crypto_keyfile.bin iflag=fullblock

This keyfile should never be shared. In fact, no user should have access to this file. The arch wiki warns that initramfs’s permissions should be set to 600 as well.

chmod 000 /crypto_keyfile.bin
chmod 600 /boot/initramfs-linux*

Add the keyfile to the LUKS devices.

cryptsetup luksAddKey /dev/nvme1n1p2 /crypto_keyfile.bin
cryptsetup luksAddKey /dev/nvme0n1p1 /crypto_keyfile.bin
# Use the commands below to verify the keyfile has been added.
cryptsetup luksDump /dev/nvme1n1p2  # Should see slots 0 and 1 occupied
cryptsetup luksDump /dev/nvme0n1p1  # Should see slots 0 and 1 occupied

Configure automatic opening of the data volume through crypttab. Edit /etc/crypttab

# SNIP ...
# <name>       <device>                                     <password>              <options>
Secondary      /dev/nvme0n1p1                               /crypto_keyfile.bin     discard
# SNIP ...

The discard option has to do with the TRIM command and is basically a performance optimization. Read more about it on wikipedia.

Edit the mkinitpcio configuration file (/etc/mkinitpcio.conf) to setup decryption.

# SNIP ...
# SNIP ...
HOOKS=(base udev autodetect modconf block keymap encrypt lvm2 resume filesystems keyboard fsck)

Generate the initrd image.

mkinitpcio -p linux

grub now has to be configured so it knows /boot is encrypted. Uncomment the GRUB_ENABLE_CRYPTODISK=y line in /etc/default/grub. Once that’s done grub can be installed.

grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=ArchLinux

Open up /etc/default/grub again and edit the GRUB_CMDLINE_LINUX line so it looks like this GRUB_CMDLINE_LINUX=cryptdevice=/dev/nvme1n1p2:EncryptedBoot:allow-discards resume=/dev/mapper/Arch-swap.

The allow-discards option also has to do with TRIM. Now the grub configuration is ready to be generated.

grub-mkconfig -o /boot/grub/grub.cfg

That’s it. The system should now be bootable. Exit, reboot, and pray.

umount -R /mnt
swapoff -a

You should be prompted for your passphrase once. If you get the passphrase wrong you’ll be dropped into grub rescue mode. Hit ctrl+alt+delete and try again (or reboot by holding down the power button if that doesn’t work). Don’t be frustrated if this doesn’t work on the first try. There are a lot of steps in setting this up and mistakes happen (I didn’t get this right at first either).

First Logon

Log in to your system as root and allow users in the wheel group to use sudo. Run visudo, if you get an error saying no editor found just prepend the editor’s path like this EDITOR=/usr/bin/vim visudo. Uncomment the following line %wheel ALL=(ALL) ALL. You can log out and log in with your own user account now.

Setup WiFi

iwd can be used to manage the network with the proper configuration. Edit /etc/iwd/main.conf



The EnableNetworkConfiguration setting allows iwd to handle stuff like DHCP. The NameResolvingService configures DNS. I decided to use systemd-resolved mostly just because I already had it installed (part of the systemd package). Enable and start systemd-resolved and iwd.

systemctl enable systemd-resolved
systemctl enable iwd
systemctl start systemd-resolved
systemctl start iwd

Follow the same steps as before to connect to wifi (run iwctl).

Install Additional Packages

The default mirrorlist was kept earlier but reflector can be used to choose mirrors. The reflector command below will filter the 200 most recently updated https servers and choose the 200 fastest ones.

pacman -S reflector
reflector --verbose -l 200 -n 20 -p https --sort rate --save /etc/pacman.d/mirrorlist

A pacman hook can be setup to automatically run reflector when pacman-mirrorlist is updated (this package contains the official mirrorlist). Create /etc/pacman.d/hooks/mirrorupgrade.hook

Operation = Upgrade
Type = Package
Target = pacman-mirrorlist

Description = Updating pacman-mirrorlist with reflector and removing pacnew...
When = PostTransaction
Depends = reflector
Exec = /bin/sh -c "reflector -l 200 -n 20 -p https --sort rate --save /etc/pacman.d/mirrorlist; rm -f /etc/pacman.d/mirrorlist.pacnew"

Make sure everything is up-to-date.

sudo pacman -Syyu

Some packages are only available from the Arch User Repository (AUR). pacman won’t handle these packages, but there are AUR helpers that can. Install yay.

sudo pacman -S git
cd ~
git clone
cd yay
makepkg -si
# clean up
cd ..
rm -rf yay

Set up ntp (keep time synchronized).

yay -S ntp
sudo systemctl ntpd enable --now

Set up zsh.

yay -S zsh oh-my-zsh-git
zsh  # runs setup
chsh -s /usr/bin/zsh  # set zsh as default shell
cp /usr/share/oh-my-zsh/zshrc ~/.zshrc

I normally use i3 but I’ve been wanting to switch to wayland so I went with sway since it’s the closest thing (the about section on GitHub bills it as an “i3-compatible Wayland compositor”).

yay -S sway swaylock swayidle waybar xorg-server-xwayland
# Can probably leave the 2 lines below out
mkdir -p ~/.config/sway
cp /etc/sway/config ~/.config/sway
mkdir -p ~/.config/waybar
cp /etc/xdg/waybar/* ~/.config/waybar

I edited my sway config to mimic my i3 config so I needed to grab a few packages first.

yay -S termite bemenu-wlroots

termite is the terminal emulator that I’m used to an I used bemenu as an alternative to dmenu.

With that out of the way, I started up sway and realized I still needed a web browser.

yay -S firefox

Firefox wouldn’t run when I tried to start it. I came to find out that Firefox’s wayland support needs to be enabled so I updated my ~/.zprofile with an environment variable to enable wayland support in Firefox.

echo "export MOZ_ENABLE_WAYLAND=1" >> ~/.zprofile

After restarting sway, I was able to run Firefox. I ran into my next issue (seems like a recurring theme) soon after. Everything on the screen seemed too big. The scaling factor for my display was too large (first world problem, I know). Luckily for me sway supports (but doesn’t recommend) fractional scaling. I got my display’s name using swaymsg.

swaymsg -t get_outputs

Then I added a line to my sway config to set a custom scaling factor: output eDP-1 scale 1.75. eDP-1 is the name of my display, as reported by swaymsg.

Next thing I wanted to fix were the fonts. I didn’t like the current ones so I installed all the [nerd fonts](https://ww( When I looked at the AUR page for nerd-fonts-complete there was a pinned comment that suggested grabbing the tarball manually since it was so large (~2GB).

yay -S wget
mkdir -p ~/.local/share/fonts
ln -s /usr/lib/nerd-fonts-complete/*.sh ~/.local/share/fonts/
echo source ~/.local/share/fonts/ >> ~/.zshrc


yay -S mako libnotify
add line to sway config

Terminal Themes

I use themes defined in base16-shell.

git clone ~/.config/base16-shell

Follow set up directions in the repo. I use base16_darktooth.


I use pulseaudio

yay -S pulseaudio pulseaudio-alsa pamixer pulseaudio-bluetooth

Spotify TUI

Spotify can be controlled from the terminal using spotify-tui and spotifyd. Doesn’t have all the features as the official client but it’s

yay -S spotify-tui spotifyd

Set up keyring to use with spotify

yay -S gnome-keyring libsecret  # seahorse too?, not sure how to manage purely from cli

Edit /etc/pam.d/login to add in auth optional and session optional auto_start. Mine looks like this.


auth       required
auth       requisite
auth       include      system-local-login
auth       optional
account    include      system-local-login
session    include      system-local-login
session    optional auto_start

Update the passwd file to include password optional

We need to run the following when sway starts.

eval $(/usr/bin/gnome-keyring-daemon --start --components=pkcs11,secrets,ssh)

Normally this would be added in ~/.xinitrc but there isn’t (afaik) a wayland equivalent. So I created a start script for sway.

eval $(/usr/bin/gnome-keyring-daemon --start --components=pkcs11,secrets,ssh)

Store spotify password in keystore secret-tool --label='Spotify' application rust-keyring service spotifyd username <your-username>. You’ll be prompted to create a default keyring if one hasn’t already been created.

Create systemd unit file and run spotifyd

mkdir -o ~/.config/systemd/user/
get -O ~/.config/systemd/user/spotifyd.service
systemctl --user start spotifyd.service  # do not run these two with sudo
systemctl --user enable spotifyd.service

Run spt and it’ll guide you through setup. See the their readme for instructions.

git clone --separate-git-dir=$HOME/.myconf /path/to/repo $HOME/myconf-tmp
rm -r ~/myconf-tmp/
alias config='/usr/bin/git --git-dir=$HOME/.myconf/ --work-tree=$HOME'  # Add this into .bashrc/.zshrc

Backlight Control

yay -S light
usermod -a -G video alejandro  # need to be in video group to control backlight
# below 2 reload udev rules, so light doesn't require root permissions
sudo udevadm control --reload-rule
sudo udevadm trigger
# Above 2 commands didn't work for me, but did after a reboot
# installed wshowkeys and used it to figure out what keys to bind to light commands in sway config

Terminal prompt

yay -S zsh-theme-powerlevel10k-git
echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >> ~/.zshrc

Restart terminal and p10k config wizard will run (or manually run p10k configure)

Vim plugin manager

mkdir -p ~/.vim/bundle
git clone ~/.vim/bundle/Vundle.vim

Power Management

Read this for power stuffs:

yay -S throttled
systemctl enable --now lenovo_fix.service

Network printer

yay -S cups
systemctl enable --now org.cups.cupsd.service
yay -S nss-mdns avahi
systemctl enable --now avahi-daemon

Update /etc/nsswitch.conf to include hosts: ... mdns_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns ...

Browse to localhost:631 to configure printer (Brother HL-L2350 for me) (yay -S brother-hll2350dw) (yay -S ghostscript)


yay -Q python-pip
pip install --user qmk
qmk setup
# CD into qmk directory
make crkbd:default

Edit /etc/udev/rules.d/55-caterina.rules

# ModemManager should ignore the following devices
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0036", TAG+="uaccess", RUN{builtin}+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0036", TAG+="uaccess", RUN{builtin}+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9205", TAG+="uaccess", RUN{builtin}+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9203", TAG+="uaccess", RUN{builtin}+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
sudo udevadm control --reload-rules
sudo udevadm trigger
qmk flash

Had to reboot before this worked for me. Because of avrdude? qmk doctor showed udev rules were setup. Had to add user to uucp group to write to device.