aboutsummaryrefslogtreecommitdiff
path: root/src/blog/freebsd-14-on-the-desktop
diff options
context:
space:
mode:
Diffstat (limited to 'src/blog/freebsd-14-on-the-desktop')
-rw-r--r--src/blog/freebsd-14-on-the-desktop/index.md1127
-rw-r--r--src/blog/freebsd-14-on-the-desktop/kde5.jpgbin0 -> 319371 bytes
2 files changed, 1127 insertions, 0 deletions
diff --git a/src/blog/freebsd-14-on-the-desktop/index.md b/src/blog/freebsd-14-on-the-desktop/index.md
new file mode 100644
index 0000000..2a6a13a
--- /dev/null
+++ b/src/blog/freebsd-14-on-the-desktop/index.md
@@ -0,0 +1,1127 @@
+---
+title: FreeBSD 14 on the Desktop
+date: November 20, 2024
+description: KDE 5 on a real Unix!
+---
+
+After [much deliberation](../reevaluating-rhel/), I finally decided to migrate my entire
+infrastructure from [Rocky Linux](https://rockylinux.org/) to [FreeBSD](https://www.freebsd.org/).
+
+Why FreeBSD? Perhaps you yearn for a simpler time, when `ifconfig` configured your network
+interfaces. When `/etc/motd` didn't download advertisements from the web. When adults actually
+packaged their software, instead of shipping 400 MB Flatpaks that make syscalls
+over D-Bus.
+
+Rest easy, weary netizen. While the [teenagers](https://www.jwz.org/doc/cadt.html) busy
+themselves with `systemd-resolved`, Netplan, and other horrors,
+you can still find peace in the tranquility of `/etc/rc.conf`. The enemy,
+ensnared by the YAML tarpit, is not **yet** at our gates (*sic transit gloria mundi*, etc).
+
+Anyway, let's build a FreeBSD desktop system with KDE. This guide will assume you're using
+Intel graphics with X11 (don't @ me about Wayland 🤡).
+
+![](kde5.jpg "KDE Plasma 5 on FreeBSD"){.center}
+
+## Installation
+
+Grab a FreeBSD memstick image from [here](https://www.freebsd.org/where/) and `dd` it
+to a USB stick:
+
+```bash
+curl -OJ https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/14.1/FreeBSD-14.1-RELEASE-amd64-memstick.img
+sudo dd if=FreeBSD-14.1-RELEASE-amd64-memstick.img of=/dev/sdX bs=1M conv=sync
+```
+
+The installation wizard is straightforward. Make sure your system is configured for UEFI boot,
+and select ZFS (GPT) for the disk layout.
+
+Once you reboot, login as root using the password specified during installation.
+
+## Devices, Drivers, and Tuning
+
+In this section, we'll configure device drivers and make various tweaks to
+get optimum performance out of a desktop system.
+
+You're not required to use *all* of these, but they've worked well for me.
+
+### Bootloader Tunables
+
+First, open up `/boot/loader.conf` and consider adding the following:
+
+```bash
+# /boot/loader.conf
+
+# Faster boot time.
+autoboot_delay="3"
+
+# Load PF firewall module.
+pf_load="YES"
+
+# Enable querying CPU information and temperature.
+cpuctl_load="YES"
+coretemp_load="YES"
+
+# 99% of users will never need destructive dtrace.
+security.bsd.allow_destructive_dtrace="0"
+
+# Bump up some default limits for desktop usage.
+kern.ipc.shmseg="1024"
+kern.ipc.shmmni="1024"
+kern.maxproc="100000"
+
+# If your system supports Intel Speed Shift (check dmesg), set this to 0:
+machdep.hwpstate_pkg_ctrl="0"
+
+# Enable PCI power saving.
+hw.pci.do_power_nodriver="3"
+
+# Optimizations for Intel graphics.
+compat.linuxkpi.i915_enable_fbc="1"
+compat.linuxkpi.i915_fastboot="1"
+
+# Load Linux binary compatibility layer.
+linux_load="YES"
+linux64_load="YES"
+
+# Load FUSE module (filesystems in user-space).
+fusefs_load="YES"
+
+# CUSE is needed for webcamd.
+cuse_load="YES"
+
+# Enable this module to use USB tethering (Android/iPhone)
+if_urndis_load="YES"
+
+################
+# Network tuning
+################
+# H-TCP congestion control algorithm designed to perform better over fast,
+# long-distance networks (like the internet). You might consider using it.
+cc_htcp_load="YES"
+
+# Enable faster soreceive() implementation.
+net.inet.tcp.soreceive_stream="1"
+
+# Increase network interface queue length.
+net.isr.defaultqlimit="2048"
+net.link.ifqmaxlen="2048"
+
+########################
+# Laptop-related options
+########################
+# Increase ZFS transaction timeout to save battery.
+vfs.zfs.txg.timeout="10"
+
+# Power saving for Intel graphics.
+compat.linuxkpi.i915_disable_power_well="1"
+compat.linuxkpi.i915_enable_dc="2"
+
+# Enable Thinkpad buttons.
+acpi_ibm_load="YES"
+```
+
+### Sysctl Tweaks
+
+Next, open up `/etc/sysctl.conf` and consider setting the following sysctls.
+Note that you can view the description of any sysctl by running `sysctl -d`.
+
+```bash
+# /etc/sysctl.conf
+
+####################
+# sEcuRitY HaRdeNinG
+####################
+hw.kbd.keymap_restrict_change=4
+kern.coredump=0
+kern.elf32.aslr.pie_enable=1
+kern.random.fortuna.minpoolsize=128
+kern.randompid=1
+net.inet.icmp.drop_redirect=1
+net.inet.ip.process_options=0
+net.inet.ip.random_id=1
+net.inet.ip.redirect=0
+net.inet.ip.rfc1122_strong_es=1
+net.inet.tcp.always_keepalive=0
+net.inet.tcp.drop_synfin=1
+net.inet.tcp.icmp_may_rst=0
+net.inet.tcp.syncookies=0
+net.inet6.ip6.redirect=0
+security.bsd.unprivileged_proc_debug=1
+security.bsd.unprivileged_read_msgbuf=0
+
+# Some guides will tell you use these.
+# More trouble than they're worth, IMO:
+#
+#kern.elf32.allow_wx=0
+#kern.elf64.allow_wx=0
+#security.bsd.hardlink_check_gid=1
+#security.bsd.hardlink_check_uid=1
+#security.bsd.see_other_gids=0
+#security.bsd.see_other_uids=0
+
+##################################
+# Network performance tuning
+#
+# The default values for many of these sysctls are optimized for the latencies
+# of a local network. The modifications below should result in better TCP
+# performance over connections with a larger RTT (like the internet), but at
+# the expense of higher memory utilization.
+#
+# source: trust me, bro
+###############################
+kern.ipc.maxsockbuf=2097152
+kern.ipc.soacceptqueue=1024
+kern.ipc.somaxconn=1024
+net.inet.tcp.abc_l_var=44
+net.inet.tcp.cc.abe=1
+net.inet.tcp.cc.algorithm=htcp
+net.inet.tcp.cc.htcp.adaptive_backoff=1
+net.inet.tcp.cc.htcp.rtt_scaling=1
+net.inet.tcp.ecn.enable=1
+net.inet.tcp.fast_finwait2_recycle=1
+net.inet.tcp.fastopen.server_enable=1
+net.inet.tcp.finwait2_timeout=5000
+net.inet.tcp.initcwnd_segments=44
+net.inet.tcp.keepcnt=2
+net.inet.tcp.keepidle=62000
+net.inet.tcp.keepinit=5000
+net.inet.tcp.minmss=536
+net.inet.tcp.msl=2500
+net.inet.tcp.mssdflt=1448
+net.inet.tcp.nolocaltimewait=1
+net.inet.tcp.recvbuf_max=2097152
+net.inet.tcp.recvspace=65536
+net.inet.tcp.sendbuf_inc=65536
+net.inet.tcp.sendbuf_max=2097152
+net.inet.tcp.sendspace=65536
+net.local.stream.recvspace=65536
+net.local.stream.sendspace=65536
+
+#######################
+# Desktop optimizations
+#######################
+# Prevent shared memory from being swapped to disk.
+kern.ipc.shm_use_phys=1
+
+# Increase scheduler preemption threshold for snappier GUI experience.
+kern.sched.preempt_thresh=224
+
+# Allow unprivileged users to mount things.
+vfs.usermount=1
+
+# Don't switch virtual consoles back and forth on suspend.
+# With some graphics cards, switching to a different VT breaks hardware acceleration.
+# https://github.com/freebsd/drm-kmod/issues/175
+kern.vt.suspendswitch=0
+
+########################
+# Power saving (laptops)
+########################
+hw.snd.latency=7
+```
+
+### WiFi
+
+WiFi is not where FreeBSD shines. Hope you can live with 802.11g.
+
+First, you'll need to figure out which driver supports your card (if any). For Intel
+cards, this will likely be `iwn(4)`, `iwm(4)`, or `iwlwifi(4)`. Check those man pages.
+
+My card happens to be an Intel Wireless AC 8265, which is supported by the `iwm` driver.
+
+First, make sure the required kernel modules are loaded on boot:
+
+```bash
+# /boot/loader.conf
+
+if_iwm_load="YES"
+iwm8265fw_load="YES"
+```
+
+Next, have `rc(8)` create a `wlan0` device on boot:
+
+```bash
+sysrc -v wlans_iwm0=wlan0
+sysrc -v create_args_wlan0='country US regdomain FCC'
+sysrc -v ifconfig_wlan0='WPA DHCP powersave'
+```
+
+The `WPA` option will use `wpa_supplicant(8)` to manage WiFi networks. You can either
+edit `/etc/wpa_supplicant.conf` by hand, or use the graphical interface provided by
+`networkmgr`:
+
+```bash
+pkg install sudo networkmgr
+```
+
+Note that `networkmgr` requires root permissions. You can allow all members of the
+`operator` group to run `networkmgr` without a password using `sudo`:
+
+```bash
+# /usr/local/etc/sudoers.d/networkmgr
+
+%operator ALL=NOPASSWD: /usr/local/bin/networkmgr
+```
+
+### CPU Microcode and Power Savings
+
+Install the latest CPU microcode:
+
+```bash
+pkg install cpu-microcode
+```
+
+Edit `/boot/loader.conf` to load the microcode on boot:
+
+```bash
+# /boot/loader.conf
+
+cpu_microcode_load="YES"
+cpu_microcode_name="/boot/firmware/intel-ucode.bin"
+```
+
+You can save a lot of battery (and heat) by enabling lower CPU C-states:
+
+```bash
+sysrc -v performance_cx_lowest=Cmax economy_cx_lowest=Cmax
+```
+
+Note that with modern Intel processors, it is no longer necessary to run `powerd(8)`.
+
+### Intel Graphics Driver
+
+Install the Intel graphics driver and make sure it's loaded on boot:
+
+```bash
+pkg install drm-kmod libva-intel-media-driver
+sysrc -v kld_list+=i915kms
+```
+
+### Webcam
+
+With any luck, your webcam will be supported by `webcamd`:
+
+```bash
+pkg install webcamd v4l-utils
+sysrc -v webcamd_enable=YES
+```
+
+### Sound
+
+Many ports are built with [sndio](https://sndio.org/) support by default (like Firefox).
+You can think of it as a BSD-native `pulseaudio`.
+
+```bash
+pkg install sndio
+sysrc -v sndiod_enable=YES
+
+# There appears to be a race condition with sndiod and clear_tmp_enable.
+# When /tmp is cleared out on boot, the sndiod socket is inadvertently removed!
+sysrc -v clear_tmp_enable=NO
+```
+
+#### Using different audio devices simultaneously
+
+FreeBSD's OSS has an unfortunate limitation: only one audio device
+can be used at a time. For example, I was unable to use my webcam's integrated
+microphone and my USB speakers simultaneously during video chats.
+
+As described on the [FreeBSD Wiki](https://wiki.freebsd.org/Sound#virtual_oss_.28advanced.29),
+the `virtual_oss(8)` package exists to work around this limitation:
+
+```bash
+pkg install virtual_oss
+```
+
+First, check the contents of `/dev/sndstat` to determine your device numbering:
+
+```
+$ cat /dev/sndstat
+Installed devices:
+pcm0: <vendor 0x046d HD Pro Webcam C920> (rec)
+pcm1: <Audioengine Ltd. Audioengine 2+> (play) default
+```
+
+Since my output (`-O`) device has index 1, and my recording (`-R`) device has
+index 0, I'll enable `virtual_oss` like so:
+
+```bash
+sysrc -v virtual_oss_enable=YES
+sysrc -v virtual_oss_dsp='-T /dev/sndstat -C 2 -c 2 -S -r 48000 -b 16 -s 25ms -O /dev/dsp1 -R /dev/dsp0 -d dsp'
+```
+
+You'll need to set the `-r` flag to the sample rate and `-b` to the bit depth of your device
+(you can usually find these values in `dmesg`).
+If your devices have different sample rates, the `-S` flag enables automatic resampling.
+
+The `-s` flag sets the buffer size. I had to increase this to `25ms` to avoid clipping.
+
+### Device Permissions via devfs
+
+You should create a custom `devfs(8)` ruleset to allow unprivileged users to access
+various hardware devices. Create `/etc/devfs.rules` with the following:
+
+```ini
+# /etc/devfs.rules
+
+[localrules=1000]
+add path 'drm/*' mode 0660 group operator
+add path 'backlight/*' mode 0660 group operator
+add path 'video*' mode 0660 group operator
+add path 'usb/*' mode 0660 group operator
+```
+
+If you plan on burning CDs, you will need a few more lines. First, check
+the output of `camcontrol devlist` to determine the `pass(4)` device associated
+with your CD burner:
+
+```
+$ camcontrol devlist
+<AHCI SGPIO Enclosure 2.00 0001> at scbus0 target 0 lun 0 (ses0,pass0)
+<CL1-3D256-Q11 NVMe SSSTC 256GB 22301116> at scbus1 target 0 lun 1 (pass1,nda0)
+<HL-DT-ST BD-RE BU40N 1.03> at scbus2 target 0 lun 0 (cd0,pass2)
+```
+
+In my case, `cd0` is associated with `pass2`, so I will add the following:
+
+```ini
+add path 'xpt*' mode 0660 group operator
+add path 'cd*' mode 0660 group operator
+add path 'pass2' mode 0660 group operator
+```
+
+Be sure to set the default ruleset like so:
+
+```bash
+sysrc -v devfs_system_ruleset=localrules
+```
+
+### USB Power Saving
+
+If you're using a laptop, you'll want to power down inactive USB devices
+to save battery life.
+
+Add the following to `/etc/rc.local`:
+
+```bash
+# /etc/rc.local
+
+usbconfig | awk -F: '{ print $1 }' | xargs -rtn1 -I% usbconfig -d % power_save
+```
+
+### Suspend on Lid Close
+
+For laptops, you'll need a `devd` rule to automatically suspend when the lid is
+closed. Create `/etc/devd/lid-close.conf` with the following:
+
+```
+# /etc/devd/lid-close.conf
+
+notify 20 {
+ match "system" "ACPI";
+ match "subsystem" "Lid";
+ match "notify" "0x00";
+ action "/usr/local/libexec/kde-lock-and-suspend";
+};
+
+```
+
+On FreeBSD, you can enter sleep mode by running `acpiconf -s3`. But if we're
+logged into a desktop session, we'd like to make sure our screen is locked first.
+
+Long ago, in a more sensible time, we'd just run `pkill -USR1 xidle` to lock the
+screen. Sadly, KDE requires that we enter the teenage wasteland of D-Bus to accomplish
+this.
+
+Create `/usr/local/libexec/kde-lock-and-suspend` like so:
+
+```bash
+#!/bin/sh
+
+# /usr/local/libexec/kde-lock-and-suspend
+
+# For any active KDE session, lock the screen via dbus.
+/usr/local/bin/qdbus-qt5 --literal --system \
+ org.freedesktop.ConsoleKit \
+ /org/freedesktop/ConsoleKit/Manager \
+ org.freedesktop.ConsoleKit.Manager.GetSessions \
+ | /usr/bin/sed 's/^.*\(Session[0-9]*\).*$/\1/' \
+ | /usr/bin/xargs -rtn1 -I% \
+ /usr/local/bin/qdbus-qt5 --system \
+ org.freedesktop.ConsoleKit \
+ /org/freedesktop/ConsoleKit/% \
+ org.freedesktop.ConsoleKit.Session.Lock
+
+# Give the previous command some time to complete.
+sleep 0.5
+
+# Suspend!
+/usr/sbin/acpiconf -s3
+```
+
+Don't forget to make it executable:
+
+```bash
+chmod 755 /usr/local/libexec/kde-lock-and-suspend
+```
+
+### ThinkPad Backlight Controls
+
+I had to do a bit of work to get the backlight keys working on my ThinkPad
+X1 Carbon.
+
+First make sure the `acpi_ibm` kernel module is loaded:
+
+```bash
+kldload acpi_ibm
+```
+
+Then, set the following sysctl to allow `devd(8)` to handle the button events:
+
+```bash
+sysctl dev.acpi_ibm.0.handlerevents='0x10 0x11'
+```
+
+We'll need a `devd(8)` rule to handle these events. Create `/etc/devd/thinkpad-brightness.conf`
+with the following:
+
+```
+# /etc/devd/thinkpad-brightness.conf
+
+notify 20 {
+  match "system" "ACPI";
+  match "subsystem" "IBM";
+  match "notify" "0x10";
+  action "/usr/local/libexec/thinkpad-brightness up";
+};
+
+notify 20 {
+  match "system" "ACPI";
+  match "subsystem" "IBM";
+  match "notify" "0x11";
+  action "/usr/local/libexec/thinkpad-brightness down";
+};
+```
+
+Finally, create the following script at `/usr/local/libexec/thinkpad-brightness`:
+
+```bash
+#!/bin/sh
+
+# /usr/local/libexec/thinkpad-brightness
+
+case $1 in
+ up)
+ if [ "$cur" -ge 50 ]; then
+ delta=10
+ elif [ "$cur" -ge 10 ]; then
+ delta=5
+ else
+ delta=2
+ fi
+
+ /usr/bin/backlight incr "$delta"
+ ;;
+ down)
+ if [ "$cur" -le 10 ]; then
+ delta=2
+ elif [ "$cur" -le 50 ]; then
+ delta=5
+ else
+ delta=10
+ fi
+
+ /usr/bin/backlight decr "$delta"
+ ;;
+esac
+```
+
+Don't forget to make it executable:
+
+```bash
+chmod 755 /usr/local/libexec/thinkpad-brightness
+```
+
+### Reboot
+
+Reboot to apply these changes and make sure you didn't break anything:
+
+```bash
+reboot
+```
+
+## PF Firewall
+
+It's sensible to block unexpected incoming connections. Create `/etc/pf.conf` with
+the following:
+
+```bash
+# /etc/pf.conf
+
+# Replace this with the names of your network interfaces.
+egress = "{ em0, wlan0, ue0 }"
+
+allowed_tcp_ports = "{ ssh }"
+# If you do any voice/video chats, you may need to open UDP ports for RTP.
+allowed_udp_ports = "{ 1024:65535 }"
+
+set block-policy return
+set skip on lo
+
+scrub in on $egress all fragment reassemble
+antispoof quick for $egress
+
+block all
+pass out quick on $egress inet
+pass in quick on $egress inet proto icmp all icmp-type { echoreq, unreach }
+
+pass in quick on $egress inet proto tcp to port $allowed_tcp_ports
+pass in quick on $egress inet proto udp to port $allowed_udp_ports
+```
+
+Activate the firewall:
+
+```bash
+sysrc -v pf_enable=YES
+service pf start
+```
+
+## Disable Periodic Scripts
+
+Out of the box, FreeBSD includes a bunch of `periodic(8)` scripts that churn
+through your hard disk, reach out to the internet, and send emails. I disable
+most of these. You can check `/etc/defaults/periodic.conf` for a full list.
+
+```bash
+sysrc -v -f /etc/periodic.conf \
+ daily_backup_aliases_enable=NO \
+ daily_backup_gpart_enable=NO \
+ daily_backup_passwd_enable=NO \
+ daily_clean_disks_verbose=NO \
+ daily_clean_hoststat_enable=NO \
+ daily_clean_preserve_verbose=NO \
+ daily_clean_rwho_verbose=NO \
+ daily_clean_tmps_verbose=NO \
+ daily_show_info=NO \
+ daily_show_success=NO \
+ daily_status_disks_enable=NO \
+ daily_status_include_submit_mailq=NO \
+ daily_status_mail_rejects_enable=NO \
+ daily_status_mail_rejects_enable=NO \
+ daily_status_mailq_enable=NO \
+ daily_status_network_enable=NO \
+ daily_status_security_enable=NO \
+ daily_status_uptime_enable=NO \
+ daily_status_world_kernel=NO \
+ daily_status_zfs_zpool_list_enable=NO \
+ daily_submit_queuerun=NO \
+ monthly_accounting_enable=NO \
+ monthly_show_info=NO \
+ monthly_show_success=NO \
+ monthly_status_security_enable=NO \
+ security_show_info=NO \
+ security_show_success=NO \
+ security_status_chkmounts_enable=NO \
+ security_status_chksetuid_enable=NO \
+ security_status_chkuid0_enable=NO \
+ security_status_ipf6denied_enable=NO \
+ security_status_ipfdenied_enable=NO \
+ security_status_ipfwdenied_enable=NO \
+ security_status_ipfwlimit_enable=NO \
+ security_status_kernelmsg_enable=NO \
+ security_status_logincheck_enable=NO \
+ security_status_loginfail_enable=NO \
+ security_status_neggrpperm_enable=NO \
+ security_status_passwdless_enable=NO \
+ security_status_pfdenied_enable=NO \
+ security_status_tcpwrap_enable=NO \
+ weekly_locate_enable=NO \
+ weekly_show_info=NO \
+ weekly_show_success=NO \
+ weekly_status_security_enable=NO \
+ weekly_whatis_enable=NO
+```
+
+## Add Users
+
+Create your local user account. Make sure to add yourself to the `operator` and
+`wheel` groups.
+
+```bash
+pw useradd \
+ -n robertlee \
+ -c 'Robert E. Lee' \
+ -s /bin/sh \
+ -M 700 \
+ -d /home/robertlee \
+ -G operator,wheel
+```
+
+You'll probably want to install `sudo`:
+
+```bash
+pkg install sudo
+```
+
+Update `/usr/local/etc/sudoers` to give sudo permissions to the `wheel` group:
+
+```
+# /usr/local/etc/sudoers
+
+%wheel ALL=(ALL:ALL) ALL
+```
+
+## Set Locale
+
+Set your locale for login shells in `/etc/login.conf`. Modify this file as shown
+below with your preferred locale:
+
+```diff
+--- login.conf
++++ login.conf
+@@ -23,7 +23,9 @@
+ :umtxp=unlimited:\
+ :priority=0:\
+ :ignoretime@:\
+- :umask=022:
++ :umask=022:\
++ :charset=UTF-8:\
++ :lang=en_US.UTF-8:
+```
+
+Rebuild the login database to apply this change:
+
+```bash
+cap_mkdb /etc/login.conf
+```
+
+For non-login shells, create `/etc/profile.d/locale.sh` like so:
+
+```bash
+# /etc/profile.d/locale.sh
+
+export LANG=en_US.UTF-8
+export CHARSET=UTF-8
+```
+
+## Enable NTP
+
+`ntpd(8)` will keep your system clock up-to-date.
+
+Edit `/etc/ntp.conf` with your preferred NTP servers:
+
+```
+tos minclock 3 maxclock 6
+
+pool 0.freebsd.pool.ntp.org iburst
+pool 2.freebsd.pool.ntp.org iburst
+
+restrict default limited kod nomodify notrap noquery nopeer
+restrict source limited kod nomodify notrap noquery
+
+restrict 127.0.0.1
+restrict ::1
+
+leapfile "/var/db/ntpd.leap-seconds.list"
+```
+
+Start `ntpd`:
+
+```bash
+sysrc -v ntpd_enable=YES ntpd_sync_on_start=YES
+service ntpd start
+```
+
+## Set the Timezone
+
+In case you didn't do this during the installation, set your timezone:
+
+```bash
+cp -v /usr/share/zoneinfo/America/New_York /etc/localtime
+```
+
+## Switch to openssh-portable
+
+The `ssh` in FreeBSD's base system is heavily patched with stuff I don't use.
+I prefer to use the vanilla `openssh-portable` from ports.
+
+```bash
+pkg install openssh-portable
+```
+
+Edit `/usr/local/etc/ssh/sshd_config` as appropriate:
+
+```bash
+# /usr/local/etc/ssh/sshd_config
+
+PermitRootLogin prohibit-password
+
+UsePAM yes
+UseDNS no
+
+Subsystem sftp /usr/local/libexec/sftp-server
+```
+
+Replace the running `sshd` with the new one:
+
+```bash
+sysrc -v sshd_enable=NO openssh_enable=YES
+service sshd stop
+service openssh start
+```
+
+The `ssh` command will continue using the base system's `/usr/bin/ssh` unless
+you update your `$PATH`.
+
+You can edit `/etc/login.conf` to make the change for all users:
+
+```diff
+--- login.conf
++++ login.conf
+@@ -4,7 +4,7 @@
+ :welcome=/var/run/motd:\
+ :setenv=BLOCKSIZE=K:\
+ :mail=/var/mail/$:\
+- :path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\
++ :path=/sbin /bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin ~/bin:\
+ :nologin=/var/run/nologin:\
+ :cputime=unlimited:\
+ :datasize=unlimited:\
+```
+
+Rebuild the login database to apply this change:
+
+```bash
+cap_mkdb /etc/login.conf
+```
+
+## Fix 256-color XTerm
+
+The `termcap(5)` database on FreeBSD has a [bug](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=280679)
+which prevents "bright" colors from rendering on `xterm`-like terminals.
+
+You can fix it with the following line noise:
+
+```bash
+mkdir -p /usr/local/share/site-terminfo
+cat <<'EOF' | tic -o /usr/local/share/site-terminfo -
+xterm-256color|xterm with 256 colors,
+ am, bce, ccc, km, mc5i, mir, msgr, npc, xenl,
+ colors#0x100, cols#80, it#8, lines#24, pairs#0x10000,
+ acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+ bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
+ clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r,
+ csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
+ cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C,
+ cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
+ cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m,
+ dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K,
+ el1=\E[1K, flash=\E[?5h$<100/>\E[?5l, home=\E[H,
+ hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@,
+ il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS,
+ initc=\E]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
+ invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, kDC=\E[3;2~,
+ kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, kLFT=\E[1;2D,
+ kNXT=\E[6;2~, kPRV=\E[5;2~, kRIT=\E[1;2C, ka1=\EOw,
+ ka3=\EOy, kb2=\EOu, kbs=^?, kc1=\EOq, kc3=\EOs, kcbt=\E[Z,
+ kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,
+ kdch1=\E[3~, kend=\EOF, kent=\EOM, kf1=\EOP, kf10=\E[21~,
+ kf11=\E[23~, kf12=\E[24~, kf13=\E[1;2P, kf14=\E[1;2Q,
+ kf15=\E[1;2R, kf16=\E[1;2S, kf17=\E[15;2~, kf18=\E[17;2~,
+ kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, kf21=\E[20;2~,
+ kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~,
+ kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S,
+ kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~,
+ kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~,
+ kf35=\E[23;5~, kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q,
+ kf39=\E[1;6R, kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~,
+ kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~,
+ kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~,
+ kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q,
+ kf51=\E[1;3R, kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~,
+ kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~,
+ kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~,
+ kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~,
+ kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~,
+ kind=\E[1;2B, kmous=\E[<, knp=\E[6~, kpp=\E[5~,
+ kri=\E[1;2A, mc0=\E[i, mc4=\E[4i, mc5=\E[5i, meml=\El,
+ memu=\Em, mgc=\E[?69l, nel=\EE, oc=\E]104\007,
+ op=\E[39;49m, rc=\E8, rep=%p1%c\E[%p2%{1}%-%db,
+ rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B,
+ rmam=\E[?7l, rmcup=\E[?1049l\E[23;0;0t, rmir=\E[4l,
+ rmkx=\E[?1l\E>, rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m,
+ rs1=\Ec\E]104\007, rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7,
+ setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
+ setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
+ sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
+ sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
+ smcup=\E[?1049h\E[22;0;0t,
+ smglr=\E[?69h\E[%i%p1%d;%p2%ds, smir=\E[4h,
+ smkx=\E[?1h\E=, smm=\E[?1034h, smso=\E[7m, smul=\E[4m,
+ tbc=\E[3g, u6=\E[%i%d;%dR, u7=\E[6n,
+ u8=\E[?%[;0123456789]c, u9=\E[c, vpa=\E[%i%p1%dd,
+EOF
+```
+
+## Install Root Certificates
+
+FreeBSD trusts a limited number of certificate authorities out of the box.
+Install the root CA bundle from Mozilla to trust all the standard ones:
+
+```bash
+pkg install ca_root_nss
+```
+
+## Install KDE and Desktop Applications
+
+Grab a cup of coffee and kick back while you install KDE and all the desktop packages.
+You might not want all of these, they're just what I find useful.
+
+```bash
+pkg install \
+ audacious-plugins-qt5 \
+ audacious-qt5 \
+ chromium \
+ digikam \
+ elisa \
+ en-hunspell \
+ firefox \
+ freedesktop-sound-theme \
+ k3b \
+ kde5 \
+ kid3-kf5 \
+ kmix \
+ konversation \
+ libreoffice \
+ libva-utils \
+ libvdpau-va-gl \
+ merkuro \
+ sddm \
+ signal-desktop \
+ thunderbird \
+ vdpauinfo \
+ xorg
+```
+
+## Install Fonts
+
+You'll find that some websites don't render quite right without these fonts installed:
+
+```bash
+pkg install \
+ cantarell-fonts \
+ droid-fonts-ttf \
+ inconsolata-ttf \
+ noto-basic \
+ noto-emoji \
+ roboto-fonts-ttf \
+ ubuntu-font \
+ webfonts
+```
+
+And of course, how can you live without [Terminus](https://files.ax86.net/terminus-ttf/):
+
+```bash
+pkg install terminus-font terminus-ttf
+```
+
+If you want to use the bitmapped version, you'll need to update `xorg.conf.d`:
+
+```
+# /usr/local/etc/X11/xorg.conf.d/terminus.conf
+
+Section "Files"
+ FontPath "/usr/local/share/fonts/terminus-font/"
+EndSection
+```
+
+## Enable D-Bus
+
+D-Bus is required for KDE and just about every GUI application these days.
+
+```bash
+sysrc -v dbus_enable=YES
+service dbus start
+```
+
+## Configure SDDM
+
+[sddm](https://github.com/sddm/sddm) is the preferred login manager for KDE. Enable and start it:
+
+```bash
+sysrc -v sddm_enable=YES
+service sddm start
+```
+
+With any luck, you'll be dumped to a graphical login screen where you can launch KDE.
+
+KDE under Wayland won't even start for me. To prevent my wife from accidentally selecting
+it, I disabled Wayland in `sddm.conf(5)` like so:
+
+```ini
+# /usr/local/etc/sddm.conf
+
+[General]
+DisplayServer = x11
+
+[Wayland]
+SessionDir = /dev/null
+```
+
+Hopefully by the time Wayland runs on FreeBSD, the RedHat teenagers will have
+moved on to another display server. X11 works fine.
+
+## Known Issues
+
+This section describes what doesn't work on FreeBSD, and some potential workarounds.
+
+### User switching is broken
+
+There is a long-standing [ConsoleKit2 bug](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=221452)
+that prevents user switching from working reliably on FreeBSD.
+
+There is [another bug](https://github.com/freebsd/drm-kmod/issues/175) that results in broken graphics
+acceleration whenever a VT switch is performed.
+
+Therefore, it's best to just disable user switching for now. You can disable it for
+all users by adding the following to `/usr/local/etc/xdg/kdeglobals`:
+
+```
+# /usr/local/etc/xdg/kdeglobals
+
+[KDE Action Restrictions]
+action/start_new_session=false
+action/switch_user=false
+```
+
+### Processes aren't killed on logout
+
+On FreeBSD, multiple processes remain running indefinitely after logging out
+of a KDE session. Chromium is especially annoying: it continues running after logout, but gets
+trapped in some crazy state where it consumes 100% of a CPU core forever.
+
+I imagine the KDE developers are mostly concerned with systemd-based Linux distributions,
+where `logind` ensures that all processes associated with a user session are forcibly killed
+when the user logs out. So this probably isn't on anyone's radar.
+
+Luckily, KDE Plasma has the ability to run a cleanup script whenever a user logs out. Create the
+following directory:
+
+```bash
+mkdir -p /usr/local/etc/xdg/plasma-workspace/shutdown
+```
+
+Then create `/usr/local/etc/xdg/plasma-workspace/shutdown/cleanup.sh` like so:
+
+```bash
+#!/bin/sh
+
+# /usr/local/etc/xdg/plasma-workspace/shutdown/cleanup.sh
+
+# Some processes don't kill themselves when the X server dies.
+# This script takes care of them.
+pkill signal-desktop chrome baloo_file dirmngr
+pkill -f /usr/local/libexec/geoclue-2.0/demos/agent
+```
+
+Don't forget to make this file executable:
+
+```bash
+chmod +x /usr/local/etc/xdg/plasma-workspace/shutdown/cleanup.sh
+```
+
+### Baloo creates a gazillion .nfs files
+
+If you have NFS-mounted home directories (like I do), you may notice that your
+homedir is littered with hundreds of `.nfs` files.
+
+KDE comes with a file indexing service called Baloo. Baloo seems to keep lots of open file
+handles on the files it's indexing. If these files get deleted, the NFS [silly rename](https://linux-nfs.org/wiki/index.php/Server-side_silly_rename)
+hack is triggered, causing a bunch of `.nfs` files to be created.
+
+I've read that Baloo doesn't index network mounts, but apparently it does (at least on FreeBSD).
+
+You can disable Baloo entirely by creating `/usr/local/etc/xdg/baloofilerc` with the following:
+
+```ini
+# /usr/local/etc/xdg/baloofilerc
+
+[Basic Settings]
+Indexing-Enabled=false
+```
+
+### Hardware video acceleration broken in Chromium
+
+Video playback in Chromium is supposed to be hardware-accelerated using [VA-API](https://wiki.archlinux.org/title/Hardware_video_acceleration).
+However, despite installing `libva-intel-media-driver`, my CPU was pegged at 60% or so when watching a YouTube video.
+
+I blindly copy-pasted dozens of command-line flags for Chromium until I found the
+incantation that enabled hardware-accelerated video playback:
+
+```bash
+chrome --enable-features=Vulkan,VulkanFromANGLE,DefaultANGLEVulkan
+```
+
+With those magic flags, CPU usage does not noticeably increase during video playback.
+
+To make this change permanent, you can use a custom `.desktop` override for Chromium.
+First, create a new directory to hold our custom `.desktop` files:
+
+```bash
+mkdir -p /usr/local/share-override/applications
+```
+
+Then, you must set the `$XDG_DATA_DIRS` environment variable so your new directory takes precedence:
+
+```diff
+--- login.conf
++++ login.conf
+@@ -2,7 +2,7 @@
+ :passwd_format=sha512:\
+ :copyright=/etc/COPYRIGHT:\
+ :welcome=/var/run/motd:\
+- :setenv=BLOCKSIZE=K:\
++ :setenv=BLOCKSIZE=K,XDG_DATA_DIRS=/usr/local/share-override\c/usr/local/share:\
+ :mail=/var/mail/$:\
+ :path=/sbin /bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin ~/bin:\
+ :nologin=/var/run/nologin:\
+```
+
+And, as usual:
+
+```bash
+cap_mkdb /etc/login.conf
+```
+
+Finally, create a custom `chromium-browser.desktop` file like so:
+
+```ini
+# /usr/local/share-override/desktop/chromium-browser.desktop
+
+[Desktop Entry]
+Type=Application
+Version=1.0
+Encoding=UTF-8
+Name=Chromium
+Comment=Google web browser based on WebKit
+Icon=chrome
+Exec=chrome --enable-features=Vulkan,VulkanFromANGLE,DefaultANGLEVulkan %U
+Categories=Application;Network;WebBrowser;
+MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
+StartupNotify=true
+```
+
+Luckily, video acceleration in Firefox seems to work properly without any fiddling.
+
+### Screen tearing
+
+Until the X11 people cut a release containing [this PR](https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1006),
+you will experience some screen tearing with the default modesetting driver.
+
+Allegedly, you can install the `xf86-video-intel` driver and enable the `TearFree` option in `xorg.conf`.
+However, I believe this driver is largely unmaintained, so YMMV.
+
+I found that screen tearing is greatly reduced by setting **Latency** to *Force lowest latency* under
+System Settings → Display and Monitor → Compositor.
+
+Please do not tell me to use Wayland. At the time of this writing, it does not work.
diff --git a/src/blog/freebsd-14-on-the-desktop/kde5.jpg b/src/blog/freebsd-14-on-the-desktop/kde5.jpg
new file mode 100644
index 0000000..66cac82
--- /dev/null
+++ b/src/blog/freebsd-14-on-the-desktop/kde5.jpg
Binary files differ