My 2018 Mac mini (64G RAM, 2T SSD) has long been a trusty multi-VM pkgsrc and notqmail build machine, mostly via SSH. And during the first couple COVID years when I was still consulting independently but we were out of the country, it was also a trusty low-latency system for collaborative coding sessions with USA-based clients, mostly via screen sharing.
The mini still performs quite well. I still rely on it for keeping my NetBSD VPS running on the latest -current pkgsrc every week or so. But macOS NFS service had a tendency to be a source of annoyance and/or effort on each new major release. NetBSD’s NFS client got fixed, which was enough to get me by, but my Tribblix and Linux VMs had already been basically unusable for a while. And macOS had lately gotten a little unstable after reboot: sometimes misrendering the login screen, freezing after a correctly entered password, or suddenly pegging the fans for no apparent reason and powering abruptly off. So when macOS Tahoe dropped support for nearly all Intel Macs, I was already game to repave mine.
I generally prefer NetBSD when possible, and generally consider my non-NetBSD systems to be only temporarily so (e.g., Small ARMs).
Hosting a pile of nvmm-accelerated VMs while also building natively for my primary NetBSD production target would have been a solid use case.
But the 2018 mini has a T2 security chip that makes a bunch of basic onboard devices relatively difficult for an OS to attach, and Linux appears to be the only free OS that mostly deals with this.
Even then, we’ll need a T2-customized installer and special attention.
1. Prepare installer
$ cd ~/Downloads
$ bash <<<1 <(curl -sL https://github.com/t2linux/T2-Mint/releases/latest/download/iso.sh)
$ sudo dd if=linuxmint-*-cinnamon-*-t2-*.iso of=/dev/$YOUR_USB_STICK
$ rm -f linuxmint-*-cinnamon-*-t2-*.iso
2. Prepare machine
- Reboot and hold down Command-R.
- In macOS Recovery, choose Utilities -> Startup Security Utility.
- Secure Boot:
No Security. - Allowed Boot Media:
Allow booting from external or removable media. - Connect USB keyboard/mouse/Ethernet directly (not via Bluetooth or Thunderbolt dock).
- Quit Startup Security Utility.
3. Install
- Reboot and hold down Option.
- Choose your USB stick.
- In the live environment, open Terminal.
- Wipe, partition, and format the disk:
$ for i in \ "mklabel gpt" \ "mkpart ESP fat32 1MiB 513MiB" \ "set 1 esp on" \ "set 1 boot on" \ "mkpart Root btrfs 513MiB 100%"; do sudo parted $YOUR_DISK_DEVICE $i done $ sudo mkfs.fat -F32 -n ESP ${YOUR_DISK_DEVICE}p1
- In the live environment, run Install.
- Instead of “Erase disk and install Linux Mint”, choose “Something else”.
- Click the btrfs partition -> “Change…”.
- Use as:
btrfs journaling file system. - Format the partition:
[x]. - Mount point:
/. - ”Install Now” and follow the prompts until “Installation Complete”.
- DO NOT click “Continue Testing” or “Restart Now”. We’re not ready for the new install to be unmounted.
4. Tweak new install
- Enter newly installed environment:
$ for i in proc dev dev/pts; do sudo mount -B /$i /target/$i done $ sudo chroot /target
- From now on, track configuration changes in
git:# echo | apt install etckeeper # cd /etc # git branch -m pet-power-plant # git gc --prune
- Configure
grub:# echo 'GRUB_RECORDFAIL_TIMEOUT=0' > default/grub.d/60_skip_grub_prompt.cfg # etckeeper commit -m 'Skip grub prompt.' # update-grub
- Give Mac boot picker a custom icon:
# apt install libarchive-tools # curl -sL https://master.dl.sourceforge.net/project/mac-icns/mac-icns.iso \ | bsdtar -xOf- iconverticons.com/os_linuxmint.icns \ > /boot/efi/.VolumeIcon.icns
- Return to the “Installation Complete” dialog (finally!) and click “Restart Now”.
5. Before first boot
- When prompted, remove USB stick and press Enter.
- On reboot, hold down Command-R.
- In macOS Recovery, choose Utilities -> Terminal.
- Give Mac boot picker a custom label:
# diskutil list # diskutil mount /dev/$YOUR_EFI_SYSTEM_PARTITION_DEVICE # bless --folder /Volumes/ESP/EFI/BOOT --label "Linux Mint"
- Reboot and hold down Option.
- Observe the icon and label. Fancy! Someday you’ll hold down Option again, and this’ll help you disambiguate which volume you’re trying to boot.
6. First boot
- Go for it!
- Observe no
grubprompt, just straight through the Mint logo to the login screen. - Log in.
- Enable passwordless
sudo:$ echo '%sudo ALL=(ALL: ALL) NOPASSWD: ALL' \ | sudo tee /etc/sudoers.d/10sudo_nopasswd >/dev/null $ sudo chmod 440 /etc/sudoers.d/10sudo_nopasswd $ sudo etckeeper commit -m 'Skip sudo password prompt.'
- Allowlist your Thunderbolt dock, if any:
$ boltctl list # find your device's UUID $ sudo boltctl enroll --policy auto $YOUR_THUNDERBOLT_UUID
- Fetch WiFi and Bluetooth firmware:
$ sudo apt install dmg2img $ echo 7 | sudo get-apple-firmware get_from_online
- Connect Ethernet/WiFi/mouse/keyboard as you prefer.
- Enable T2 fan control and SSH service:
$ echo | sudo apt install t2fanrd openssh-server $ sudo systemctl enable --now ssh $ sudo etckeeper commit -m 'Enable sshd.'
- Update device firmware: (Note that the Mac’s EFI can’t be updated this way. The only way to update Mac EFI is as a side effect of installing the latest macOS.)
$ echo y | sudo fwupdmgr get-updates
- Follow the post-install Welcome prompts.
- In “Account details”, add my photo.
- Install some basics:
$ echo | sudo apt install tmux vim myrepos tig silversearcher-ag qemu-system-x86-64 kdeconnect dropbox
7. Back to work
- Mount source trees from NAS. (Oh hey, I finally have a NAS! Similar post forthcoming about that.)
- Create a NetBSD VM to match my production VPS.
- Build a fresh batch of packages.
- Carry on with life.
I’ve got some older Mac minis that may also soon find gainful employment around here.
One of the few things I know about Minecraft is, some players I know and love could be playing together if someone were to run a server for them. That’s the sort of thing I’d gladly do, provided I could approximately never again pay attention to it.
Here’s what I came up with. Let’s see how it pans out.
Prerequisites
My Virtual Private Server at Panix is NetBSD/amd64 with plenty of RAM, disk, and network headroom. Marginal additional usage is therefore free.
System- and pkgsrc-provided services are controlled by the usual NetBSD-style rc.d scripts.
Site-specific services are supervised, including supervision trees controlled by each user. In the following excerpt from my running system:
rootstarts sniproxynotqmailruns a web site and its Let’s Encrypt autorenewal,schmonzruns a few of each- all these services were bounced yesterday after a regular package update
/service/sniproxy: up (pid 11133) 81141 seconds
/service/svscan-notqmail: up (pid 325) 846389 seconds
/home/notqmail/service/renewssl.www.notqmail.org: up (pid 20951) 81141 seconds
/home/notqmail/service/www.notqmail.org: up (pid 24539) 81141 seconds
/service/svscan-schmonz: up (pid 394) 846389 seconds
/home/schmonz/service/agilein3minut.es: up (pid 26325) 81141 seconds
/home/schmonz/service/latentagility.com: up (pid 24554) 81141 seconds
/home/schmonz/service/renewssl.agilein3minut.es: up (pid 18299) 81141 seconds
/home/schmonz/service/renewssl.latentagility.com: up (pid 14394) 81141 seconds
/home/schmonz/service/renewssl.schmonz.com: up (pid 12424) 81140 seconds
/home/schmonz/service/schmonz.com: up (pid 21449) 81141 seconds
These packages were already installed:
djbdns-runs6-portable-utilsunzipcurlpy-html2textjq
I’ve added these:
openjdk21execline
I’m installing Minecraft’s server software manually, so it’ll be my job to notice when to update. Gotta automate the noticing, at least.
Oh, and I need a sensible process-supervision run script for the Minecraft server.
The script needs to solve two application-specific system-integration problems:
- The usual supervision signals cause the server to terminate without saving the state of the world, which seems… rude
- The usual way to configure a running server is to get on the console and type commands into it, which implies having started it inside a tmux session (which, I love
tmux, but not for this)
We solve for (1) by running the server as a child process and translating supervision signals into Minecraft commands.
We solve for (2) by connecting a named pipe to the server’s standard input.
Then commands can be sent to the server using nothing but echo and ordinary output redirection.
Without even really being asked, my brain instantly produced the mechanisms for (1) in Perl.
Then someone on #s6 shared a run script that additionally solved (2) while being more than twice as short as mine.
How?
By writing it in execline, a language expressly designed to be this kind of glue.
Step by step: Supervising Minecraft service
1. Create dedicated Unix user with its own process supervisor
useradd -m minecraft
mkdir -p /etc/service/svscan-minecraft/log
cd /etc/service/svscan-minecraft
cat > log/run <<'EOF'
#!/bin/sh
exec setuidgid minecraft logger -t svscan-minecraft -p daemon.info
EOF
chmod +x log/run
cat > run <<'EOF'
#!/bin/sh
exec 2>&1
exec setuidgid minecraft argv0 svscan svscan-minecraft /home/minecraft/service
EOF
chmod +x run
ln -s /etc/service/svscan-minecraft /var/service/
2. Globally, map hostname to IP
echo '+minecraft.schmonz.com:my.server.ip.here' >> /etc/tinydns/data
service tinydns reload
3. Locally, map port number to service name
echo 'minecraft 25565/tcp # minecraft.schmonz.com' >> /etc/services
service sysdb services
4. Allow incoming connections
$EDITOR /etc/npf.conf # add minecraft to $services_tcp
service npf reload
5. Download software
su minecraft
cd
mkdir -p sites/schmonz.com/minecraft/service
cd sites/schmonz.com/minecraft
mkdir bin
curl -o bin/server.jar https://piston-data.mojang.com/v1/objects/e6ec2f64e6080b9b5d9b471b291c33cc7f509733/server.jar
6. Accept EULA
mkdir data
echo 'eula=true' > data/eula.txt
7. Create control socket
mkfifo -m 0600 data/control
8. Symlink service logs where I usually look
ln -s data/logs logs
9. Create run script
cat > service/run <<'EOF'
#!/opt/pkg/bin/execlineb -P
fdmove -c 2 1
cd /home/minecraft/sites/schmonz.com/minecraft/data
redirfd -w -nb 3 control
trap -x
{
SIGINT { fdmove 1 3 s6-echo stop }
SIGHUP { fdmove 1 3 s6-echo stop }
SIGTERM { fdmove 1 3 s6-echo stop }
SIGPIPE { fdmove 1 3 s6-echo stop }
}
fdclose 3
redirfd -r 0 control
java -Xmx1024M -Xms1024M -jar ../bin/server.jar --nogui
EOF
chmod +x service/run
10. Enable service
ln -s /home/minecraft/sites/schmonz.com/minecraft/service ~/service/minecraft.schmonz.com
11. Send initial configuration
cat > data/control <<'EOF'
whitelist on
whitelist add so-and-so
save-on
EOF
Be notified of updates
As the minecraft user:
1. Create service and run script
cd ~/sites/schmonz.com/minecraft
mkdir -p update/service/log
cd update/service
cat > log/run <<'EOF'
#!/bin/sh
exec multilog t ./main
EOF
chmod +x log/run
cat > run <<'EOF'
#!/bin/sh
set -e
set -x
exec 2>&1
MINECRAFT_VERSIONS_ENDPOINT='https://piston-meta.mojang.com/mc/game/version_manifest.json'
running_version() {
unzip -p ../bin/server.jar version.json \
| jq -r .name
}
available_version() {
curl \
--silent \
--user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" \
"${MINECRAFT_VERSIONS_ENDPOINT}" \
| jq -r .latest.release
}
main() {
cd /home/minecraft/sites/schmonz.com/minecraft/data
while true; do
running="$(running_version)"
available="$(available_version)"
if [ "${running}" != "${available}" ]; then
echo "MINECRAFT SERVER UPDATE NEEDED: ${running} -> ${available}"
echo "" | mail -s "Please update Minecraft server: ${running} -> ${available}" your@email.address.here
fi
sleep 250000
done
}
main "$@"
exit $?
EOF
chmod +x run
2. Enable service
ln -s /home/minecraft/sites/schmonz.com/minecraft/update/service ~/service/update.minecraft.schmonz.com
Further improvements
If it’s desirable to have the server periodically auto-save, I’ll add one more service that writes save-all to data/control.
If pkgsrc had a minecraft-server package (FreeBSD Ports does), I could delete my ad hoc update-notification service.
Maybe I’ll make it so.
I do like packaging things as part of my area-under-the-curve strategy.
My host-specific supervision trees and pkgsrc’s djbware rc.d scripts (qmailsmtpd, for instance) are still using daemontools.
I’ve had the intention to switch to s6 for a long time.
When it happens, I’ll be happy about it.
I haven’t tried to understand why there are “Java” and “Bedrock” editions of Minecraft, but it seems like they don’t interoperate. I’m sure there are reasons for this product segmentation and that I’ll come to better understand them in the fullness of time. Meanwhile, if and when I need to add Bedrock client interop, Geyser looks like an easy option — once I’m running something other than the vanilla upstream Minecraft Java server. I’ll need to try to understand that whole situation, too.
Are you an experienced Minecraft server administrator? Please share tips and advice!
We have a 2017 13” MacBook Air that’s periodically of interest to the kids, but Apple doesn’t offer macOS updates for it beyond Monterey (12.x).
I just installed OpenCore Legacy Patcher on it, clicked “Build and Install OpenCore”, rebooted, ran Software Update again, and was offered Sonoma (14.x), the latest available macOS at the time of writing.
Upgraded, rebooted, ran OpenCore Legacy Patcher, clicked “Post-Install Root Patch”, rebooted when prompted, and that was that.
On my home network, some important jobs are performed by little ARM computers.
AirPlay to sound system
The house came with a decent sound system wired in. The receiver can take 1/8” stereo input — from AirPlay, with help from a decade-old Raspberry Pi 1 Model B Rev 2.
1. Prepare disk
With a 4GB SD card, from macOS:
$ diskutil list # inspect output
$ SDCARD=disk6
$ diskutil unmountDisk ${SDCARD}
$ links https://raspi.debian.net/tested-images/
$ DISKIMAGE=20231109_raspi_1_bookworm.img.xz
$ fetch https://raspi.debian.net/tested/${DISKIMAGE}
$ xzcat ${DISKIMAGE} \
| sudo dd of=/dev/r${SDCARD} bs=64k oflag=sync status=progress
$ diskutil eject ${SDCARD}
2. First boot
Place the RPi somewhere convenient.
Connect SD card, keyboard, HDMI, Ethernet, and power.
Log in as root, no password:
# apt update
# apt -y install etckeeper
# cd /etc
# git branch -M main
# apt -y install sudo
# visudo # for the sudo group, insert NOPASSWD: before the final ALL
# useradd -m -G sudo -s /bin/bash schmonz
# passwd schmonz
# exit
Log in as schmonz:
$ sudo passwd root
$ sudo sh -c 'echo 127.0.1.1 schleierplay >> /etc/hosts'
$ sudo hostnamectl hostname schleierplay
$ sudo ln -sf /usr/share/zoneinfo/US/Eastern /etc/localtime
$ sudo etckeeper commit -m 'Set root password, hostname, and timezone.'
$ sudo apt -y install shairport-sync
$ sudo vi /etc/shairport-sync.conf
$ sudo etckeeper commit -m 'Set AirPlay name.'
$ sudo shutdown -h now
3. Deployment
Place the RPi where it’ll live. Connect audio cable, Ethernet, and power.
$ ssh-copy-id schleierplay.local
4. Usage
Make sure receiver is set to AUX input. Use AirPlay.
5. Maintenance
As with any Debian:
$ ssh schleierplay.local -t 'sudo apt update && sudo apt -y upgrade && sudo apt -y autoremove'
To back up /etc, git push it someplace trustworthy and private.
6. Wishlist
I’d rather run NetBSD, but on 10.0 with shairport-sync, I saw a lot of AirPlay Speaker Not Available: 'House' is being used by someone else (even when it wasn’t).
AirPrint to old printer
My ancient USB-only HP LaserJet P1006 remains reliable for our basic needs and we’ve still got a pile of toner cartridges. A friend recently sent me a comparatively beefy Pine A64 board.
1. Prepare disk
With a 4GB SD card, from macOS:
$ diskutil list # inspect output
$ SDCARD=disk6
$ diskutil unmountDisk ${SDCARD}
$ links https://www.armbian.com/pine64/
$ DISKIMAGE=Armbian_24.5.1_Pine64_bookworm_current_6.6.31_minimal.img.xz
$ fetch https://dl.armbian.com/pine64/archive/${DISKIMAGE}
$ xzcat ${DISKIMAGE} \
| sudo dd of=/dev/r${SDCARD} bs=64k oflag=sync status=progress
$ diskutil eject ${SDCARD}
2. First boot
Place the A64 somewhere convenient.
Connect SD card, keyboard, HDMI, Ethernet, and power.
Follow the prompts to set the root password, create a user account, and select a locale.
Then continue:
# apt update
# apt -y install etckeeper
# cd /etc
# git branch -M main
# visudo # for the sudo group, insert NOPASSWD: before the final ALL
# exit
Log in as schmonz:
$ sudo sh -c 'echo 127.0.1.1 schleierprint >> /etc/hosts'
$ sudo hostnamectl hostname schleierprint
$ sudo ln -sf /usr/share/zoneinfo/US/Eastern /etc/localtime
$ sudo etckeeper commit -m 'Set root password, hostname, and timezone.'
$ sudo apt -y install hplip avahi-daemon
$ sudo usermod -a -G lpadmin schmonz
$ sudo etckeeper commit -m 'Make myself a printer admin.'
$ sudo shutdown -h now
3. Deployment
Place the A64 where it’ll live. Connect printer, Ethernet, and power.
$ ssh-copy-id schleierprint.local
$ ssh schleierprint.local
$ sudo hp-setup -i # follow prompts, mostly defaults; name the queue 'hpljp1006'
$ sudo etckeeper commit -m 'Add initial hplip config for P1006.'
$ sudo sed -i \
-e '/^\*ColorDevice: True$/s|True|False|' \
-e '/^\*OpenUI \*Duplex\/Double-Sided Printing: PickOne$/,/^\*CloseUI: \*Duplex$/s|^|*% |' \
-e '/^\*OpenUI \*ColorModel\/Output Mode: PickOne$/,/^\*CloseUI: \*ColorModel$/s|^|*% |' \
/etc/cups/ppd/hpljp1006.ppd
$ sudo etckeeper commit -m 'Correct advertised printer capabilities.'
$ sudo sed -i \
-e 's|^Info $|Info HP LaserJet P1006|' \
/etc/cups/printers.conf
$ sudo lpadmin -d hpljp1006
$ sudo etckeeper commit -m 'Name printer and set it as default.'
$ sudo cupsctl --remote-any
$ sudo etckeeper commit -m 'Let local network talk to CUPS.'
$ sudo sed -i \
-e '/^WebInterface /a PreserveJobFiles No' \
/etc/cups/cupsd.conf
$ sudo etckeeper commit -m 'Maybe avoid some disk writes.'
$ sudo systemctl restart cups
On macOS, do not override the generic driver with “HP LaserJet P1006”.
You won’t be able to print (with filter failed in the server logs), except that every “Supply Levels” check —
including the ones that happen as part of every print job —
will produce a piece of paper containing the single line @PJL INFO SUPPLIES.
As I understand it, some versions of CUPS have a server bug where it can’t discern whether incoming data has already been filtered for the target queue:
filters converted the data (via application/vnd.cups-raster) to the printer’s native command set (whatever that might be)… but when the job got sent to the CUPS server it was tagged as application/vnd.cups-raster rather than, say, application/octet-stream.
While that discussion is over a decade old, its advice — leave the filtering to the server, and make sure clients don’t do any — has me printing from macOS, iOS, and Windows.
4. Usage
On macOS, add the printer. When it autoselects “Generic PostScript Printer”, leave it (details in sidebar). Print.
On iOS, print.
On Windows, add the printer. Print.
5. Maintenance
As with any Debian:
$ ssh schleierprint.local -t 'sudo apt update && sudo apt -y upgrade && sudo apt -y autoremove'
To back up /etc, git push it someplace trustworthy and private.
6. Wishlist
I’d rather run NetBSD, but neither 10.0 nor -current brought up HDMI.
I could try writing NetBSD to an SD card, mounting it from another NetBSD system, setting hostname in rc.conf, adding a non-root user, and then booting the A64 from it in order to do the rest over ssh.
(Other systems that also didn’t bring up HDMI, wherefore I landed by trial and error on Armbian: FreeBSD 14, OpenBSD 7.5, Debian 12.)
AirPlay to old Sonos
Since one of my old Sonos speakers can’t be upgraded to AirPlay-compatible firmware, I’m not eager to upgrade the other. Instead, I’ve added AirConnect on the Pine A64 as an AirPlay relay.
Contents of /etc/systemd/system/airupnp.service:
[Unit]
Description=AirUPnP bridge
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/home/schmonz/bin/airupnp-linux-aarch64-static -l 1000:2000 -N '%%s' -x /home/schmonz/etc/airupnp.xml -Z
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Contents of /home/schmonz/etc/airupnp.xml (to omit my UPnP router from the AirPlay list):
<?xml version="1.0"?>
<airupnp>
<device>
<udn>uuid:1e38fc78-51f5-5f5d-9268-50c6b1dc59f8</udn>
<name>Verizon FiOS-G1100 ManageableDevice+</name>
<mac>bb:bb:bb:bb:bb:bb</mac>
<enabled>0</enabled>
</device>
</airupnp>
I’d rather install AirConnect from a system-provided package, but there isn’t one for Debian. Maybe I can puzzle out the AirConnect build system and add it to pkgsrc.
On Friday, May 10, I presented “Not So Extreme Programming” for Large Scale Scrum (LeSS) in NYC & Global.
The abstract:
Where “Agile” sounds pleasant and inclusive, “Extreme Programming”… doesn’t. But it’s a differentiator: teams practicing XP are seen to move with uncommon agility. The name has other problems, too. For one, XP is about much more than programming. For another, when compared with other ways software still gets developed, XP is much less extreme. This talk — for anyone involved with Agile in any role, at any scale — will take you through where Extreme Programming came from, where it’s going, what it requires, why it remains as relevant as ever, and how to take advantage.
Video:


