equivariant

Personal Arch Linux setup

This is how I have my computer set up. I don't expect this document to be useful for other people.

Installation

Follow Arch Linux with mirrored, encrypted ZFS root.

Connect to network:

dhcpcd

Non-root user

useradd -m -G wheel,network,sys,uucp me
chmod 700 /home/me
passwd me
useradd -m -G network guest
passwd guest
pacman -S sudo
visudo

Uncomment the line %wheel ALL=(ALL) ALL and save. Log out and log back in as regular user.

Desktop environment

Install video drivers:

pacman -S nvidia nvidia-utils cuda cudnn
systemctl reboot

Install basic desktop environment:

pacman -S xfce4 xf86-input-{synaptics,wacom} firefox vim 
cp /etc/X11/xinit/xinitrc .xinitrc

Replace the last few lines with

exec startxfce4

Remap caps lock to escape:

# ~/.Xmodmap
# Double check that ~/.xinitrc loads this file.
# If not, add `xmodmap "$HOME/.Xmodmap"` before the `exec` line.
clear Lock
!keycode 66 = Escape Caps_Lock  ! <Shift>Escape for caps lock
keycode 66 = Escape

Run exec startx to start DE.

Screen locker

Configure shortcuts in xfce4-keyboard-settings:

Alt+Q       xflock4

Install xautolock to lock the screen after 20 minute timeout:

pacman -S xautolock

In xfce4-session-settings under "Application Autostart", add an entry for xautolock:

/bin/bash -c 'xset dpms 0 0 0; xautolock -time 20 -locker /usr/bin/xflock4 > ~/.xautolock.log 2>&1'

Confirm that screen lock works. Confirm that you can't bypass the lock with Ctrl-Alt-F7.

NetworkManager

pacman -S networkmanager network-manager-applet
systemctl enable NetworkManager systemd-resolved
systemctl start NetworkManager systemd-resolved
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

The router provided by my ISP has a bug that causes DNS requests to hang for 5 seconds. To work around it:

nmcli con
nmcli con modify MyNetworkName ipv4.dns-options single-request

To revert, set ipv4.dns-options to '' instead of single-request.

Sound

pacman -S pipewire pipewire-media-session pipewire-pulse xfce4-pulseaudio-plugin xfce4-notifyd pavucontrol clementine gst-libav gst-plugins-{base,good,bad,ugly}

Add PulseAudio applet to panel. Log out and log back in, then confirm sound works.

Bluetooth headset

Reference

$ pacman -S bluez-utils blueman-applet

Configure BlueZ to automatically power on Bluetooth adapter:

# /etc/bluetooth/main.conf
[Policy]
AutoEnable=true

Power on the headset. Start BlueZ and connect to the headset:

$ systemctl enable bluetooth
$ systemctl start bluetooth
$ bluetoothctl
[bluetooth]# power on
[bluetooth]# agent on
[bluetooth]# default-agent
[bluetooth]# scan on
[bluetooth]# devices
[bluetooth]# pair XX:XX:XX:XX:XX:XX
[bluetooth]# connect XX:XX:XX:XX:XX:XX

Confirm audio is working in headset. You may need to switch the output device in xfce4-pulseaudio-plugin/pavucontrol. Mark the headset trusted so that it can initiate the connection:

[bluetooth]# trust XX:XX:XX:XX:XX:XX

Create the following script to automatically switch to the headset when powered on:

# ~/.local/bin/btautoswitch
#!/bin/bash
while true; do
    if pactl list sinks | fgrep bluez_output.XX_XX_XX_XX_XX_XX.a2dp-sink >/dev/null; then
        if pactl info | fgrep 'Default Sink: alsa_output.pci-XXXX_XX_XX.X.analog-stereo' >/dev/null; then
            pactl set-default-sink bluez_output.XX_XX_XX_XX_XX_XX.a2dp-sink
        fi
    fi
    sleep 0.5
done

Add it to autostart in xfce4-session-settings. You may also need to add blueman-applet. Start btautoswitch manually now:

$ btautoswtich

Power off the headset. Audio should go back to the previous audio sink. Power on the headset. Audio should move to the headset.

If something goes wrong, the following commands may be useful:

[bluetooth]# show  # Show Bluetooth controller status
[bluetooth]# info  # Show Bluetooth device status
$ journalctl -fu bluetooth  # Watch BlueZ logs
$ journalctl -fk  # Watch kernel messages for new devices
$ pactl list cards  # See if headset is known by PulseAudio
$ pactl list sinks  # See if headset is recognized as audio sink by PulseAudio
$ blueman-manager  # Blueman GUI

Console utilities

pacman -S mlocate ncdu openssh parallel virtualbox virtualbox-host-modules-arch wget whois ripgrep the_silver_searcher moreutils bind-tools android-{tools,udev} cronie
gpasswd -a me vboxusers
systemctl enable man-db.timer systemd-timesyncd cronie
systemctl start man-db.timer systemd-timesyncd cronie

Ignore backups and directories with many files in /etc/updatedb.conf:

PRUNEPATHS = "[...] /slow/backup /slow/bulk/datasets"

Development tools

pacman -S python base-devel python-{numpy,scipy,matplotlib,seaborn,scikit-learn,pandas,pip} python2-{pip,dbus} git pandoc ipython ipython2 jupyterlab tensorflow-opt-cuda tensorboard rustup texlive-most wireshark
gpasswd -a me wireshark

Docker

zfs create -o mountpoint=/var/lib/docker zroot/docker
pacman -S docker
gpasswd -a me docker

Desktop programs

pacman -S firefox xorg-xset redshift caja dmenu gvim maim dzen2 pidgin pidgin-libnotify thunderbird keepassx2 aspell-en eom gimp gnumeric inkscape picard liferea mpv soundconverter transmission-gtk mcomix engrampa youtube-dl ttf-dejavu ttf-liberation noto-fonts{,-cjk,-emoji,-extra}

Set units in Caja to IEC.

Install auracle-git. Install the following:

auracle download anki freecad-weekly-appimage insect zotero-bin
cd $pkg; makepkg -sic  # for each

Firefox

about:preferences settings:

about:config (or user.js) settings:

// Don't mangle URLs
user_pref("keyword.enabled", false);
user_pref("browser.fixup.alternate.enabled", false);

// Don't close the window when the last tab is closed.
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Send origin but not full URL.
// Not sending the referrer at all breaks too many sites.
user_pref("network.http.referer.XOriginTrimmingPolicy", 2);

// Disable clipboard API to avoid exposing clipboard contents on middle click.
// This has been fixed for links in <https://bugzilla.mozilla.org/show_bug.cgi?id=1521396>,
// but middle clicking on other elements still exposes your clipboard contents.
user_pref("middlemouse.paste", false);
user_pref("dom.event.clipboardevents.enabled", false);
user_pref("dom.events.asyncClipboard", false);

// Service Workers can make requests even after a page is closed. This is a
// privacy risk.
user_pref("dom.serviceWorkers.enabled", false);

Theme: "Light". The "System theme" doesn't highlight the selected tab.

Extensions:

Vimium config:

unmapAll
map j scrollDown
map k scrollUp
map gg scrollToTop
map G scrollToBottom
map d scrollPageDown
map u scrollPageUp
map i enterInsertMode
map f LinkHints.activateMode
map F LinkHints.activateModeToOpenInNewTab

HDD ZFS pool

Create /etc/systemd/system/zfs-load-key.service (see ZFS#Unlock at boot time: systemd):

[Unit]
Description=Load encryption keys
DefaultDependencies=no
Before=zfs-mount.service
After=zfs-import.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/bash -c '/usr/bin/zfs load-key -a'

[Install]
WantedBy=zfs-mount.service

Place key in /etc/cryptfs.key. Import the pool and enable services:

zpool import
zpool import slow
systemctl enable zfs-scrub@slow.timer
systemctl start zfs-scrub@slow.timer
systemctl enable zfs-load-key.service
systemctl start zfs-load-key.service

Backups

Snapshots to HDD

See Onsite backups with zrepl.

Offsite

Using Borg to back up to rsync.net.

In /usr/local/bin/rsyncnet-backup:

#!/bin/bash
set -eu

if [ "$EUID" -ne 0 ]; then
    echo 'Run as root'
    exit 1
fi

# # /root/.ssh/config
# Host whatever.rsync.net
#    User 12345
#    IdentityFile ~/.ssh/rsyncnet
#    ServerAliveInterval 10
#    ServerAliveCountMax 30
export SSH_HOST='whatever.rsync.net'
export BORG_REPO="$SSH_HOST:borg/myhostname"
export BORG_PASSPHRASE="$(cat /path/to/borg.key)"

# Based on https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups

info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; }
trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM

info "Checking quota"

ssh "$SSH_HOST" quota

info "Starting backup"
borg create \
    --remote-path=borg1 \
    --verbose \
    --filter AME \
    --list \
    --stats \
    --show-rc \
    --compression lz4 \
    --exclude-caches \
    --one-file-system \
    --exclude '/home/*/.cache' \
    --exclude '/home/*/.cargo' \
    --exclude '/home/*/.conda' \
    --exclude '/home/*/.go' \
    --exclude '/home/*/.julia' \
    --exclude '/home/*/.npm' \
    --exclude '/home/*/.platformio' \
    --exclude '/home/*/.pyenv' \
    --exclude '/home/*/.rustup' \
    --exclude '/home/*/.vagrant.d/boxes' \
    --exclude '/home/*/Code/3p' \
    --exclude '/home/*/Downloads' \
    --exclude '/home/*/Music' \
    --exclude '/home/*/aur' \
    --exclude '/var/cache/*' \
    --exclude '/var/lib/docker' \
    --exclude '/var/lib/prometheus' \
    --exclude '/var/log/journal' \
    --exclude '/var/tmp/*' \
    ::'{utcnow}' \
    /etc \
    /home \
    /root \
    /var

info "Pruning repository"
borg prune \
    --remote-path=borg1 \
    --list \
    --show-rc \
    --keep-daily 7 \
    --keep-weekly 4 \
    --keep-monthly 4

Set appropriate permissions:

sudo chown root:root /usr/local/bin/rsyncnet-backup /path/to/borg.key
sudo chmod 755 /usr/local/bin/rsyncnet-backup
sudo chmod 600 /path/to/borg.key

Add to /etc/anacrontab:

1 120 rsyncnet-backup nice chronic /usr/local/bin/rsyncnet-backup

Prometheus

See Collecting local system stats with Prometheus on Arch Linux.