Some checks failed
CI / skinny-install (aco) (push) Successful in 1m18s
CI / skinny-install (api) (push) Successful in 40s
CI / skinny-install (bcda) (push) Successful in 35s
CI / skinny-install (bib) (push) Successful in 38s
CI / skinny-install (cli) (push) Successful in 46s
CI / skinny-install (conf) (push) Successful in 36s
CI / skinny-install (opps) (push) Successful in 38s
CI / skinny-install (pfs) (push) Successful in 47s
CI / skinny-install (rex) (push) Successful in 35s
Infra CI / notebooks (push) Successful in 3m17s
CI / lint-test (push) Failing after 3m30s
CI / skinny-install (bls) (push) Successful in 34s
CI / skinny-install (ccw) (push) Successful in 45s
CI / skinny-install (cms) (push) Successful in 32s
CI / skinny-install (perf) (push) Successful in 43s
Deploy / build-scan-report (push) Has been cancelled
Infra CI / docs (push) Failing after 20s
Infra CI / api (push) Successful in 16s
Infra CI / mc (push) Successful in 12s
Package Supply Chain / pkg-supply-chain (push) Successful in 1m27s
Infra CI / zotero (push) Successful in 6m10s
7.3 KiB
7.3 KiB
Storage Node Build Runbook
Hardware: ASUS ROG Crosshair VIII Hero / AMD 3950X / 192GB RAM OS: Alpine Linux 3.21 (custom ISO) Security: LUKS2 encryption + YubiKey FIDO2 SSH + YubiKey challenge-response unlock
Drive Map
| Device | Hardware | Mount | Encryption | Purpose |
|---|---|---|---|---|
| Samsung Fit 128GB USB | Rear USB 3.1 port | / (read-only) |
None (no secrets) | OS root |
| 6x WD Blue SA510 2TB | M.2→SATA adapters on SATA6G_1-6 | /data/rustfs (LVM, 12TB) |
LUKS2 on LV | RustFS object storage |
| 1x Modern NVMe 2TB | M.2_1 onboard slot (PCIe 3.0 x4) | /var/lib/docker + /var/cache |
LUKS2 per partition | Docker, writes, cache |
Security Model
Boot → read-only root (no secrets, no data) → SSH only with YubiKey FIDO2
→ yubikey-unlock (challenge-response decrypts LUKS volumes)
→ Docker starts → RustFS serves data
- At rest: All data encrypted with LUKS2 (AES-XTS-512). Server can be physically stolen and data is safe.
- SSH access: Requires a hardware YubiKey with FIDO2 ed25519-sk key. No passwords accepted.
- Volume unlock: Requires physical YubiKey challenge-response (HMAC-SHA1 slot 2).
- Backup access: Emergency passphrase enrolled in LUKS slot 1 (store offline, safe deposit box).
- Two YubiKeys enrolled: Primary and backup, both work for SSH and LUKS.
YubiKey Preparation (before build)
On your workstation, for EACH YubiKey:
# 1. Program HMAC-SHA1 challenge-response on slot 2
ykman otp chalresp --touch --generate 2
# 2. Generate FIDO2 SSH key (resident on the key)
ssh-keygen -t ed25519-sk -O resident -C "yubikey-1-storagenode" -f ~/.ssh/id_yubikey1_storagenode
# Repeat with second key:
ssh-keygen -t ed25519-sk -O resident -C "yubikey-2-storagenode" -f ~/.ssh/id_yubikey2_storagenode
Save both .pub files — you'll paste them into /root/.ssh/authorized_keys during setup.
BIOS Settings
Enter BIOS: hold Delete during POST.
Required
-
Advanced → Onboard Devices Configuration
M.2_2 PCIe Bandwidth Configuration→Disabled(X8 mode)HD Audio Controller→DisabledRGB LED lighting (working state)→OffRGB LED lighting (sleep/off)→Off
-
Advanced → CPU Configuration
SVM Mode→Enabled(AMD-V, for Docker/future VMs)
-
Advanced → AMD fTPM configuration
Firmware TPM→Enabled
-
Advanced → SATA Configuration
SATA Mode→AHCI- Verify all 6 SATA ports show
Enabled
-
Advanced → APM Configuration
Restore On AC Power Loss→Power OnPower On By PCI-E/PCI→Enabled(Wake-on-LAN)
-
Extreme Tweaker
TPU→TPU II(water cooling overclock profile)
-
Boot
- Boot priority: USB drive first
CSM (Compatibility Support Module)→Disabled(pure UEFI)Fast Boot→Disabled(until stable)
Optional Performance
- Extreme Tweaker → PBO (if available in BIOS update)
- Precision Boost Overdrive →
Enabled
- Precision Boost Overdrive →
Build Steps
Phase 1: Build the ISO (on your current machine)
cd ~/stack/alpine-iso
chmod +x build-iso.sh
./build-iso.sh
If building from a non-Alpine system:
docker run --rm -v $(pwd):/work -w /work alpine:3.21 sh -c "
apk add alpine-sdk build-base alpine-conf syslinux xorriso \
mtools dosfstools grub grub-efi squashfs-tools git sudo && \
adduser -D build && \
addgroup build abuild && \
echo 'build ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers && \
su build -c 'abuild-keygen -an' && \
chmod +x build-iso.sh && \
./build-iso.sh
"
Phase 2: Write ISO to a temporary USB
dd if=out/alpine-storagenode-*.iso of=/dev/sdX bs=4M status=progress
sync
Phase 3: Boot and install to Samsung Fit
- Plug BOTH the temp ISO USB and the Samsung Fit into rear USB ports
- Boot from the ISO USB
- Log in as
root(no password) - Run:
setup-alpine(use defaults, skip disk install) - Run:
chmod +x install-to-usb.sh && ./install-to-usb.sh - Remove the ISO USB, reboot
Phase 4: Configure storage + encryption
- Boot from Samsung Fit
- Log in as root (still has password access for initial setup)
- Have YubiKey #1 inserted
- Run:
chmod +x /root/storage-setup.sh && /root/storage-setup.sh- This creates the LVM volume group, encrypts all data volumes, enrolls both YubiKeys
- You'll be prompted for a backup passphrase — store this OFFLINE
- Add your SSH public keys:
nano /root/.ssh/authorized_keys # Paste both yubikey .pub lines - Edit the RustFS password:
nano /opt/rustfs/docker-compose.yml - Lock it down:
ro
Phase 5: Test the full boot cycle
- Reboot the server
- From your workstation:
ssh -i ~/.ssh/id_yubikey1_storagenode root@<ip>- Touch YubiKey when it blinks (FIDO2 auth)
- On the server:
yubikey-unlock- Touch YubiKey again (challenge-response to decrypt LUKS)
- Start RustFS:
cd /opt/rustfs && docker compose up -d
Phase 6: Verify
# LUKS status
dmsetup ls
cryptsetup status docker-crypt
cryptsetup status rustfs-crypt
# LVM status
pvs
vgs
lvs
# Check mounts
mount | grep -E '(docker-crypt|cache-crypt|rustfs-crypt|tmpfs)'
# Docker
docker ps
docker logs rustfs
# Root is read-only
touch /testfile # should fail: "Read-only file system"
# RustFS health
curl http://localhost:9000/minio/health/live
# SMART status
for d in /dev/sd?; do smartctl -H "$d"; done
smartctl -H /dev/nvme0n1
Daily Operation
Power on → server boots to locked state (SSH only)
→ SSH in with YubiKey
→ yubikey-unlock (decrypts data, starts Docker)
→ RustFS serving
Power off / reboot → data re-encrypted automatically
Maintenance
# System updates
sys-update # handles rw/ro automatically
# LVM status
pvs
vgs
lvs
# Add a new SATA drive to expand storage
pvcreate /dev/sdX
vgextend rustfs-vg /dev/sdX
lvextend -l +100%FREE /dev/rustfs-vg/rustfs-lv
# Unlock rustfs-crypt first, then grow the filesystem live:
xfs_growfs /data/rustfs
# Replace a failed SATA drive
# 1. Move data off the dying drive:
pvmove /dev/sdX
# 2. Remove from VG:
vgreduce rustfs-vg /dev/sdX
pvremove /dev/sdX
# 3. Swap physical drive, then add the new one:
pvcreate /dev/sdY
vgextend rustfs-vg /dev/sdY
# Docker management (volumes must be unlocked)
cd /opt/rustfs
docker compose logs -f
docker compose restart
docker compose pull && docker compose up -d
# Manually lock volumes (before maintenance/travel)
yubikey-lock
Emergency Recovery
If both YubiKeys are lost/destroyed:
# Boot server, SSH will fail (no valid keys)
# Connect keyboard + monitor directly
# Log in as root (if password still set) or boot from ISO
# Use backup passphrase to unlock:
vgchange -ay rustfs-vg # activate LVM first
cryptsetup open /dev/rustfs-vg/rustfs-lv rustfs-crypt # enter backup passphrase
cryptsetup open /dev/nvme0n1p1 docker-crypt # enter backup passphrase
cryptsetup open /dev/nvme0n1p2 cache-crypt # enter backup passphrase
mount /data/rustfs
mount /var/lib/docker
mount /var/cache
Network (post-install)
rw
cat > /etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.1.X/24
gateway 192.168.1.1
EOF
echo "nameserver 1.1.1.1" > /etc/resolv.conf
ro
reboot