proc:bare_metal_server_provisioning
Table of Contents
Bare metal server provisioning (debian trixie)
Install of a Debian 13 (Trixie) on a dedicated server with:
- Full LUKS encryption (except
/bootand ESP) - RAID1 on two NVMe disks
- LVM on LUKS
- Boot UEFI or legacy/BIOS (auto-detected)
- Remote cryptroot unlock with
dropbear-initramfs
Target disk layout
nvme0n1 (419 Go) nvme1n1 (419 Go)
├── p1 : see note below ├── p1 : see note below
├── p2 : 512 Mo ────┐ ├── p2 : 512 Mo ────┐
│ ├── md0 (RAID1) → /boot (ext4)
└── p3 : ~418 Go ───┤ └── p3 : ~418 Go ───┤
└── md1 (RAID1) → LUKS → LVM (vg0)
├── root (10 Go, ext4)
└── thin pool
p1 depends on boot mode:
* UEFI : 512 Mo ESP (ef00, vfat, mounted /boot/efi), one per disk
* legacy : 1 Mo BIOS boot partition (ef02, raw), one per disk
Procedure
1. Boot in rescue mode
Detect the rescue boot mode once, everything downstream branches on $MODE. The mode is imposed by the rescue boot (Hetzner PXE), not locally adjustable.
$ if [ -d /sys/firmware/efi ]; then MODE=uefi; else MODE=legacy; fi $ echo "Boot mode: $MODE"
2. Disk setup
$ apt update
$ apt install -y mdadm cryptsetup lvm2 parted debootstrap gdisk rsync
# Cleanup (/!\ this delete all data on disks /!\)
$ mdadm --stop --scan
$ mdadm --zero-superblock /dev/nvme0n1 2>/dev/null
$ mdadm --zero-superblock /dev/nvme1n1 2>/dev/null
$ wipefs -a /dev/nvme0n1 /dev/nvme1n1
$ sgdisk --zap-all /dev/nvme0n1
$ sgdisk --zap-all /dev/nvme1n1
# GPT partitioning on both disks
# p1 differs by mode:
# uefi -> ESP (ef00, 512M, vfat, mounted /boot/efi)
# legacy -> BIOS boot (ef02, 1M, raw, used by grub-install)
$ for D in /dev/nvme0n1 /dev/nvme1n1; do
if [ "$MODE" = uefi ]; then
sgdisk -n 1:0:+512M -t 1:ef00 -c 1:"EFI" $D
else
sgdisk -n 1:0:+1M -t 1:ef02 -c 1:"BIOS boot" $D
fi
sgdisk -n 2:0:+512M -t 2:fd00 -c 2:"boot RAID" $D
sgdisk -n 3:0:0 -t 3:fd00 -c 3:"luks RAID" $D
done
$ partprobe
3. RAID1 mdadm
$ mdadm --create /dev/md0 \ --level=1 --raid-devices=2 --metadata=1.2 \ --homehost=schwartz --name=boot \ /dev/nvme0n1p2 /dev/nvme1n1p2 $ mdadm --create /dev/md1 \ --level=1 --raid-devices=2 --metadata=1.2 \ --homehost=schwartz --name=cryptroot \ /dev/nvme0n1p3 /dev/nvme1n1p3
4. LUKS on md1
$ cryptsetup luksFormat --type luks2 --iter-time 5000 /dev/md1 $ cryptsetup open /dev/md1 cryptroot
Passphrase generated with pass generate luks/schwartz 30 –no-symbols and stored in password-store.
5. LVM
$ pvcreate /dev/mapper/cryptroot $ vgcreate vg0 /dev/mapper/cryptroot $ lvcreate -L 10G -n root vg0
6. Formatting & partitions setup
# ESP only in UEFI mode; in legacy p1 is a raw BIOS boot partition (no fs) $ if [ "$MODE" = uefi ]; then mkfs.vfat -F32 -n EFI0 /dev/nvme0n1p1 mkfs.vfat -F32 -n EFI1 /dev/nvme1n1p1 fi $ mkfs.ext4 -L boot /dev/md0 $ mkfs.ext4 -L root /dev/vg0/root $ mount /dev/vg0/root /mnt $ mkdir -p /mnt/boot $ mount /dev/md0 /mnt/boot $ if [ "$MODE" = uefi ]; then mkdir -p /mnt/boot/efi mount /dev/nvme0n1p1 /mnt/boot/efi fi
7. Debootstrap
$ debootstrap --arch amd64 trixie /mnt http://deb.debian.org/debian/
8. Chroot setup
$ for d in dev dev/pts proc sys run; do mount --bind /$d /mnt/$d done # efivars only exists (and is only needed) in UEFI mode $ if [ "$MODE" = uefi ]; then mount --bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars fi $ cp /etc/resolv.conf /mnt/etc/resolv.conf # Get UUIDs $ blkid -s UUID -o value /dev/md0 # BOOT_UUID $ blkid -s UUID -o value /dev/vg0/root # ROOT_UUID $ blkid -s UUID -o value /dev/md1 # LUKS_UUID # EFI UUIDs only relevant in UEFI mode: $ if [ "$MODE" = uefi ]; then blkid -s UUID -o value /dev/nvme0n1p1 # EFI0_UUID blkid -s UUID -o value /dev/nvme1n1p1 # EFI1_UUID fi $ chroot /mnt /bin/bash
9. Base configuration (in chroot)
Re-evaluate the mode inside the chroot (the rescue variable is not inherited). /sys/firmware/efi is still visible here because /sys is bind-mounted from the rescue, so the detection stays consistent with section 1.
$ if [ -d /sys/firmware/efi ]; then MODE=uefi; else MODE=legacy; fi
# Hostname $ echo "schwartz" > /etc/hostname $ sed -i "2i 127.0.1.1\tschwartz" /etc/hosts # Locales / timezone $ apt update && apt install -y locales $ echo "en_US.UTF-8 UTF-8" > /etc/locale.gen $ echo "fr_FR.UTF-8 UTF-8" >> /etc/locale.gen $ locale-gen $ echo "LANG=en_US.UTF-8" > /etc/default/locale $ ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime $ dpkg-reconfigure -f noninteractive tzdata # APT setup $ cat > /etc/apt/sources.list <<EOF deb http://deb.debian.org/debian trixie main contrib non-free-firmware deb http://security.debian.org/debian-security trixie-security main contrib non-free-firmware deb http://deb.debian.org/debian trixie-updates main contrib non-free-firmware EOF $ apt update
10. Network configuration
$ apt install -y ifupdown $ cat > /etc/network/interfaces <<EOF auto lo iface lo inet loopback auto eno1 iface eno1 inet dhcp EOF
11. crypttab, fstab, mdadm.conf
# crypttab $ cat > /etc/crypttab <<EOF cryptroot UUID=$LUKS_UUID none luks,initramfs,discard EOF # fstab $ cat > /etc/fstab <<EOF UUID=$ROOT_UUID / ext4 defaults,noatime,errors=remount-ro 0 1 UUID=$BOOT_UUID /boot ext4 defaults 0 2 EOF # ESP entry only in UEFI mode $ if [ "$MODE" = uefi ]; then echo "UUID=$EFI0_UUID /boot/efi vfat umask=0077,defaults 0 2" >> /etc/fstab fi # mdadm.conf $ apt install -y mdadm $ cat > /etc/mdadm/mdadm.conf <<EOF HOMEHOST <system> MAILADDR root EOF $ mdadm --detail --scan >> /etc/mdadm/mdadm.conf
12. Kernel and boot tools
$ apt install -y \ linux-image-amd64 \ cryptsetup cryptsetup-initramfs \ lvm2 \ thin-provisioning-tools \ intel-microcode \ firmware-linux \ initramfs-tools \ openssh-server
13. GRUB (UEFI or legacy)
$ echo 'GRUB_ENABLE_CRYPTODISK=y' >> /etc/default/grub $ if [ "$MODE" = uefi ]; then ## --- UEFI --- apt install -y grub-efi-amd64 efibootmgr grub-install --target=x86_64-efi \ --efi-directory=/boot/efi \ --bootloader-id=debian \ --recheck update-grub # Sync ESP2 mkdir -p /tmp/efi2 mount /dev/nvme1n1p1 /tmp/efi2 cp -av /boot/efi/* /tmp/efi2/ umount /tmp/efi2 # UEFI fallback for ESP2 efibootmgr --create \ --disk /dev/nvme1n1 --part 1 \ --label "debian-backup" \ --loader '\EFI\debian\shimx64.efi' # Reorder to make debian (ESP1) as priority # efibootmgr -o 000A,000B,... (adapt IDs from `efibootmgr`) else ## --- legacy/BIOS --- apt install -y grub-pc # Install GRUB core into the BIOS boot partition (ef02) of BOTH disks # for redundancy: if nvme0 fails, the machine still boots on nvme1. grub-install --target=i386-pc --recheck /dev/nvme0n1 grub-install --target=i386-pc --recheck /dev/nvme1n1 update-grub fi
14. User et SSH
$ passwd # root password setup $ adduser phil $ usermod -aG sudo phil # SSH key $ mkdir -p /home/phil/.ssh $ cat > /home/phil/.ssh/authorized_keys <<'EOF' ssh-ed25519 AAAA... phil@host EOF $ chmod 700 /home/phil/.ssh $ chmod 600 /home/phil/.ssh/authorized_keys $ chown -R phil:phil /home/phil/.ssh # sshd hardening $ cat > /etc/ssh/sshd_config.d/10-hardening.conf <<EOF PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes KbdInteractiveAuthentication no EOF $ systemctl enable ssh
15. Dropbear-initramfs
$ apt install -y dropbear-initramfs busybox # Config dropbear : port 4222 and force cryptroot-unlock command $ cat > /etc/dropbear/initramfs/dropbear.conf <<'EOF' DROPBEAR_OPTIONS="-p 4222 -s -j -k -I 60 -c cryptroot-unlock" EOF # ssh key allowed to connect $ cat > /etc/dropbear/initramfs/authorized_keys <<'EOF' ssh-ed25519 AAAA... phil@host EOF $ chmod 600 /etc/dropbear/initramfs/authorized_keys # network config for initramfs cat >> /etc/initramfs-tools/initramfs.conf <<EOF IP=xx.xx.xx.xx::gw.ad.dr.ess:255.255.255.0:schwartz:eno1:off EOF $ update-initramfs -u
16. reboot
$ exit # exit chroot # $MODE from section 1 is still set in the rescue shell here $ if [ "$MODE" = uefi ]; then umount /mnt/sys/firmware/efi/efivars umount /mnt/boot/efi fi $ umount /mnt/boot $ umount /mnt/run $ umount /mnt/sys $ umount /mnt/proc $ umount /mnt/dev/pts $ umount /mnt/dev $ umount /mnt $ vgchange -an vg0 $ cryptsetup close cryptroot $ mdadm --stop /dev/md0 /dev/md1 $ reboot
17. First boot and unlock
# Wait dropbear to be up nc -zv xx.xx.xx.xx 4222 # remote unlocking oneliner: pass show luks/schwartz | tr -d '\n' | ssh -p 4222 root@xx.xx.xx.xx cryptroot-unlock
After few seconds, normal SSD become available.
18. (EXTRA) hugepage & swap & LVM thin configuration
sudo lvcreate -L 4G -n swap vg0 sudo mkswap /dev/vg0/swap sudo swapon /dev/vg0/swap echo "/dev/vg0/swap none swap sw 0 0" | sudo tee -a /etc/fstab
If you want to configure hugepage for a qemu hypervisor:
sudo tee /etc/sysctl.d/10-hugepages.conf > /dev/null <<'EOF' vm.nr_hugepages = 13312 # EXAMPLE FOR 26GB/32GB EOF
LVM Thin initialization to store VM storage
sudo lvcreate --type thin-pool -L 250G -n thin vg0
proc/bare_metal_server_provisioning.txt · Last modified: by phil
