Based on https://wiki.archlinux.org/title/Installation_guide, retrieved 2024-01-27.
This file will document any changes or details that I make for my own purpose(s).
Important note: this same root password will be used for the system.
passwd
ip addr
Note the IP address, then remote in from the client:
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@THE_IP
This ignores my zpool, since that's handled already.
Also, specifics are initially being written for the VM that I'm testing this on.
As usual, the compress=zstd
option on the first mounted subvolume will apply in practice to all other mounted subvolumes. I'm discriminating based on how I would set it if this weren't the case.
fdisk -l
device=/dev/vda
vared -p 'Root device: ' -r "[$device]" device
echo 'g\nn\n\n\n+1G\nt\n1\nn\n\n\n\nw' | fdisk $device
sync
mkfs.fat -F 32 -n ESP ${device}1
mkfs.btrfs -L MAIN ${device}2
mount ${device}2 /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@snapshots
btrfs subvolume create /mnt/@var_log
btrfs subvolume create /mnt/@var_pacman_pkg
umount /mnt
mount -o subvol=@,compress=zstd ${device}2 /mnt
mount --mkdir ${device}1 /mnt/boot
mount --mkdir -o subvol=@home,compress=zstd ${device}2 /mnt/home
mount --mkdir -o subvol=@snapshots ${device}2 /mnt/.snapshots
mount --mkdir -o subvol=@var_log,compress=zstd ${device}2 /mnt/var/log
mount --mkdir -o subvol=@var_pacman_pkg ${device}2 /mnt/var/pacman/pkg
while systemctl show reflector | grep -q ActiveState=activating; do echo Waiting for Reflector to finish...; sleep 1s; done
echo Reflector finished
perl -pi -e 's/^#(?=(?:Color)|(?:ParallelDownloads = \d+)$)//' /etc/pacman.conf
pacstrap -PK /mnt base linux-lts dracut base-devel linux-lts-headers linux-firmware amd-ucode btrfs-progs emacs-nox git man-db man-pages texinfo openssh pacman-contrib dkms zsh devtools
genfstab -L /mnt >> /mnt/etc/fstab
ln -sf ../run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf
cut -f 2 -d: /etc/shadow | head -n1 > /mnt/etc/.root-password
arch-chroot /mnt
cat >/root/.emacs <<END
(setq make-backup-files nil)
END
ln -sf /usr/share/zoneinfo/America/Detroit /etc/localtime
systemctl enable systemd-timesyncd.service
hwclock --systohc
perl -pi -e 's/#(?=en_US\.UTF-8 UTF-8)//' /etc/locale.gen
locale-gen
cat >/etc/locale.conf <<END
LANG=en_US.UTF-8
END
cat >/etc/hostname <<END
juan
END
cat >/etc/systemd/network/20-wired.network <<END
[Match]
Name=en*
[Network]
DHCP=yes
END
systemctl enable systemd-networkd.service systemd-resolved.service
cat >/etc/dracut.conf.d/myflags.conf << END
uefi="yes"
compress="zstd"
kernel_cmdline="root=LABEL=MAIN rootflags=subvol=@,compress=zstd"
END
for k in /usr/lib/modules/*; do dracut --kver $(basename "$k"); done
bootctl install
cat >/boot/loader/loader.conf <<END
timeout 0
console-mode keep
editor no
END
usermod -p `cat /etc/.root-password` root
useradd -m -G wheel,users -U -s /usr/bin/zsh -p `cat /etc/.root-password` joe
rm /etc/.root-password
cat >/etc/sudoers.d/00_wheel <<END
%wheel ALL=(ALL:ALL) ALL
END
perl -pi -e 's/(?<=-march=x86-64) /-v4 / ; s/(?<=-mtune=)generic/native/ ; s/^#(RUSTFLAGS="[^"]*)"/$1 -C target-cpu=x86-64-v4"/ ; s/^#(?<prefix>MAKEFLAGS="-j)(\d+)(?<postfix>.*)$/$+{prefix}10$+{postfix}/' /etc/makepkg.conf
systemctl enable sshd.service paccache.timer
mkdir -m 0700 /home/joe/.ssh
cat >/home/joe/.ssh/authorized_keys <<END
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICCWGHWSbfrMaedEPJUZKoHKHKcowy2oKW2PIK8MUJ7P
END
cat >/home/joe/.emacs <<END
(setq make-backup-files nil)
END
touch /home/joe/.zshrc
chown -R joe:joe /home/joe/.ssh /home/joe/.emacs /home/joe/.zshrc
# https://gitlab.archlinux.org/archlinux/arch-install-scripts/-/issues/70
chmod 0644 /etc/pacman.conf
exit
umount -R /mnt
reboot
Ideally this should be converted to a script, but I am still building it out, so it'll be broken out into steps for now based on https://wiki.archlinux.org/title/General_recommendations
Connect to the server as joe
(there should be no password prompt), then:
curl -o install-omz.sh -L https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
cat >install-omz.sh.sha256 <<END
96d90bb5cfd50793f5666db815c5a2b0f209d7e509049d3b22833042640f2676 install-omz.sh
END
sha256sum -c install-omz.sh.sha256 || exit 1
sh install-omz.sh
rm install-omz.sh{,.sha256}
perl -pi -e 's/(?<=ZSH_THEME=")robbyrussell(?=")/strug/ ; s/# (?=(?:HYPHEN_INSENSITIVE="true")|(?:COMPLETION_WAITING_DOTS="true")|(?:DISABLE_MAGIC_FUNCTIONS="true"))//' ~/.zshrc
cat >/home/joe/.oh-my-zsh/custom/prefs.zsh <<END
export EDITOR=/usr/bin/emacs
export DIFFPROG=/usr/bin/meld
setopt appendhistory
setopt INC_APPEND_HISTORY
END
Reconnect as joe
one last time.
ZFS comes from the AUR. I need to be updating ZFS regularly, so I need to hold my nose here.
mkdir -p ~/src/paru-bin
pushd ~/src/paru-bin
git clone https://aur.archlinux.org/paru-bin .
makepkg -si
popd
sudo perl -pi -e 's/#(?=SudoLoop)//' /etc/paru.conf
ALWAYS:
paru -S zfs-dkms
sudo mkdir -p /etc/zfs/zfs-list.cache
ONLY DURING TESTING:
for i in {1..5}; do fallocate -l 1G ~/$i.img; done
sudo zpool create nas raidz1 ~/{1,2,3,4,5}.img
for f in joe kristina media shared archipelago forgejo foundryvtt foundryvtt2 archipelago2 factorio20241027
do
sudo zfs create -o compression=zstd nas/$f
sudo touch /nas/$f/some-file-owned-by-$f
done
sudo chown -R 1000:1000 /nas/joe
sudo chown -R 1002:1002 /nas/kristina
sudo chown -R 1004:1004 /nas/factorio20241027
sudo chown -R 0:984 /nas/media
sudo chown -R 969:969 /nas/archipelago
sudo chown -R 963:963 /nas/forgejo
sudo chown -R 968:984 /nas/foundryvtt
sudo chown -R 966:966 /nas/foundryvtt2
sudo chown -R 967:967 /nas/archipelago2
sudo mkdir -p /nas/media/jellyfin
sudo chown 971:971 /nas/media/jellyfin
ALWAYS:
sudo systemctl enable --now zfs.target zfs-import.target zfs-import-cache.service zfs-mount.service zfs-zed.service
sudo touch /etc/zfs/zfs-list.cache/nas
Not sure what weird thing dracut-ukify
is doing. This is all I need for this setup... which is a lot more code to write here, but it's the simplest I've seen so far. Which probably means that I'm doing it wrong, but *shrugs*.
sudo mkdir /.snapshots/backup-efi
sudo mkdir /.snapshots/root-auto
cat >/tmp/airbreather-runs-dracut-like-this.sh <<'END'
#!/usr/bin/sh
kvers=($(basename -a /usr/lib/modules/*))
for img in /boot/EFI/Linux/linux-*.efi
do
kver_img=$(basename $img)
found=0
for kver in $kvers
do
if [[ $kver_img = "linux-$kver-"* ]]
then
found=1
break
fi
if [[ $found = 0 ]]
then
mv $img /.snapshots/backup-efi/
fi
done
done
for kver in $kvers
do
dracut --force --kver $kver
done
END
chmod 0755 /tmp/airbreather-runs-dracut-like-this.sh
cat >/tmp/snapshot-root.sh <<'END'
#!/usr/bin/sh
# based on https://github.com/vaminakov/btrfs-autosnap
btrfs subvolume snapshot -r / "/.snapshots/root-auto/$(date -u --rfc-3339=ns)"
END
chmod 0755 /tmp/snapshot-root.sh
sudo mv /tmp/airbreather-runs-dracut-like-this.sh /tmp/snapshot-root.sh /usr/local/bin/
cat >/tmp/90-airbreather-installs-dracut-like-this.hook <<END
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Target = usr/lib/modules/*/pkgbase
Target = usr/lib/dracut/*
Target = usr/lib/systemd/systemd
Target = usr/lib/systemd/boot/efi/*.efi.stub
Target = usr/src/*/dkms.conf
[Action]
Description = Updating linux images, the airbreather way...
When = PostTransaction
Exec = /usr/local/bin/airbreather-runs-dracut-like-this.sh
NeedsTargets
END
chmod 0644 /tmp/90-airbreather-installs-dracut-like-this.hook
cat >/tmp/01-snapshot-root.hook <<END
[Trigger]
Type = Package
Operation = Install
Operation = Upgrade
Operation = Remove
Target = *
[Action]
Description = Making BTRFS snapshot of the root...
Depends = btrfs-progs
When = PreTransaction
Exec = /usr/local/bin/snapshot-root.sh
AbortOnFail
NeedsTargets
END
chmod 0644 /tmp/01-snapshot-root.hook
sudo mkdir -p /etc/pacman.d/hooks
sudo mv /tmp/90-airbreather-installs-dracut-like-this.hook /tmp/01-snapshot-root.hook /etc/pacman.d/hooks/
# Three ways I could have done this:
# 1. renumber the clashes that Arch brings to us out-of-the-box, then chown everything in the base
# system accordingly
# 2. accept different UID / GID values, then chown everything in the zpool accordingly
# 3. before the migration, on the source side: assign different non-clashing values, then chown
# everything in the zpool accordingly
#
# I feel like #2 is the safest, assuming that I don't make any stupid mistakes outside the scope of
# this Gist. I don't expect any important parts of the Arch ecosystem to do anything as insane as to
# assume that specific groups have specific GID values, but the instant that I pulled an AUR helper
# into all this, I also opted-in to assuming that any given package might be doing some chaotic
# neutral things to make the underlying software work. It also should result in IDs that fall within
# the appropriate ranges as defined by the **very different** /etc/login.defs files, which *can* be
# treated as in-scope for the Arch ecosystem to make assumptions for routines that have no better
# option. So I add a step that will be obnoxious to reverse — but not impossible by any means — if I
# need to abort partway through.
sudo useradd -G users -U -m kristina
echo '#!/usr/bin/sh' >/home/joe/remap_ids.sh
joe_uid=$(id -u joe)
kristina_uid=$(id -u kristina)
echo find /nas -uid 1002 -exec chown --no-dereference $kristina_uid "'{}'" "';'" >> /home/joe/remap_ids.sh
for old_uid in 198 962 963 965 966 967 968 969 971 994 1000 1003 1004
do
echo find /nas -uid $old_uid -exec chown --no-dereference $joe_uid "'{}'" "';'" >> /home/joe/remap_ids.sh
done
joe_gid=$(getent group joe | cut -d: -f3)
users_gid=$(getent group users | cut -d: -f3)
kristina_gid=$(getent group kristina | cut -d: -f3)
echo find /nas -gid 984 -exec chgrp --no-dereference $users_gid "'{}'" "';'" >> /home/joe/remap_ids.sh
echo find /nas -gid 1002 -exec chgrp --no-dereference $kristina_gid "'{}'" "';'" >> /home/joe/remap_ids.sh
for old_gid in 198 961 962 963 965 966 967 968 969 971 994 1000 1004
do
echo find /nas -gid $old_gid -exec chgrp --no-dereference $joe_gid "'{}'" "';'" >> /home/joe/remap_ids.sh
done
echo
echo
echo
echo 'A script has been created at /home/joe/remap_ids.sh that will remap the IDs in the zpool.'
echo 'This is a DESTRUCTIVE operation, so I am taking full precautions not to run it automatically.'
echo 'Examine it before running it (which must be done as root). Good luck.'