Switching to Linux

by Anthony Greenberg, posted on Apr 6, 2019


I have been using Apple computers close to full time since mid-90’s. Originally because I used Photoshop extensively for processing confocal microscope images and Macs were the standard platform for that. It helped that they were also relatively safe from viruses and generally stable. I switched to more computational work in the early 2000’s, and Apple was right there with Mac OS X, which still is a great interface to a Unix-like system. I have owned an Apple laptop continuously since around 2000.

For me, the extra I had to pay for these laptops has been a good value for money. For one, the Apple premium was not that high given the specs. In addition, the quality of build, reliability, and excellent customer service were worth paying extra for. I also like the Apple design approach. My job involves writing open-source software for scientific applications. It has to be easily transferable to servers that usually run some version of Linux. Most of my colleagues, who are the target audience for my development, use Macs or Linux. The Unix flavor provided by Mac OS allows me write my code for any flavor of Unix, but there have always been pain points. For example, Apple moved to LLVM as the C/C++ compiler (I am a C++ developer), but the version they include is pretty old. It is possible to install GCC and most other things via third-party package managers (like Homebrew), but things do break every once in a while. Apple has always aimed for a tight integration of software and hardware, which is great for its users but not for portability. In short, while it is quite possible to use Macs for open-source development, this is not what Macs are designed for.

I put up with the (relatively minor) annoyances I was experiencing with Mac OS as a developer platform for two reasons: (1) I really liked the hardware and (2) I had software needs that were not directly related to development work but were required for other aspects of my job. On the hardware side, the Apple Retina displays changed my life. I have to stare at text on the screen most of the day, and the sharpness delivered by Retina very noticeably reduced eye fatigue for me. The other specs I care about (processor speed and RAM) have been good enough, and I do not use many external devices so was never upset about Apple’s port shenanigans. On the software side, my colleagues (and, now that I am out on my own, my clients) use Microsoft products extensively. I have to be able to look at and edit these files. I occasionally need to make vector-graphic illustrations. I used to use the Illustrator from Adobe, but switched to Graphic, which is a lot cheaper and does everything I need (I am not really a power user). I use desktop replacement laptops because I work from different locations. I can do 95% of my computational work on a high-end laptop, and off-load to a server or AWS if a need for some really serious data processing arises.

Switching to System76

Over the past few years, many other manufacturers have started to catch up to Apple laptops in design and specs. Apple, on the other hand, seems focused on thinness and lightness. For my use case this is close to irrelevant. The price I have to pay now for a high-end MBP is becoming hard to justify. I am also thinking of adding GPU computation to some of the code I write. Apple’s GPU offerings are really not up to scratch for these purposes. The software that was holding me back from switching to Linux is available on the iPad. I have the 11" iPad Pro, a great device for editing Word documents, PDFs, or making illustrations. I also use it for most of my e-mail and reading. Although I been using BBEdit for years as the scripting editor, I have switched to Vim a couple of years ago. Vim blows BBEdit out of the water for my needs. So when it came time to upgrade my 2015 15" MBP (still one of the best machines ever made), I decided to try out the System76 Oryx Pro. Some of my colleagues tried other System76 laptops and seemed generally happy with them. I ordered the 15" version with the HiDPI display, 32G of RAM and 250G NVME drive. I added my own 1TB SSD to the extra slot. I did not need to get an extra cable, everything was ready to just plug the drive in and it worked.

Overall, having used this laptop full time since the end of November, I am very happy with it. There are small manufacturing glitches here and there, something I notice because I am spoiled by Apple’s obsessive perfectionism in industrial design, but no deal-breakers. The keyboard is amazing, and has fun back-light options. The only minus is that there is no indicator light on the caps lock. It feels very good to type on, better than any laptop keyboard I have personally tried (even the excellent ThinkPad keyboard). The trackpad is OK, Apple is way ahead of everyone on that, but I use a trackball most of the time. I note that the Oryx Pro line-up recently changed and they no longer have a HiDPI screen offering. That is too bad, the display is excellent. Although I am not a big user of various ports, it is nice to have the wide range of them available (even an ethernet port!). Battery life is not great, especially when using the GPU, but that is not a concern for me. It is enough to last through a meeting with a client, otherwise I have it plugged in.

System configuration

System76 laptops come with a choice of Ubuntu or their own Ubuntu front-end they call Pop! OS. Pop! OS is well-designed, but after distro-hopping for a while I decided to go with Arch. My main reason was to educate myself about how Linux works. Since I have been on Mac OS X since its beginning, I feel fairly confident about fixing anything that goes wrong. I only needed to contact Apple support over a system software issue once. I noticed that most of my queries about Linux ended up at the Arch wiki, so I figured I should just install the thing rather than try and figure out how the answers there apply to Ubuntu. Below is the short version of the instructions to install Arch on the Oryx Pro. The full instructions are here, including details on how to compile R with Intel’s MKL library for fast linear algebra. I like open source software, and release source code of everything I write, but I am not super ideological about it. I am happy to use free offerings from Intel and NVIDIA.

