Table of Contents

Bare metal server provisioning (debian trixie)

Install of a Debian 13 (Trixie) on a dedicated server with:

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