You are not logged in.
Hello
I recently acquired a Framework laptop and decided to mess around with Arch Linux to build a lightweight yet modern system.
I've been working on an automated install script to set everything up from scratch while learning how it all works.
Here are the laptop's specs
CPU Intel® Core™ i7-1360P
SSD WD_BLACK™ SN850X NVMe™- M.2 2280 - 1TB
RAM DDR4-3200 - 16GB (2 x 8GB)
Goals
Minimal
Modern
Keyboard-focused
Ruby/PHP dev environment
Here's what I've settled for
Display server Wayland
Compositor Sway
Terminal foot
Shell bash
Text editor vim
In order to install Arch, I first connect to my Wi-Fi and then `curl | sh` the install script using the following commands
iwctl station wlan0 connect <SSID>
curl -fsSL https://.../arch.sh | sh
Here's the latest version of the install script.
I'm still far away from having a complete system, but if I'm making any stupid mistakes, or forgetting something, I'd rather know now than when it's too late.
#!/bin/bash
set -euo pipefail
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
step() {
echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
SSD="nvme0n1"
PARTITION_EFI="/dev/${SSD}p1"
PARTITION_ROOT="/dev/${SSD}p2"
LUKS="luks"
TIMEZONE="Europe/Paris"
LOCALE="en_US.UTF-8"
HOSTNAME="framezork"
USER="zogstrip"
PACKAGES=(
base
base-devel # dev tools
linux
linux-firmware
intel-ucode
tlp # power management
ethtool # disable Wake-on-LAN
smartmontools # S.M.A.R.T
util-linux # fstrim
reflector # pacman's mirrorlist
fprintd # fingerprint reader
pipewire # audio
pipewire-pulse # bluetooth audio
mesa # video
iwd # wifi
bluez # bluetooth
bluez-utils # bluetoothctl
tailscale # vpn
curl
wget
httpie
man-db
man-pages
tldr # better man
fastfetch # better neofetch
git
pass # password manager
btop # better top
vim # text editor
fzf # fuzzy finder
eza # better ls
zoxide # better cd
bat # better cat
ripgrep # better grep
jq # json
sway # window manager
foot # terminal emulator
imagemagick # image manipulation
ffmpeg # video manipulation
mpv # video watching
grim # wayland screen grabber
firefox
zeal # offline documentation
)
###
### 0 - checks
###
VENDOR=$(cat /sys/class/dmi/id/sys_vendor)
CPU=$(lscpu | awk -F: '/^Vendor ID:/ {print $2}' | xargs)
BATTERY_STATUS=$(cat /sys/class/power_supply/BAT*/status | head -n 1)
BATTERY_CAPACITY=$(cat /sys/class/power_supply/BAT*/capacity | head -n 1)
[[ -d "/sys/firmware/efi" ]] || { echo "System not booted in UEFI mode."; exit 1; }
[[ -d "/sys/block/${SSD}" ]] || { echo "NVMe drive not found."; exit 1; }
[[ "$VENDOR" == "Framework" ]] || { echo "Not running on a Framework laptop. Found $VENDOR."; exit 1; }
[[ "$CPU" == *"Intel"* ]] || { echo "Not running on Intel CPU. Found $CPU."; exit 1; }
[[ "$BATTERY_STATUS" != "Discharging" || "$BATTERY_CAPACITY" -ge 20 ]] || { echo "Battery too low."; exit 1; }
step "Installing dialog..."
pacman -Sy --noconfirm dialog
PASSWORD=$(dialog --clear --stdout --passwordbox "Enter password" 0 0)
clear
[[ -z "$PASSWORD" ]] && { echo "Empty password. Aborting."; exit 1; }
###
### 1 - pre-installation
###
step "Unmounting..."
umount -R /mnt 2> /dev/null || true
cryptsetup luksClose $LUKS 2> /dev/null || true
step "Setting up disks & partitions..."
sgdisk -Z /dev/$SSD
sgdisk -n 1::+512M -t 1:ef00 /dev/$SSD # EFI System Partition
sgdisk -n 2::-0 -t 2:8300 /dev/$SSD # Linux filesystem
step "Formatting EFI partition..."
mkfs.vfat -F32 $PARTITION_EFI
step "Formatting ROOT partition..."
echo -n "${PASSWORD}" | cryptsetup luksFormat $PARTITION_ROOT -
echo -n "${PASSWORD}" | cryptsetup luksOpen --allow-discards --persistent $PARTITION_ROOT $LUKS -
mkfs.ext4 /dev/mapper/$LUKS
step "Mounting partitions..."
mount /dev/mapper/$LUKS /mnt
mkdir /mnt/boot
mount $PARTITION_EFI /mnt/boot
###
### 2 - installation
###
step "Configuring initrd..."
mkdir /mnt/etc
cat <<EOF > /mnt/etc/mkinitcpio.conf
HOOKS=(systemd autodetect modconf kms keyboard block sd-encrypt filesystems fsck)
EOF
step "Setting up mirrors (~ 1 min)..."
systemctl start reflector
step "Installing packages..."
pacstrap /mnt "${PACKAGES[@]}"
###
### 3 - configuration
###
step "Generating fstab..."
genfstab -U /mnt >> /mnt/etc/fstab
step "Setting timezone..."
ln -sf /usr/share/zoneinfo/$TIMEZONE /mnt/etc/localtime
arch-chroot /mnt hwclock --systohc
step "Setting locale..."
echo "LANG=$LOCALE" > /mnt/etc/locale.conf
echo "$LOCALE UTF-8" > /mnt/etc/locale.gen
arch-chroot /mnt locale-gen
step "Setting hostname..."
echo "$HOSTNAME" > /mnt/etc/hostname
cat <<EOF >> /mnt/etc/hosts
127.0.0.1 localhost
::1 localhost
EOF
step "Copying WiFi credentials..."
mkdir -p /mnt/var/lib/iwd
cp /var/lib/iwd/*.psk /mnt/var/lib/iwd
step "Creating user $USER..."
arch-chroot /mnt useradd -m -G wheel $USER
echo "$USER:$PASSWORD" | arch-chroot /mnt chpasswd
echo "%wheel ALL=(ALL) ALL" > /mnt/etc/sudoers.d/wheel
chmod 440 /mnt/etc/sudoers.d/wheel
step "Disabling root..."
arch-chroot /mnt passwd -dl root
step "Installing bootloader..."
arch-chroot /mnt bootctl install
step "Configuring bootloader..."
UUID=$(blkid -s UUID -o value $PARTITION_ROOT)
cat <<EOF > /mnt/boot/loader/entries/arch.conf
title I use Arch btw
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options rd.luks.name=${UUID}=${LUKS} rd.luks.options=password-echo=no root=/dev/mapper/${LUKS} rw
EOF
cat <<EOF > /mnt/boot/loader/loader.conf
default arch
timeout 0
editor no
EOF
###
### 5 - setup
###
step "Configuring systemd..."
mkdir -p /mnt/etc/systemd/system.conf.d
cat <<EOF > /mnt/etc/systemd/system.conf.d/kill-fast.conf
[Manager]
DefaultTimeoutStopSec=3s
EOF
step "Setting up autologin on tty1..."
mkdir -p /mnt/etc/systemd/system/getty@tty1.service.d
cat <<EOF > /mnt/etc/systemd/system/getty@tty1.service.d/autologin.conf
[Service]
ExecStart=
ExecStart=-/usr/bin/agetty --autologin $USER --noclear %I \$TERM
EOF
step "Configuring networkd..."
cat <<EOF > /mnt/etc/systemd/network/20-wired.network
[Match]
Name=en*
[Network]
DHCP=yes
[DHCP]
RouteMetric=100
EOF
cat <<EOF > /mnt/etc/systemd/network/25-wireless.network
[Match]
Name=wl*
[Network]
DHCP=yes
[DHCP]
RouteMetric=600
IgnoreCarrierLoss=3s
EOF
step "Allowing wheel members to enroll fingerprints..."
cat <<EOF > /mnt/etc/polkit-1/rules.d/49-fprintd.rules
polkit.addRule(function(action, subject) {
if (action.id == "net.reactivated.fprint.device.enroll" && subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
});
EOF
step "Disabling power button..."
mkdir -p /mnt/etc/systemd/logind.conf.d
cat <<EOF > /mnt/etc/systemd/logind.conf.d/powerkey.conf
[Login]
HandlePowerKey=ignore
EOF
step "Remapping CAPS to ESC..."
mkdir -p /mnt/etc/udev/hwdb.d
cat <<EOF > /mnt/etc/udev/hwdb.d/90-caps-to-esc.hwdb
evdev:atkbd:*
KEYBOARD_KEY_3a=esc
EOF
arch-chroot /mnt systemd-hwdb update
step "Configuring 30Hz key repeats..."
cat <<EOF > /mnt/etc/systemd/system/kbdrate.service
[Unit]
Description=Faster kbdrate (30Hz key repeat)
[Service]
ExecStart=/usr/bin/kbdrate -r 30
Type=oneshot
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
step "Forcing bluetooth's BR/EDR mode..."
sed -i '/^.*ControllerMode.*=.*/c\ControllerMode = bredr' /mnt/etc/bluetooth/main.conf
step "Enabling systemd timers..."
arch-chroot /mnt systemctl enable \
fstrim.timer \
reflector.timer
step "Enabling systemd services..."
arch-chroot /mnt systemctl enable \
bluetooth.service \
fprintd.service \
iwd.service \
kbdrate.service \
reflector.service \
systemd-networkd.service \
systemd-resolved.service \
systemd-timesyncd.service \
tlp.service
###
### 6 - dotfiles
###
step "Setting up .bash_profile..."
cat <<EOF > /mnt/home/$USER/.bash_profile
# source .bashrc if it exists
[[ -f ~/.bashrc ]] && . ~/.bashrc
# launch sway on tty1
[[ -z \$DISPLAY && \$(tty) = /dev/tty1 ]] && exec sway
EOF
arch-chroot /mnt chown $USER:$USER /home/$USER/.bash_profile
###
### done
###
step "Unmounting..."
umount -R /mnt
cryptsetup luksClose $LUKS
step "Installation complete!"
step "You may 'reboot'."
Note: this was heavily inspired by maximbaz's install.sh.
Last edited by zogstrip (2024-07-24 11:32:42)
Offline
according to the guide en_US.UTF-8 is always required - your script will break when changing the master locale
also: I noticed that pacstrap base-devel xan lead to issues because locale.gen isn't configured yet - hence I would do a two pass: first install base and set up locale and second pass install devel
Offline
according to the guide en_US.UTF-8 is always required - your script will break when changing the master locale
That's fine as I don't plan on using any other locale any time soon. Even though it's a variable, it's mostly to keep repeating myself and centralizing configuration.
also: I noticed that pacstrap base-devel can lead to issues because locale.gen isn't configured yet - hence I would do a two pass: first install base and set up locale and second pass install devel
That's interesting. What kind of issues? During the install or when using any of the programs/libs it contains?
Last edited by zogstrip (2024-07-24 12:12:55)
Offline
both:
during initial pacstrap you get several warnings about not set locale
and even after fixing the locale sometimes I got weird issues related to it which could be fixed by reinstalling base-devel after locale was set up
Offline