I want to have full-disk encryption, with the exception of the boot partition and I am installing with WiFi. There is a discrete NVIDIA graphics card. I would like to use it for OpenCL/CUDA and for an external monitor.

Preliminaries

I used the archlinux-2019.01.01-x86_64.iso, put it on a USB flash drive using dd (standard procedure).

Inserted the USB drive, booted while pressing F7 on boot to enter the boot disk picker. Once there, press e to enter kernel command line options. Add video=1920x1080 to enlarge the console fonts (I have the 4K screen version of the Oryx Pro and the default resolution makes the letters tiny) and module_blacklist=nouveau to switch off the NVIDIA GPU for now. The commands should be separated by space and entered at the end of the line. Switching off the nouveau driver is necessary, otherwise any hardware listing (such as lspci) will hang with fans blazing. The WiFi card has functional firmware, checked by running

1
2
lspci -k
dmseg | grep iwlwifi

List WiFi networks, pick the relevant and follow prompts to connect:

1
wifi-menu

Set up time and date.

1
2
timedatectl set-ntp true
timedatectl set-timezone America/New_York

I want to use LVM on LUKS to get full-disk encryption, including the SWAP. /boot will be unencrypted.

Disk preparation

Partition the target disk (here, it is /dev/sda).

To list partitions:

1
fdisk -l

System76 requires the EFI partition to be in /boot so that it can do firmware updates. I leave the secondary disk alone. It is already formatted and has data on it. I use gdisk to set up GPT.

1
gdisk /dev/nvme0n1

p to list current partitions o to delete them all and create an empty GPT partition table n to create new

p to check if everything looks sane w to write (THE DISK WILL BE ERASED AT THIS POINT)

Format the EFI boot partition (left unencrypted):

1
mkfs.fat -F32 /dev/nvme0n1p1

Create the non-boot file systems. The following will require coming up with a passphrase. I followed the instructions in Encrypting an entire system.

Encrypt the future LVM container:

1
2
cryptsetup luksFormat —-type luks1 /dev/nvme0n1p2
cryptsetup open /dev/nvme0n1p2 cryptlvm

I have to use luks1 because GRUB does not support luks2 as of this writing. That is not an issue in the ThinkPad set-up, as far as I can tell because I am booting from BIOS there rather than EFI. Prepare the logical volumes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
pvcreate /dev/mapper/cryptlvm
vgcreate MainVolGroup /dev/mapper/cryptlvm
lvcreate -L 16G MainVolGroup -n swap
lvcreate -l 100%FREE MainVolGroup -n root

mkfs.ext4 /dev/MainVolGroup/root
mkswap /dev/MainVolGroup/swap

mount /dev/MainVolGroup/root /mnt
swapon /dev/MainVolGroup/swap

Mount the EFI boot partition:

1
2
3
mkdir /mnt/boot
mkdir /mnt/boot/efi
mount /dev/nvme0n1p1 /mnt/boot/efi

Installation

Install Arch:

1
pacstrap /mnt base base-devel python gvim git grub dhclient networkmanager gnome-keyring efibootmgr

Generate fstab:

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

Edit /mnt/etc/crypttab to add a line

1
cryptlvm	UUID=_/dev/nvme0n1p2 UUID_	none	luks,discard,timeout=5

The discard option enables TRIM support. There are security implications, but not serious enough for my use case. Read the linked documentation to decide for yourself. An easy way to transfer the UUID without typing it is to do

1
blkid -pi /dev/nvme0n1p2 | cut -d' ' -f3 >> /mnt/etc/crypttab

and edit the crypttab file to make it correct, or use :read in vim.

There is an issue with GRUB and LVM which causes grub-mkconfig to hang and grub-install to keep probing LVM devices. For the workaround, prepare the following:

1
2
mkdir /mnt/hostlvm
mount --bind /run/lvm /mnt/hostlvm

Configuration

Move into the fresh Arch installation on the main disk. Note that the paths will no longer require /mnt in front.

1
arch-chroot /mnt

To deal with the GRUB/LVM problem, run

1
ln -s /hostlvm /run/lvm

Edit /etc/mkinitcpio.conf (this is now on the target drive):

1
2
3
HOOKS=(base udev autodetect **keyboard** **keymap** modconf block **encrypt** **lvm2** **resume** filesystems fsck) # resume is included for suspend to disk support

mkinitcpio -p linux

Use blkid to list UUIDs of devices. Edit /etc/default/grub to modify variables. Append “lvm” to GRUB_PRELOAD_MODULES. Uncomment the GRUB_ENABLE_CRYOTDISK=y line. Append cryptdevice=UUID=UUID-of-/dev/nvme0n1p2:cryptlvm root=/dev/MainVolGroup/root resume=/dev/MainVolGroup/swap ec_sys.write_support=1 video=1920x1080 module_blacklist=nouveau to GRUB_CMDLINE_LINUX_DEFAULT. The resume=... part is for suspend to disk support. Install GRUB on EFI:

1
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ArchLinuxGRUB

Make the GRUB config:

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

Activate NetworkManager:

1
systemctl enable NetworkManager

Time zone and locale specification (I am in New York, yours may be different).

1
2
ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime
hwclock --systohc

Uncomment all en_US entries in /etc/locale.gen.

1
locale-gen

Set LANG=en_US.UTF-8 in newly created /etc/locale.conf.

Network settings:

Create /etc/hostname and write nerv there (that is the name I gave the laptop; substitute what you like). Edit /etc/hosts to add

1
2
3
127.0.0.1	localhost
::1			localhost
127.0.0.1	nerv.localdomain nerv

Exit chroot, optionally unmount -R /mnt, and shut down the computer. Remove the USB drive and start the laptop again.

Driver installation

Start by connecting to WiFi

1
2
nmcli device wifi list
nmcli device wifi connect *SSID* --ask

and enter the WiFi password (if any) at the prompt.

Start by installing the NVIDIA drivers:

1
pacman -S nvidia nvidia-utils nvidia-settings

The System76 drivers are available from AUR. Cannot install these using root, so I generate my root password and add a regular user first.

1
2
3
passwd
useradd -m -g wheel *username*
passwd tonyg

To enable sudo for the user, uncomment the %wheel ALL=(ALL) ALL line in /etc/sudoers. I like to reboot etc without entering a password, so I also add %wheel ALL=(ALL) NOPASSWD: REBOOT. I add /sbin/shutdown to the REBOOT alias. Reboot the laptop. Login as the regular user and proceed with driver installation. First install kernel headers:

1
pacman -S linux-headers

Now install the kernel modules (mostly following the instructions here):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
git clone https://aur.archlinux.org/system76-dkms.git
cd system76-dkms
makepkg -sci
modprobe system76
cd
rm -rvf system76-dkms

git clone https://aur.archlinux.org/system76-firmware-daemon.git
cd system76-firmware-daemon
makepkg -sci
systemctl enable --now system76-firmware-daemon.service
cd
rm -rvf system76-firmware-service

git clone https://aur.archlinux.org/system76-io-dkms.git
cd system76-io-dkms
makepkg -sci
modprobe system76-io
cd
rm -rvf system76-io-dkms

git clone https://aur.archlinux.org/system76-driver.git
cd system76-driver
makepkg -sci
cd
rm -rvf system76-driver

git clone https://aur.archlinux.org/system76-power.git
cd system76-power
makepkg -sci
systemctl enable --now system76-power.service
cd
rm -rvf system76-power

NVIDIA set-up

I mostly follow the instructions here. I activate the NVIDIA card

1
sudo system76-power graphics nvidia

I add the file 10-nvidia-drm-outputclass.conf to /usr/share/X11/xorg.conf.d. This file contains

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Section "OutputClass"
 Identifier "intel"
 MatchDriver "i915"
 Driver "modesetting"
EndSection

Section "OutputClass"
 Identifier "nvidia"
 MatchDriver "nvidia-drm"
 Driver "nvidia"
 Option "AllowEmptyInitialConfiguration"
 Option "PrimaryGPU" "yes"
 ModulePath "/usr/lib/nvidia/xorg"
 ModulePath "/usr/lib/xorg/modules"
EndSection

I also put the following script in /etc/lightdm to check if the NVIDIA card is on at startup, and add an external monitor if it is connected.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/sh

if system76-power graphics | grep -q 'nvidia'; then
	xrandr --setprovideroutputsource modesetting NVIDIA-0
	xrandr --output eDP-1-1 --auto --primary --dpi 144
	if [ $( xrandr | grep HDMI | cut -d ' ' -f2 ) == "connected" ]; then
		output=$( xrandr | grep HDMI | cut -d ' ' -f1 )
		xrandr --output $output --auto --left-of eDP-1-1
	fi
fi

I keep the screens (I have a 4K external monitor, and the HiDPI screen on the laptop) at the full resolution, but enlarge fonts and follow some suggestions on the Arch HiDPI wiki page. I am really happy with the text rendering with this set-up. Running the screens at 1080p made everything larger, but noticeably fuzzier.

I am running the i3-gaps fork of the i3 tiling window manager, with LightDM. My dotfiles are here. Things that work on this set-up:

Things I have not tested:

I heavily use Vim, with YouCompleteMe for C++ and some of my own keybindings for Sweave code in R and for LaTeX. For the latter two, I like to have a PDF window (using zathura) side-by-side with the code. The zathura PDF viewer auto-updates when the file on disk changes, so I can see the type-set document essentially in real-time. The tiling window manager makes it easy to arrange the windows and the HiDPI screen ensures that all the text readable and crisp.

To cap everything off, here is the obligatory screen shot:

Oryx Pro Screenshot