Running Arch Linux on the Framework Laptop 13
This article sums up why and how I run Arch Linux on my new Framework Laptop 13, which I received on the 3rd of this month.
I’ve been busy getting up+running and getting to know the device. TL;DR; I’m extremely impressed and very happy with this laptop - it is by far one of the best devices I’ve owned in a long time. It is near-silent, It uses ~3watts at idle, it’s fast, it’s sturdy and it looks good!
This is going to be quite a long article, so here’s a Table of Contents:
- Why Arch Linux
- BIOS settings
- Basic installation
- Packages I install
- Additionally install when you want dynamic application of power management settings
- Additionally install when you want to use dracut instead of mkinitcpio
- Additionally install when you use X11 instead of Wayland and want gestures
- Additionally install on devices with fingerprint reader
- Additionally install on devices with Intel graphics
- Additionally install on devices with AMD graphics
- Additionally install on devices with NVidia graphics
- Installing lib32 package equivalents (optional)
- Service configuration
- How I use AUR
- Final thoughts
Why Arch Linux
Prior to my Framework Laptop adventures, I’ve been planning to move to Arch Linux for a while. I’m a long-time Linux desktop user. I started out with those Red Hat CD-ROMs you’d buy at your local bookshop (this was around ‘96). I fooled around with SCO UnixWare, had a whole period of SGI IRIX after that and then some distro-hopping to Debian, Fedora, Arch Linux (in 2007), Gentoo and Void Linux, more-or-less in that order.
The last two-ish years I’ve been running on Void Linux, which I still strongly recommend and love - it’s a really good distribution with a nice balance between stability and simplicity.
I used the OS package manager for the essentials, like Gnome, Firefox, the
terminal, Wayland and X11, and used /opt
like a sort of Program Files
or
Applications
directory where I had my own programs like IntelliJ, Postman,
PostgreSQL, etc.
This works relatively well: you don’t have a lot of demands on package availability in the distro itself and you keep things stable - only update your own stuff when you feel like it and/or when you need to.
At a certain point I had about 70 applications in /opt
which became a burden
to keep up to date, so I set out to find a distribution that packages as much
as possible of the software I use, and where packaging it yourself is as simple
as possible.
I definitely prefer a rolling-release distro. I checked out Nix, Gentoo and Arch Linux. Nix I found really appealing (hard declarative) but I hated the syntax (maybe Guix one day? I like Scheme better).
Gentoo did have a comparable set of packaged software available, and it is one of my favorite distributions, but I had too many issues on my test-build virtual machine which blocked me from experimenting prior to running it on my daily driver.
So why move to Arch? Well, in one word: mind-share. The wiki, the amount of packaged software, sane package standards:
- The Arch wiki has essentially become the de-facto standard Linux wiki
- Really huge package collection, fantastic community participation with AUR
- Upstream stable usually means an upstream update is in repos within minutes
- Does not have separate dev/devel packages for headers
The above three things means a lot of convenience to a Linux desktop user. On Arch Linux, 99.9% of the software I ever used on Linux is packaged, most of it in the official repositories and a few in AUR. Next to that, AUR and PKGBUILD are so easy to get into, so you can easily package things yourself and share it with the community.
On most other distros the gap between what’s packaged and what isn’t is quite a bit larger which means you need alternative ways to get that software and keep it up-to-date, which in practice becomes error-prone and time-consuming.
On Arch Linux I made the choice to run everything packaged with pacman
. If
I need something that isn’t packaged yet (currently the case for 5 packages of
which I packaged one already), I package it and publish it to AUR.
The rest of this article is essentially a how-to with notes about what I run and install. Feel free to peruse and/or replay!
BIOS settings
You can find the BIOS guide on the Framework community website. The only settings I changed were:
- CPU Configuration -> Boot Performance mode: MAX BATTERY
- CPU Configuration -> Intel Turbo Boost Max Technology 3.0: DISABLED
- Secure Boot -> Enforce Secure Boot: DISABLED
The above causes the laptop to effectively run in a lower TDP setting. I think
it goes from 28watts to 22watts. This has a dramatic effect on the battery life
and thermals of the device. I don’t want a wild beast that blasts the fans as
soon as I type ls
, so these settings are great for me. Did I mention the
laptop performs really great with these settings?
With the above and the settings I configure in TLP I get ~3watts idle with screen on normal brightness, Wi-Fi and Bluetooth enabled, and about ~45 celsius core temperatures.
Basic installation
Basically, download the ISO and follow the
installation guide. I
opted to use systemd-boot
as boot manager and systemd-networkd
as my network
configuration tooling.
Additional kernel parameters I use
Whichever boot manager you use, you might want to set a few extra kernel parameters. I found the following additionals handle a bunch of stuff nicely on my Framework Laptop 13:
net.ifnames=0 libata.allow_tpm=1 module_blacklist=cros_ec_lpcs,hid_sensor_hub acpi_osi="!Windows 2020" tpm_tis.interrupts=0 nvme.noacpi=1 mem_sleep_default=s2idle split_lock_detect=off
Packages I install
Next to the default core
and extra
repositories, I enable multilib
in
pacman.conf
before installing. Additionally, I blacklist pam_systemd_home.so
because I don’t use systemd-homed
and it spams the journal on any login or
auth action:
--- pacman.conf.orig 2023-08-06 12:09:44.849855338 +0300
+++ pacman.conf 2023-08-06 12:01:30.261984035 +0300
@@ -26,7 +26,7 @@
#IgnoreGroup =
#NoUpgrade =
-#NoExtract =
+NoExtract = usr/lib/security/pam_systemd_home.so
# Misc options
#UseSyslog
@@ -87,12 +87,11 @@
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist
-#[multilib]
-#Include = /etc/pacman.d/mirrorlist
+[multilib]
+Include = /etc/pacman.d/mirrorlist
You can install the following packages right after your system comes up first
boot, or you could do it during install with pacstrap
(it doesn’t really
matter, but in any case, make sure core
, contrib
and multilib
are enabled
in /etc/pacman.conf
first):
pacman -S --needed acpi acpi_call-dkms acpid alacritty alsa-utils ansible-language-server ansible-lint ant ardour aribb25 arj autoconf automake aws-cli bash bash-language-server bc bear bind bison blender btop bubblewrap calibre cameractrls carla cdemu-client cdemu-daemon cdrdao cdrtools cifs-utils clang clinfo clojure cmake corkscrew cpio cppcheck cpupower ctags cue cups curl dagger dash dconf-editor dcraw ddcutil debugedit delve deno desmume devtools direnv discord distrobox dive dmidecode dmraid dnsmasq docker docker-buildx docker-compose dool dos2unix dosfstools dotnet-sdk dvd+rw-tools editorconfig-core-c efibootmgr elixir emacs-wayland erlang eslint ethtool evince exfatprogs extra-cmake-modules fakeroot fastfetch fd file file-roller fio firefox flex foomatic-db-engine foomatic-db-nonfree-ppds foomatic-db-ppds fop fractal fs-uae fs-uae-launcher furnace fwupd fzf gamemode gcc gdb gdm ghidra gimp git git-filter-repo git-lfs glab gnome-backgrounds gnome-browser-connector gnome-calculator gnome-characters gnome-control-center gnome-disk-utility gnome-session gnome-settings-daemon gnome-shell gnome-shell-extensions gnome-system-monitor gnome-themes-extra gnome-tweaks gnupg go gopls go-tools gparted gradle groovy guile gvfs gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb handbrake harfbuzz-cairo hdparm helix helm hplip htop i2c-tools ifuse inkscape iperf3 iptables-nft irssi iwd jfsutils jq k9s kafka kubectl leiningen lhasa libblockdev-crypto libblockdev-dm libblockdev-fs libblockdev-loop libblockdev-lvm libblockdev-mdraid libblockdev-mpath libblockdev-nvme libblockdev-part libblockdev-swap libebur128 libgoom2 libindicator-gtk3 librecad libreoffice-still libretro-beetle-pce libretro-beetle-psx-hw libretro-blastem libretro-core-info libretro-desmume libretro-dolphin libretro-flycast libretro-mame libretro-mgba libretro-mupen64plus-next libretro-nestopia libretro-pcsx2 libretro-picodrive libretro-ppsspp libretro-sameboy libretro-scummvm libretro-snes9x libretro-yabause libtiger libva-mesa-driver libva-utils libvirt libxcrypt-compat linux-firmware-bnx2x linux-firmware-liquidio linux-firmware-mellanox linux-firmware-nfp linux-firmware-qlogic linux-headers live-media lldb lm_sensors loupe lshw lsof lsscsi ltrace lua-language-server make mame mame-tools man-db mangohud man-pages marksman mattermost-desktop maven mbedtls2 mednafen mesa-utils mesa-vdpau meson mgba-qt minikube mitmproxy mono mono-msbuild moreutils mplayer mtools multipath-tools mupdf-tools mupen64plus mutter nasm nautilus neovide neovim netbeans net-tools nfs-utils ninja nmap nodejs npm ntfs-3g nuget nvchecker nvme-cli nvtop openbsd-netcat opencl-clhpp opencl-headers openldap openssh openvpn osv-scanner p7zip pacman-contrib pacutils papirus-icon-theme patch patchelf pciutils perf perl perl-lwp-protocol-https perl-net-dbus perl-x11-protocol pinentry piper pipewire pipewire-alsa pipewire-jack pipewire-pulse pipewire-v4l2 pkgconf postgresql powertop ppsspp projectm psutils python python-jsbeautifier python-kubernetes python-ldap python-lsp-server python-nose python-opengl python-pip python-pycryptodomex python-pylint python-pyopenssl python-pytest python-rope python-setuptools python-websockets python-wheel qbittorrent qemu-full qjackctl qmc2 qpwgraph qt5ct qt5-declarative qt5-tools qt5-wayland qt5-webchannel qt5-webengine qt6ct qt6-multimedia-ffmpeg qt6-tools qt6-wayland quodlibet rabbitmq racket realtime-privileges retroarch retroarch-assets-glui retroarch-assets-ozone ripgrep rlwrap rsync ruby ruby-rake-compiler rustup samba sane sbt scons screen scummvm sdcc sdl2_mixer seahorse signal-desktop simple-scan smartmontools smbclient snapshot snes9x-gtk speedtest-cli squashfs-tools steam step-ca step-cli stern strace s-tui stylelint sudo syncthing sysprof sysstat tar terminus-font texinfo texlab texlive-basic texlive-bin the_silver_searcher thunderbird tidy tinyxxd tmux traceroute tracker3-miners tree tree-sitter-cli tree-sitter-grammars ttf-ibm-plex ttf-joypixels ttf-ubuntu-font-family typescript typescript-language-server udftools udisks2-lvm2 uncrustify unixodbc unzip urlwatch usbutils util-linux v4l-utils valgrind vdpauinfo vhba-module-dkms virt-manager vscode-css-languageserver vscode-html-languageserver vscode-json-languageserver vulkan-tools w3m wayland-utils wgetpaste whois wine wine-mono winetricks wireless_tools wireplumber wireshark-cli wireshark-qt wl-clipboard wmctrl wol xchm xclip xdg-desktop-portal-gnome xdg-user-dirs-gtk xdg-utils xdotool xfsprogs xorg-fonts-100dpi xorg-fonts-misc xorg-font-util xorg-mkfontscale xorg-server xorg-server-devel xorg-xauth xorg-xdpyinfo xorg-xdriinfo xorg-xev xorg-xfontsel xorg-xhost xorg-xinit xorg-xinput xorg-xkill xorg-xprop xorg-xrandr xorg-xrdb xorg-xset xorg-xsetroot xorg-xvinfo xorg-xwayland xorg-xwininfo yaml-language-server yarn yasm yq yt-dlp zig zip zls zstd
Additionally install when you want dynamic application of power management settings
You can install tlp
to enable dynamically applying power management settings,
based on if your power connector is connected or if you’re on battery.
pacman -S --needed tlp
Additionally install when you want to use dracut instead of mkinitcpio
I use dracut
on my desktop computer, because I need working LVM RAID mirrors
there. I couldn’t get that to work with mkinitcpio
based initrd.
If you want to use dracut
instead of mkinitcpio
to generate initrd images,
you need to install it and remove the default initrd tooling:
pacman -S --needed dracut
pacman -R mkinitcpio mkinitcpio-busybox
Note that there are no hooks by default for rebuilding dracut-based initrd
images, so you’d need to do that manually after linux
kernel package updates.
For example:
# Kernel version in package contains dot, on-disk it contains dash, hence the sed.
export KERNEL_VERSION=$(pacman -Q linux | awk '{print $2}' | sed 's|\.arch|-arch|g')
cp /lib/modules/$KERNEL_VERSION/vmlinuz /boot/vmlinuz-linux
dracut --kver $KERNEL_VERSION --force /boot/initramfs-linux-fallback.img
dracut --hostonly --no-hostonly-cmdline --kver $KERNEL_VERSION --force /boot/initramfs-linux.img
Additionally install when you use X11 instead of Wayland and want gestures
Wayland provides working gestures on Gnome out of the box, and Wayland is the default on Arch Linux. If, however, you have some reason to use X11 instead of Wayland by default and you want gestures to work, you can install Touchégg:
pacman -S --needed touchegg
Make sure you enable the service:
systemctl enable --now touchegg
And also install the required Gnome extension.
Additionally install on devices with fingerprint reader
Devices like the Framework Laptop 13 have a fingerprint reader. If you want to use it, make sure to install the following packages:
pacman -S --needed libfprint fprintd
And make sure you enable the service:
systemctl enable --now fprintd
To configure the fingerprint reader, I needed to upgrade the firmware. Version
01000320
is known to not work with Linux. In my case, I had to use an older
version of fwupd
(version 1.9.5 to be specific) - 1.9.10 did not work, but
I’ve had a report recently (thanks, Jan Schoone) that fwupd
version 1.9.13
and possibly higher, does actually work these days (your milage may vary, etc).
In any case, I followed the instructions here
to update to the required firmware version 01000330
to make the fingerprint
reader work with Linux. Instructions below are based on the linked document:
# First downgrade fwupd...
wget --continue https://archive.archlinux.org/packages/f/fwupd/fwupd-1.9.5-2-x86_64.pkg.tar.zst --output fwupd-1.9.5-2-x86_64.pkg.tar.zst
pacman -U fwupd-1.9.5-2-x86_64.pkg.tar.zst
# Get firmware file and install...
wget --continue https://github.com/FrameworkComputer/linux-docs/raw/main/goodix-moc-609c-v01000330.cab --output goodix-moc-609c-v01000330.cab
fwupdtool install --allow-reinstall --allow-older goodix-moc-609c-v01000330.cab
Note: There might be a transfer error mentioned at the end. This can be safely ignored. Reboot and start a fresh terminal and show the device:
fwupdmgr get-devices 1e8c8470-a49c-571a-82fd-19c9fa32b8c3
Note: The above command sometimes times out the first time it’s run. Just run it
again and you’ll see some details about the fingerprint reader. Notably, the
version should now be: 01000330
. You can now enroll fingerprints.
If your fingerprint reader is working, you can continue to follow the steps here to set it up for usage.
Additionally install on devices with Intel graphics
My Framework Laptop 13 is intel-based, so I install these packages additionally:
pacman -S --needed intel-gpu-tools vulkan-intel intel-media-driver libvdpau-va-gl intel-graphics-compiler intel-compute-runtime gst-plugin-va
Also, from AUR, I install these, making sure their version is synced with the above mentioned 64-bit versions, These make it possible for 32-bit apps to also use VAAPI-based hardware acceleration:
lib32-intel-gmmlib
lib32-intel-media-driver
Additionally install on devices with AMD graphics
If you have an AMD device instead, you might want these ones:
pacman -S --needed radeontop vulkan-radeon gst-plugin-va
Additionally install on devices with NVidia graphics
If you have an NVidia device instead, you might want these ones:
pacman -S --needed cuda cuda-tools ffnvcodec-headers libva-nvidia-driver nvidia-cg-toolkit nvidia-settings nvidia-utils nvidia-dkms nvtop opencl-nvidia openssl-1.1 gst-plugins-bad
Note: The openssl-1.1
package is a (missing) dependency for
libnvidia-pkcs11.so
contained in nvidia-utils
.
Installing lib32 package equivalents (optional)
I like to install the lib32-* package equivalents of packages I installed. There isn’t an easy way to do this and it is a bit messy, but here’s how I do it:
# Clean beginnings.
mkdir -p ~/Desktop
rm ~/Desktop/lib32-candidates ~/Desktop/lib32-notfound
# Append a list of possible lib32-* package names by prepending to package name.
for p in `pacman -Qq | grep -v lib32`; do echo lib32-$p >> ~/Desktop/lib32-candidates; done
# Append a list of possible lib32-* package names which consist of lib32-firstnamepart.
for p in `pacman -Qq | grep -v lib32 | cut -d- -f1 | sort -u`; do echo lib32-$p >> ~/Desktop/lib32-candidates; done
# Remove known-not-working elements and make sure the output file is sorted and unique.
cat ~/Desktop/lib32-candidates | grep -v rustup | grep -v openssl-1.1 | grep -v mesa-amber | grep -v mesa-demos | sort -u -o ~/Desktop/lib32-candidates
# Abuse pacman -S to obtain invalid package names.
pacman -S --needed `cat ~/Desktop/lib32-candidates | sort -u` 2>&1 | grep 'error: target not found' | awk '{print $5}' > ~/Desktop/lib32-notfound
# Make sure that's sorted and unique too.
sort -o ~/Desktop/lib32-notfound ~/Desktop/lib32-notfound
# Use comm to diff the lists and only feed valid package names to pacman -S.
pacman -S --needed `comm -23 ~/Desktop/lib32-candidates ~/Desktop/lib32-notfound`
Service configuration
I use and customize a bunch of services on my device. So I don’t forget what I customized and why, let me document it.
CpuPower
The cpupower
service reads from /etc/default/cpupower
and configures the
default scheduler. Edit that file to set the default scheduler to powersave
or
performance
and then enable it:
systemctl enable --now cpupower.service
Avahi
I don’t want any service auto-configuring stuff on my system, especially things like printers for example. Therefore I disable the Avahi zeroconf service:
systemctl mask avahi-daemon.service
systemctl mask avahi-daemon.socket
systemctl mask avahi-dnsconfd.service
Bluetooth
Enable bluetooth
with:
systemctl enable --now bluetooth.service
Bluetooth mostly just works out of the box, except for my XBox Series X|S
Wireless Game Controller. To get this to run, I use the xpadneo-dkms
AUR
package. Additionally, I need to configure a few settings in
/etc/bluetooth/main.conf
(add or set these settings yourself, or use patch
to apply the settings to your main.conf
):
--- main.conf.orig 2023-07-31 19:01:29.473651656 +0300
+++ main.conf 2023-08-01 23:39:23.294653784 +0300
@@ -49,7 +49,7 @@
# Restricts all controllers to the specified transport. Default value
# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
# Possible values: "dual", "bredr", "le"
-#ControllerMode = dual
+ControllerMode = dual
# Maximum number of controllers allowed to be exposed to the system.
# Default=0 (unlimited)
@@ -100,7 +100,7 @@
# Specify the policy to the JUST-WORKS repairing initiated by peer
# Possible values: "never", "confirm", "always"
# Defaults to "never"
-#JustWorksRepairing = never
+JustWorksRepairing = confirm
# How long to keep temporary devices around
# The value is in seconds. Default is 30.
@@ -212,9 +212,9 @@
# LE default connection parameters. These values are superceeded by any
# specific values provided via the Load Connection Parameters interface
-#MinConnectionInterval=
-#MaxConnectionInterval=
-#ConnectionLatency=
+MinConnectionInterval=7
+MaxConnectionInterval=9
+ConnectionLatency=0
#ConnectionSupervisionTimeout=
#Autoconnecttimeout=
@@ -318,7 +318,7 @@
# AutoEnable defines option to enable all controllers when they are found.
# This includes adapters present on start as well as adapters that are plugged
# in later on. Defaults to 'true'.
-#AutoEnable=true
+AutoEnable=false
# Audio devices that were disconnected due to suspend will be reconnected on
# resume. ResumeDelay determines the delay between when the controller
After changes, restart the service:
systemctl restart bluetooth.service
GDM
Enable gdm
with:
systemctl enable --now gdm.service
I prefer auto-login on some of my devices (no on laptop, yes on desktop). Add
the two lines or apply the below diff to /etc/gdm/custom.conf
using patch
if you would like your user to allow GDM to automatically login (don’t forget
to replace $USER
with your user name):
--- custom.conf.orig 2023-07-31 19:08:35.307832755 +0300
+++ custom.conf 2023-07-31 19:08:25.741219958 +0300
@@ -1,6 +1,8 @@
# GDM configuration storage
[daemon]
+AutomaticLogin=$USER
+AutomaticLoginEnable=True
# Uncomment the line below to force the login screen to use Xorg
#WaylandEnable=false
Reboot for the above to take effect.
Libvirtd
Enable libvirtd
with:
systemctl enable --now libvirtd.service
I run libvirtd
mostly stock. I do set unix_sock_group
to libvirt
and add
myself to the libvirt
group. I then set unix_sock_ro_perms
,
unix_sock_rw_perms
and unix_sock_admin_perms
to 0770
(Meaning, the owner
and group can read, write and execute, everybody else can do nothing).
unix_sock_group = "libvirt"
unix_sock_ro_perms = "0770"
unix_sock_rw_perms = "0770"
unix_sock_admin_perms = "0770"
You need to change this for a whole lot of files under /etc/libvirt
. Not doing
this causes problems when connecting with your user instead of root using the
virsh
or virt-manager
clients. don’t forget to restart the service after
changes:
systemctl restart libvirtd.service
Furthermore, I configure the virt0
interface of the default
NAT-enabled
network to have a specific IP address (10.10.11.1) and range (note: needs to
be run after starting/restarting libvirtd):
export UUID=$(uuidgen)
cat <<EOF > "/tmp/net-default.xml"
<network>
<name>default</name>
<uuid>$UUID</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virt0' stp='off' delay='0'/>
<ip address='10.10.11.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.10.11.2' end='10.10.11.254'/>
</dhcp>
</ip>
</network>
EOF
virsh net-destroy default
virsh net-undefine default
virsh net-define /tmp/net-default.xml
virsh net-start default
rm -qf /tmp/net-default.xml
Systemd-networkd and iwd
First configure iwd
defaults:
mkdir -p /etc/iwd
cat <<EOF > "/etc/iwd/main.conf"
[General]
RoamThreshold=-75
RoamThreshold5G=-80
[Network]
EnableIPv6=false
NameResolvingService=systemd
[Scan]
DisablePeriodicScan=true
EOF
If you’re connecting to a wireless network, you need to first configure that
using iwctl
, see the iwd page on the
Arch wiki.
After that, you can configure systemd-networkd
, which comes down to placing
configuration files in /etc/systemd/network
. Let me give three examples.
Setting up an ethernet device with DHCP
cat <<EOF > "/etc/systemd/network/10-ethernet.network"
[Match]
Name=eth0
[Network]
DHCP=yes
EOF
Setting up a bridge device with DHCP
Create the physical device bind config:
cat <<EOF > "/etc/systemd/network/10-bind.network"
[Match]
Name=eth0 eth1
[Network]
Bridge=bridge0
EOF
Create the virtual bridge device:
cat <<EOF > "/etc/systemd/network/10-bridge.netdev"
[NetDev]
Name=bridge0
Kind=bridge
MACAddress=52:41:41:46:00:0b
EOF
And finally, the bridge network, configured by DHCP:
cat <<EOF > "/etc/systemd/network/10-bridge.network"
[Match]
Name=bridge0
[Network]
DHCP=yes
EOF
Setting up a wireless device with DHCP
Note that you first need to configure iwd
to authenticate and connect to your
wireless network. After that, tell systemd-networkd
about it:
cat <<EOF > "/etc/systemd/network/10-wireless.network"
[Match]
Name=wlan0
[Network]
DHCP=yes
IgnoreCarrierLoss=10s
EOF
Enable systemd-networkd and iwd services
Enable systemd-networkd
and iwd
with:
systemctl enable --now iwd.service
systemctl enable --now systemd-networkd.service
systemctl disable systemd-networkd-wait-online.service
Note that we disable the waiting service, since we want to continue booting
even if there is no network. If you would like to know more about how you can
configure systemd-networkd
, be sure to read the
systemd-networkd page on
the Arch wiki.
OpenSSL
I did run into an issue connecting with older VPN environments related to OpenSSL 3.x disabling various legacy encapsulation and connection modes by default. The error you would see in such a case is:
Jul 31 20:26:38 FRAME openvpn[58956]: OpenSSL: error:11800071:PKCS12 routines::mac verify failure
Jul 31 20:26:38 FRAME openvpn[58956]: OpenSSL: error:0308010C:digital envelope routines::unsupported
Jul 31 20:26:38 FRAME openvpn[58956]: Decoding PKCS12 failed. Probably wrong password or unsupported/legacy encryption
Furthermore, when you are behind corporate proxies, you might also have
difficulties passing through the corporate proxy without the settings
UnsafeLegacyRenegotiation
and UnsafeLegacyServerConnect
(which were allowed
by default on OpenSSL 1.x).
To work-around these issues, I place a custom /etc/ssl/openssl.cnf
:
cp -n "/etc/ssl/openssl.cnf" "/etc/ssl/openssl.cnf.orig"
cat <<EOF > "/etc/ssl/openssl.cnf"
HOME = .
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
ssl_conf = ssl_sect
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
Options = UnsafeLegacyRenegotiation,UnsafeLegacyServerConnect
EOF
Note that I would only do the above if you need to interact with some old legacy VPN stuff, or if you’re behind moron-grade SSL-terminating proxies.
NFSv4
Enable nfsv4
with:
systemctl enable --now nfsv4-server.service
Quick note: the above start may fail if you updated the linux
package but did
not reboot yet, you’ll see an error about a dependency failure.
I use NFS only on internal interfaces, specifically the virt0
interface of
the default
network (remember that ip address 10.10.11.1?). This allows me
to work with shared storage on other operating systems that I fool around with
on Qemu/KVM (Note that you have much better options for modern Linux systems -
there you can use enable shared memory
and a virtiofs
device to essentially
loop-mount a memory block device which is a directory on the host).
Since we’re only doing NFSv4, and we’re not interested in user/group ID mapping, let’s stop and mask a couple of RPC services first:
systemctl stop rpcbind.service
systemctl mask rpcbind.service
systemctl stop nfs-blkmap
systemctl mask nfs-blkmap
systemctl stop nfs-idmapd
systemctl mask nfs-idmapd
systemctl stop nfs-mountd
systemctl mask nfs-mountd
To make NFSv4 only listen on a specific interface, and to disable version 3 of
the protocol explicitly, we patch /etc/nfs.conf
(use patch
or add the
host=
, vers3=
and vers4=
elements by hand under [nfsd]
:
--- nfs.conf.orig 2023-07-31 21:16:20.438028044 +0300
+++ nfs.conf 2023-08-03 09:01:05.586457300 +0300
@@ -67,13 +67,14 @@
# debug=0
# threads=8
-# host=
+host=10.10.11.1
# port=0
# grace-time=90
# lease-time=90
# udp=n
# tcp=y
-# vers3=y
-# vers4=y
+vers3=n
+vers4=y
# vers4.0=y
# vers4.1=y
# vers4.2=y
Create an exports for /home:
cp -n "/etc/exports.d/home.exports" "/etc/exports.d/home.exports.orig"
cat <<EOF > "/etc/exports.d/home.exports"
/home 10.10.11.0/24(rw,sync,crossmnt,no_subtree_check)
EOF
After the above changes, restart the service:
systemctl restart nfsv4-server.service
Samba
Enable smbd
and nmbd
with:
systemctl enable --now nmb.service smb.service
I use Samba for the same reasons as I use NFS, that is to have shared storage
on various older virtual machines (like Windows NT 4.0). Let’s create an
smb.conf
file:
cat <<EOF > "/etc/samba/smb.conf"
[global]
workgroup = WORKGROUP
netbios name = $(hostname | tr 'a-z' 'A-Z' | cut -d. -f1)
server string =
server role = standalone server
server min protocol = NT1
ntlm auth = yes
lanman auth = yes
hosts allow = 10.10.11.
log file = /var/log/samba/log.smbd
max log size = 100
interfaces = 10.10.11.1/24
bind interfaces only = yes
dns proxy = yes
wins proxy = yes
wins support = yes
local master = yes
domain master = yes
preferred master = yes
os level = 33
[homes]
comment = Home Directories
acl allow execute always = True
browsable = yes
writable = yes
valid users = %U
create mask = 0644
directory mask = 0755
EOF
After creating or changing smb.conf
, restart the services:
systemctl restart nmb.service smb.service
TLP
Enable tlp
with:
systemctl enable --now tlp.service
TLP is used to manage power-saving modes of various hardware. It is usually configured to enable power-saving when not connected to AC, and to disable it when connected to AC. It does that for all kinds of things, like Wi-Fi, USB, PCIe, Bluetooth, the CPU scheduler, etc.
I’ve made a custom TLP configuration for my Framework Laptop 13 which you can install as follows:
cat <<EOF > "/etc/tlp.d/01-custom.conf"
CPU_SCALING_GOVERNOR_ON_AC=powersave
CPU_SCALING_GOVERNOR_ON_BAT=powersave
CPU_BOOST_ON_AC=0
CPU_BOOST_ON_BAT=0
PCIE_ASPM_ON_BAT=powersupersave
PLATFORM_PROFILE_ON_AC=balanced
PLATFORM_PROFILE_ON_BAT=low-power
USB_ALLOWLIST=32ac:0002
USB_EXCLUDE_BTUSB=1
USB_EXCLUDE_PRINTER=0
WIFI_PWR_ON_AC=off
WIFI_PWR_ON_BAT=off
WOL_DISABLE=N
EOF
After the above, you need to restart the service:
systemctl restart tlp.service
Cups
Enable cups
with:
systemctl enable --now cups.socket
I also don’t want cups via cups-browsed
to be able to auto-add printers, so I
patch /etc/cups/cups-browsed.conf
as follows:
--- cups-browsed.conf.default 2023-08-29 09:57:47.157250003 +0200
+++ cups-browsed.conf 2023-08-29 09:58:45.054108764 +0200
@@ -53,7 +53,7 @@
# BrowseLocalProtocols.
# Can use DNSSD and/or CUPS and/or LDAP, or 'none' for neither.
-# BrowseProtocols none
+BrowseProtocols none
# Only browse remote printers (via DNS-SD or CUPS browsing) from
Docker
Enable docker with:
systemctl enable --now docker.socket
I use docker without any adjustments.
CDemu
I use cdemu
and related vhba
kernel module to emulate optical drives for
use with emulation stuff. If you’d like to run CDemu, make sure the required
modules are loaded at boot:
# Load modules at boot.
cat <<EOF > "/etc/modules-load.d/cdemu.conf"
sg
sr_mod
vhba
EOF
# Do it now too.
modprobe -a sg sr_mod vhba
Enabling user services
I use the following user-level services (do as logged in user):
for u in syncthing.service wireplumber.service pipewire.socket pipewire-pulse.socket; do systemctl enable --now --user $u; done
How I use AUR
All the hip young things are running yay
these days, but I like to do it the
bare-hands way. That way I have more feeling with what’s going on with AUR
packages.
Setting up your own custom local repository
First, let’s create a custom
repository source for pacman. Note that I sign
my packages using my GnuPG key, so the following assumes that. If you don’t
want to sign your own packages, you need to change the SigLevel
setting in
/etc/pacman.conf
for your custom
repository and you need to tell makepkg
not to sign your built package.
Also note that my package build root location is specific to my needs; feel free to change it to anything you like.
# Set package build root location.
export PKG_ROOT="$HOME/Packaging/Arch"
# Create a directory structure for Arch packaging.
mkdir -p "$PKG_ROOT"
cd "$PKG_ROOT"
install -d Build Repository 'Source Packages' Sources -o $USER
# Create a repository database.
cd "$PKG_ROOT/Repository"
repo-add custom.db.tar.gz
# Add entry to /etc/pacman.conf.
cat <<EOF >> "/etc/pacman.conf"
[custom]
SigLevel = Required DatabaseRequired TrustedOnly
Server = file://$PKG_ROOT/Repository
EOF
# Add your public key to pacman keychain and set trust (assuming key matches $USER).
gpg --export --armor $USER > your.key
pacman-key --add your.key
pacman-key --lsign-key $USER
rm -qf your.key
# Run update for db and files, you should see custom being referenced.
pacman -Syu
pacman -Fy
# Unset variables.
unset PKG_ROOT
Configuring for package building
Now we configure makepkg
defaults. Make sure you configure the PKG_ROOT,
GPG_PUBKEY and PACKAGER environment variables to your specific likings:
# Set package build root location, gpgpubkey-id and packager string.
export PKG_ROOT="$HOME/Packaging/Arch"
export GPG_PUBKEY='89E5EB2541BC6668A9C165D424BD51CD12534CE6'
export PACKAGER='Rubin Simons <me@rubin55.org>'
cat <<EOF > "$HOME/.makepkg.conf"
# ~/.makepkg.conf.
MAKEFLAGS="-j$(nproc)"
BUILDENV=(!distcc color !ccache check sign)
BUILDDIR="$PKG_ROOT/Build"
PKGDEST="$PKG_ROOT/Repository"
SRCDEST="$PKG_ROOT/Sources"
SRCPKGDEST="$PKG_ROOT/Source Packages"
GPGKEY="$GPG_PUBKEY"
PACKAGER="$PACKAGER"
EOF
# Unset variables.
unset PKG_ROOT GPG_PUBKEY PACKAGER
Example interactions using AUR
Here are a few example interactions with package fetching, building, repository updating and package removal to get you started:
# Set package build root location.
export PKG_ROOT="$HOME/Packaging/Arch"
# Get an AUR package.
cd "$PKG_ROOT/Build"
git clone https://aur.archlinux.org/aurutils.git
# Build an AUR package.
cd aurutils
makepkg -cCs
ls ../../Repository/aurutils*
# Show information about a built package.
cd "$PKG_ROOT/Repository"
pacman -Qpi aurutils-*-any.pkg.tar.zst
# Build a source package.
cd aurutils
makepkg -cCsS
ls ../../Source\ Packages
# Update local repository (adds new packages, removes older ones).
cd "$PKG_ROOT/Repository"
repo-add -n -R -s custom.db.tar.gz *.zst
# Remove a specific AUR package from local repository
cd "$PKG_ROOT/Repository"
repo-remove -s custom.db.tar.gz aurutils
AUR packages I build and install
So now to fill that $PKG_ROOT/Build
directory with packages from AUR so we
can build some stuff and put it in our own repository:
# Set package build root location.
export PKG_ROOT="$HOME/Packaging"
# Git clone them all.
cd "$PKG_ROOT/Build"
export GITREPOS="adwaita-qt-git aic94xx-firmware akku alsa-hdspeconf amitools ares-emu aseprite ast-firmware attract-git audacious-gtk3 audacious-plugins-gtk3 aurutils battop-bin blastem-hg bluez-hcitool brscan4 bun-bin chez-scheme clojure-lsp-bin cmake-format cmake-language-server codechecker conan cubeb djmount dockerfile-language-server dolphin-emu-git dosbox-x drawio-desktop-bin duckstation-git dumpasn1 eclipse-java edid-decode-git erlang_ls exercism figma-linux-bin flow-bin flutterup flycast-git ghcup-hs-bin github-desktop-bin godot-mono-bin google-cloud-cli groovy-language-server-git ibmcloud-cli icaclient ida-pro-patched imhex infer-bin ipfs-desktop irccloud-bin jdk17-jetbrains-bin jdk17-openj9-bin jdk21-jetbrains-bin jdk21-openj9-bin jdtls jetbrains-toolbox krew kubelogin lagrange lemminx lexical-bin lib32-gperftools lib32-intel-gmmlib lib32-intel-media-driver libbacktrace-git librashader libretro-beetle-cygne-git libretro-beetle-lynx-git libretro-beetle-ngp-git libretro-beetle-pcfx-git libretro-bluemsx-git libretro-dosbox-pure-git libretro-fsuae-git libretro-swanstation-git libspng-git libwireplumber-4.0-compat license-wtfpl logisim-evolution-bin m64py marsdev-git mednaffe mei-amt-check-git metals mkinitcpio-firmware mongodb moonlight-qt ms-sys ncurses5-compat-libs neovim-symlinks nestopia netcoredbg networkmanager-iwd ocenaudio-bin omnisharp-roslyn-bin openmsx openshift-client-bin openshift-codeready-bin openshift-developer-bin openshift-pipelines-bin papirus-folders passmark-performancetest-bin pcsx2-git pegasus-frontend-git perlnavigator postman2openapi-bin postman-bin powercap powershell-bin pragmatapro-fonts protonmail-bridge-bin protonplus protontricks ps3-disc-dumper-bin pupdate-bin pvsneslib-git pwvucontrol python-dataclasses-json python-ffmpeg-python python-m3u8 python-machine68k python-mpegdash python-patch-ng python-pathvalidate python-pdm-pep517 python-pluginbase python-pyqtdarktheme python-pysdl2 python-pytest-runner python-tidalapi python-vdf qt5-gamepad rabtap rcu-bin rebar3 regionset roomeqwizard rpcs3-git ruby-backport ruby-e2mmap ruby-jaro_winkler ruby-rbs ruby-reverse_markdown ruby-solargraph rusty-psn-bin ryujinx-git sameboy scala-dotty scala-scala3-symlink scheme-chez-symlink sedutil shellcheck-bin sigtop-git skyscraper-git slack-desktop snd-hdspe-dkms soapui softhsm-git sublime-text-4 sunshine-git tidal-dl-ng tidal-hifi-bin tla-toolbox townsemu-git ttf-b612 ungoogled-chromium-bin upd72020x-fw vala-language-server vasm visual-studio-code-bin vlc-git vlink vmware-horizon-client vmware-keymaps vtsls wd719x-firmware wolfram wxedid xcursor-dmz xdg-terminal-exec-git xpadneo-dkms y-cruncher zeal-git zlib-ng zoom"
for p in $GITREPOS; do git clone https://aur.archlinux.org/$p.git; done
# Build all (I wouldn't do this, I would initially enter one-by-one and do
# git log ; makepkg -cCs manually). Will result in packages under $PKG_ROOT/Repository.
cd "$PKG_ROOT/Build"
for p in *; do cd $p; makepkg -cCs; if [[ $? -ne 0 && $? -ne 13 ]]; then echo "$p did not go well, please fix..."; break; fi; cd - > /dev/null; done
# Update custom repository.
cd "$PKG_ROOT/Repository"
repo-add -n -R custom.db.tar.gz *.zst
# Update pacman databases.
pacman -Syu
# List all packages pacman sees in custom repository.
pacman -Sl custom
# Install all not-installed packages from custom repository.
pacman -S --needed $(pacman -Sl custom | grep -v installed | awk '{print $2}')
Personal AUR packages
I maintain these AUR packages:
- infer-bin
- jdk21-openj9-bin
- marsdev-git
- openshift-codeready-bin
- openshift-developer-bin
- openshift-pipelines-bin
- passmark-performancetest-bin
- postman2openapi-bin
- pragmatapro-fonts
- pupdate-bin
- pvsneslib-git
- rcu-bin
- rusty-psn-bin
- scala-scala3-symlink
- scheme-chez-symlink
Here are a few more things I plan to create AUR packages for:
Final thoughts
After you’ve done (most of) the above, a reboot
is in order; the system should
come up cleanly, without errors or stalls.
I’ve be been using this setup for the last month and it has been pretty great. I get really good battery life, The fan almost never comes on, sleep and resume work reliably, bluetooth works with mouse, gamepad and headphones, Libvirt has been amazing with a bunch of interesting virtual machines (Guix, Windows NT, RHEL6 with Softimage).
I hope to be using this machine and operating system for a long time!