Compare commits

...

16 commits

Author SHA1 Message Date
Yt 5dc786009c
Merge pull request #20 from nix-community/updates
hetzner-dedicated updates
2023-07-18 17:06:47 +09:00
happysalada 09fbbe5440 hetzner-dedicated updates 2023-07-17 12:24:51 +09:00
Yt 3d57fb8d03
Merge pull request #16 from happysalada/update_hetzner_dedicated
hetzner-dedicated: update to 21.11 & improvements
2023-07-17 11:35:25 +09:00
Fabian Affolter 552eb22477 Fix typo 2022-05-12 12:00:58 +02:00
happysalada 34c76dd039 hetzner: dedicated update zfs script 2022-04-27 22:33:43 -04:00
Yt 266f688176
Merge pull request #10 from nix-community/issue-9-support-parted-3.3
Support parted 3.3 exiting 1 on kernel inform failure
2022-04-27 07:23:39 -04:00
Raphael Megzari e815d85edf
Merge pull request #15 from bitonic/patch-1
Add FIXME regarding address prefixLength for Hetzner
2021-10-06 20:45:05 +09:00
Francesco Mazzoli 604327e7ab
Add FIXME regarding address prefixLength for Hetzner
Fixes #13
2021-10-06 13:41:32 +02:00
Niklas Hambüchen 35adc676e1 Support parted 3.3 exiting 1 on kernel inform failure. Fixes #9 2021-07-19 03:47:30 +02:00
Raphael Megzari cb09b52de8
Merge pull request #8 from asymmetric/mdadm-hostname
hosters/hetzner-dedicated: mdadm: ignore homehost
2021-04-27 09:54:17 +09:00
Lorenzo Manacorda 76a4a6ff36 hosters/hetzner-dedicated: mdadm: ignore homehost
This is a safer way of ensuring there are no issues when auto-assembling
RAID arrays, since we skip the homehost validation altogether.
2021-04-26 17:23:48 +02:00
Raphael Megzari 4e59dfe221
Merge pull request #6 from asymmetric/patch-1
hetzner: add FIXME placeholders
2021-04-10 00:11:40 +09:00
Lorenzo Manacorda 3ad26c49a9 remove FIXME from networking.hostname
Can be safely changed afterwards.
2021-04-09 17:07:17 +02:00
Raphael Megzari d5a3443410
Add zfs uefi (#7)
* initial try at adding zfs and uefi

* update script to remove rescue mode

* various fixes

* remove uneeded grub efi

* script improvements

Co-authored-by: Sandro <sandro.jaeckel@gmail.com>

Co-authored-by: Sandro <sandro.jaeckel@gmail.com>
2021-04-09 14:58:27 +02:00
asymmetric 85994a5057
hetzner: add FIXME placeholders
To make the necessary edits easier to find.
2021-01-28 10:16:20 +00:00
Niklas Hambüchen 5134fc83e8 hetzner-cloud: Use poweroff instead of reboot.
Reduces chance of users not noticing that without unmounting
the image, they'll boot back into the NixOS installer.
2020-12-30 17:37:59 +01:00
6 changed files with 467 additions and 31 deletions

View file

@ -7,7 +7,9 @@
- Fork this repo and replace your SSH public key in the script. For exapmle, make a commit on a new branch.
- Get the URL of the script you created (might look like `https://raw.github.com/YOUR_USER_NAME/nixos-install-scripts/YOUR_BRANCH_NAME/hosters/hetzner-cloud/nixos-install-hetzner-cloud.sh`).
- Paste the following command into your console gui `curl -L YOUR_URL | sudo bash`.
4. on your own computer you can now ssh into the newly created machine.
This will install NixOS and turn the server off.
4. In your server options on the Hetzner UI click on `Unmount`, and turn the server back on using the big power-switch button in the top right.
5. On your own computer you can now ssh into the newly created machine.
# Troubleshooting

View file

@ -17,8 +17,10 @@
#
# # Replace this URL by your own that has your pubkey in
# curl -L https://raw.githubusercontent.com/nix-community/nixos-install-scripts/master/hosters/hetzner-cloud/nixos-install-hetzner-cloud.sh | sudo bash
#
# This will install NixOS and power off the server.
# 4. Unmount the ISO image from the Hetzner Cloud GUI.
# 5. Reboot.
# 5. Turn the server back on from the Hetzner Cloud GUI.
#
# To run it from the Hetzner Cloud web terminal without typing it down,
# you can either select it and then middle-click onto the web terminal, (that pastes
@ -71,4 +73,4 @@ echo '
nixos-install --no-root-passwd
reboot
poweroff

View file

@ -5,7 +5,7 @@
# This is for a specific server configuration; adjust where needed.
#
# Prerequisites:
# * Update the script to put in your SSH pubkey, adjust hostname, NixOS version etc.
# * Update the script wherever FIXME is present
#
# Usage:
# ssh root@YOUR_SERVERS_IP bash -s < hetzner-dedicated-wipe-and-install-nixos.sh
@ -59,9 +59,13 @@ mdadm --stop --scan
echo 'AUTO -all
ARRAY <ignore> UUID=00000000:00000000:00000000:00000000' > /etc/mdadm/mdadm.conf
# Create wrapper for parted >= 3.3 that does not exit 1 when it cannot inform
# the kernel of partitions changing (we use partprobe for that).
echo -e "#! /usr/bin/env bash\nset -e\n" 'parted $@ 2> parted-stderr.txt || grep "unable to inform the kernel of the change" parted-stderr.txt && echo "This is expected, continuing" || echo >&2 "Parted failed; stderr: $(< parted-stderr.txt)"' > parted-ignoring-partprobe-error.sh && chmod +x parted-ignoring-partprobe-error.sh
# Create partition tables (--script to not ask)
parted --script /dev/sda mklabel gpt
parted --script /dev/sdb mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/sda mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/sdb mklabel gpt
# Create partitions (--script to not ask)
#
@ -78,10 +82,10 @@ parted --script /dev/sdb mklabel gpt
# ... part-type is one of 'primary', 'extended' or 'logical', and may be specified only with 'msdos' or 'dvh' partition tables.
# A name must be specified for a 'gpt' partition table.
# GPT partition names are limited to 36 UTF-16 chars, see https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries_(LBA_2-33).
parted --script --align optimal /dev/sda -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
parted --script --align optimal /dev/sdb -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal /dev/sda -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal /dev/sdb -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
# Relaod partitions
# Reload partitions
partprobe
# Wait for all devices to exist
@ -160,8 +164,7 @@ set +u +x # sourcing this may refer to unset variables that we have no control o
. $HOME/.nix-profile/etc/profile.d/nix.sh
set -u -x
# Keep in sync with `system.stateVersion` set below!
# nix-channel --add https://nixos.org/channels/nixos-20.03 nixpkgs
# FIXME Keep in sync with `system.stateVersion` set below!
nix-channel --add https://nixos.org/channels/nixos-20.03 nixpkgs
nix-channel --update
@ -219,7 +222,6 @@ cat > /mnt/etc/nixos/configuration.nix <<EOF
devices = [ "/dev/sda" "/dev/sdb" ];
};
networking.hostName = "hetzner";
# The mdadm RAID1s were created with 'mdadm --create ... --homehost=hetzner',
@ -231,13 +233,11 @@ cat > /mnt/etc/nixos/configuration.nix <<EOF
# This is mdadm's protection against accidentally putting a RAID disk
# into the wrong machine and corrupting data by accidental sync, see
# https://bugzilla.redhat.com/show_bug.cgi?id=606481#c14 and onward.
# We set the HOMEHOST manually go get the short '/dev/md' names,
# and so that things look and are configured the same on all such
# machines irrespective of host names.
# We do not worry about plugging disks into the wrong machine because
# we will never exchange disks between machines.
# we will never exchange disks between machines, so we tell mdadm to
# ignore the homehost entirely.
environment.etc."mdadm.conf".text = ''
HOMEHOST hetzner
HOMEHOST <ignore>
'';
# The RAIDs are assembled in stage1, so we need to make the config
# available there.
@ -248,6 +248,12 @@ cat > /mnt/etc/nixos/configuration.nix <<EOF
networking.interfaces."$NIXOS_INTERFACE".ipv4.addresses = [
{
address = "$IP_V4";
# FIXME: The prefix length is commonly, but not always, 24.
# You should check what the prefix length is for your server
# by inspecting the netmask in the "IPs" tab of the Hetzner UI.
# For example, a netmask of 255.255.255.0 means prefix length 24
# (24 leading 1s), and 255.255.255.192 means prefix length 26
# (26 leading 1s).
prefixLength = 24;
}
];
@ -266,12 +272,13 @@ cat > /mnt/etc/nixos/configuration.nix <<EOF
services.openssh.permitRootLogin = "prohibit-password";
users.users.root.openssh.authorizedKeys.keys = [
# Replace this by your SSH pubkey!
# FIXME Replace this by your SSH pubkey!
"ssh-rsa AAAAAAAAAAA..."
];
services.openssh.enable = true;
# FIXME
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database
# servers. You should change this only after NixOS release notes say you

View file

@ -0,0 +1,417 @@
#!/usr/bin/env bash
# Installs NixOS on a Hetzner server, wiping the server.
#
# This is for a specific server configuration; adjust where needed.
#
#
# Usage:
# ssh root@YOUR_SERVERS_IP bash -s < hetzner-dedicated-wipe-and-install-nixos.sh
#
# When the script is done, make sure to boot the server from HD, not rescue mode again.
# Explanations:
#
# * Following largely https://nixos.org/nixos/manual/index.html#sec-installing-from-other-distro.
# * and https://nixos.wiki/wiki/NixOS_on_ZFS
# * **Important:** First you need to boot in legacy-BIOS mode. Then ask for
# hetzner support to enable UEFI for you.
# * We set a custom `configuration.nix` so that we can connect to the machine afterwards,
# inspired by https://nixos.wiki/wiki/Install_NixOS_on_Hetzner_Online
# * This server has 2 SSDs.
# We put everything on mirror (RAID1 equivalent).
# * A root user with empty password is created, so that you can just login
# as root and press enter when using the Hetzner spider KVM.
# Of course that empty-password login isn't exposed to the Internet.
# Change the password afterwards to avoid anyone with physical access
# being able to login without any authentication.
# * The script reboots at the end.
# * exports of env vars are added throughout the script in case you want to run it manually
export LC_ALL=C
# WARNING: on 2023/07/16 the rescue system of hetzner boots with kernel 6.3.7 which
# is by default not supported by the latest debian package. You need to update to debian
# unstable to proceed with the zfs installation.
cat > /etc/apt/preferences.d/90_zfs <<EOF
Package: libnvpair1linux libnvpair3linux libuutil1linux libuutil3linux libzfs2linux libzfs4linux libzpool2linux libzpool4linux spl-dkms zfs-dkms zfs-test zfsutils-linux zfsutils-linux-dev zfs-zed
Pin: release n=bullseye-backports
Pin-Priority: 990
EOF
apt update -y
apt install -y dpkg-dev linux-headers-$(uname -r) linux-image-amd64 sudo parted zfs-dkms zfsutils-linux
set -euox pipefail
# hetzner has some weird symlinks to make you install zfs with their script
rm /usr/local/sbin/zfs || true
rm /usr/local/sbin/zpool || true
# Inspect existing disks
# Should give you something like
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# nvme0n1 259:0 0 476.9G 0 disk
# ├─nvme0n1p1 259:2 0 32G 0 part
# │ └─md0 9:0 0 32G 0 raid1 [SWAP]
# ├─nvme0n1p2 259:3 0 512M 0 part
# │ └─md1 9:1 0 511M 0 raid1 /boot
# └─nvme0n1p3 259:4 0 444.4G 0 part
# └─md2 9:2 0 444.3G 0 raid1 /
# nvme1n1 259:1 0 476.9G 0 disk
# ├─nvme1n1p1 259:5 0 32G 0 part
# │ └─md0 9:0 0 32G 0 raid1 [SWAP]
# ├─nvme1n1p2 259:6 0 512M 0 part
# │ └─md1 9:1 0 511M 0 raid1 /boot
# └─nvme1n1p3 259:7 0 444.4G 0 part
# └─md2 9:2 0 444.3G 0 raid1 /
lsblk
# check the disks that you have available
# you have to use disks by ID with zfs
# see https://openzfs.github.io/openzfs-docs/Getting%20Started/Ubuntu/Ubuntu%2020.04%20Root%20on%20ZFS.html#step-2-disk-formatting
ls /dev/disk/by-id
# should give you something like this
# md-name-rescue:0 nvme-eui.0025388a01051b58-part1
# md-name-rescue:1 nvme-eui.0025388a01051b58-part2
# md-name-rescue:2 nvme-eui.0025388a01051b58-part3
# md-uuid-15391820:32e070f6:ecbfb99e:e983e018 nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00424
# md-uuid-48379d14:3c44fe11:e6528eec:ad784ade nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00424-part1
# md-uuid-f2a894fc:9e90e3af:9af81d28:b120ae1f nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00424-part2
# nvme-eui.0025388a01051b55 nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00424-part3
# nvme-eui.0025388a01051b55-part1 nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00427
# nvme-eui.0025388a01051b55-part2 nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00427-part1
# nvme-eui.0025388a01051b55-part3 nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00427-part2
# nvme-eui.0025388a01051b58 nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00427-part3
#
# we will use the two disks
# nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00424
# nvme-SAMSUNG_MZVLB512HBJQ-00000_S4GENA0NA00427
# The following variables should be replaced
export DISK1=/dev/disk/by-id/nvme-SAMSUNG_MZQLB3T8HALS-00007_S438NC0R804840
export DISK2=/dev/disk/by-id/nvme-SAMSUNG_MZQLB3T8HALS-00007_S438NC0R811800
# Replace with your key
export SSH_PUB_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyQSeQ0CV/qhZPre37+Nd0E9eW+soGs+up6a/bwggoP raphael@RAPHAELs-MacBook-Pro.local"
# choose whatever you want, it doesn't matter
export MY_HOSTNAME=htz
# this has to be a number in this format exactly. You can replace the numbers though
export MY_HOSTID=00000001
# Undo existing setups to allow running the script multiple times to iterate on it.
# We allow these operations to fail for the case the script runs the first time.
umount /mnt || true
vgchange -an || true
# Stop all mdadm arrays that the boot may have activated.
mdadm --stop --scan
# Prevent mdadm from auto-assembling arrays.
# Otherwise, as soon as we create the partition tables below, it will try to
# re-assemple a previous RAID if any remaining RAID signatures are present,
# before we even get the chance to wipe them.
# From:
# https://unix.stackexchange.com/questions/166688/prevent-debian-from-auto-assembling-raid-at-boot/504035#504035
# We use `>` because the file may already contain some detected RAID arrays,
# which would take precedence over our `<ignore>`.
echo 'AUTO -all
ARRAY <ignore> UUID=00000000:00000000:00000000:00000000' > /etc/mdadm/mdadm.conf
# Create wrapper for parted >= 3.3 that does not exit 1 when it cannot inform
# the kernel of partitions changing (we use partprobe for that).
echo -e "#! /usr/bin/env bash\nset -e\n" 'parted $@ 2> parted-stderr.txt || grep "unable to inform the kernel of the change" parted-stderr.txt && echo "This is expected, continuing" || echo >&2 "Parted failed; stderr: $(< parted-stderr.txt)"' > parted-ignoring-partprobe-error.sh && chmod +x parted-ignoring-partprobe-error.sh
# Create partition tables (--script to not ask)
./parted-ignoring-partprobe-error.sh --script $DISK1 mklabel gpt
./parted-ignoring-partprobe-error.sh --script $DISK2 mklabel gpt
# Create partitions (--script to not ask)
#
# We create the 1MB BIOS boot partition at the front.
#
# Note we use "MB" instead of "MiB" because otherwise `--align optimal` has no effect;
# as per documentation https://www.gnu.org/software/parted/manual/html_node/unit.html#unit:
# > Note that as of parted-2.4, when you specify start and/or end values using IEC
# > binary units like "MiB", "GiB", "TiB", etc., parted treats those values as exact
#
# Note: When using `mkpart` on GPT, as per
# https://www.gnu.org/software/parted/manual/html_node/mkpart.html#mkpart
# the first argument to `mkpart` is not a `part-type`, but the GPT partition name:
# ... part-type is one of 'primary', 'extended' or 'logical', and may be specified only with 'msdos' or 'dvh' partition tables.
# A name must be specified for a 'gpt' partition table.
# GPT partition names are limited to 36 UTF-16 chars, see https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries_(LBA_2-33).
# TODO the bios partition should not be this big
# however if it's less the installation fails with
# cannot copy /nix/store/d4xbrrailkn179cdp90v4m57mqd73hvh-linux-5.4.100/bzImage to /boot/kernels/d4xbrrailkn179cdp90v4m57mqd73hvh-linux-5.4.100-bzImage.tmp: No space left on device
./parted-ignoring-partprobe-error.sh --script --align optimal $DISK1 -- mklabel gpt \
mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on \
mkpart 'EFI-system-partition' 2MB 512MB set 2 esp on \
mkpart 'data-partition' 512MB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal $DISK2 -- mklabel gpt \
mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on \
mkpart 'EFI-system-partition' 2MB 512MB set 2 esp on \
mkpart 'data-partition' 512MB '100%'
# Reload partitions
partprobe
# Wait for all devices to exist
udevadm settle --timeout=5 --exit-if-exists=$DISK1-part1
udevadm settle --timeout=5 --exit-if-exists=$DISK1-part2
udevadm settle --timeout=5 --exit-if-exists=$DISK1-part3
udevadm settle --timeout=5 --exit-if-exists=$DISK2-part1
udevadm settle --timeout=5 --exit-if-exists=$DISK2-part2
udevadm settle --timeout=5 --exit-if-exists=$DISK2-part3
# Wipe any previous RAID signatures
# sometimes they are not on a specific disk for some reason
mdadm --zero-superblock --force $DISK1-part1 || true
mdadm --zero-superblock --force $DISK1-part2 || true
mdadm --zero-superblock --force $DISK1-part3 || true
mdadm --zero-superblock --force $DISK2-part1 || true
mdadm --zero-superblock --force $DISK2-part2 || true
mdadm --zero-superblock --force $DISK2-part3 || true
# Creating file systems changes their UUIDs.
# Trigger udev so that the entries in /dev/disk/by-uuid get refreshed.
# `nixos-generate-config` depends on those being up-to-date.
# See https://github.com/NixOS/nixpkgs/issues/62444
udevadm trigger
# taken from https://nixos.wiki/wiki/NixOS_on_ZFS
# somehow there is a weird symlink in the default zfs
zpool create -O mountpoint=none \
-O atime=off \
-O compression=lz4 \
-O xattr=sa \
-O acltype=posixacl \
-o ashift=12 \
-f \
root_pool mirror $DISK1-part3 $DISK2-part3
# Create the filesystems. This layout is designed so that /home is separate from the root
# filesystem, as you'll likely want to snapshot it differently for backup purposes. It also
# makes a "nixos" filesystem underneath the root, to support installing multiple OSes if
# that's something you choose to do in future.
zfs create -o mountpoint=legacy root_pool/root
zfs create -o mountpoint=legacy root_pool/root/nixos
zfs create -o mountpoint=legacy root_pool/home
# add 1G of reseved space in case the disk gets full
# zfs needs space to delete files
zfs create -o refreservation=1G -o mountpoint=none root_pool/reserved
# this creates a special volume for db data see https://wiki.archlinux.org/index.php/ZFS#Databases
zfs create -o mountpoint=legacy \
-o recordsize=8K \
-o primarycache=metadata \
-o logbias=throughput \
root_pool/postgres
# NixOS pre-installation mounts
#
# Mount the filesystems manually. The nixos installer will detect these mountpoints
# and save them to /mnt/nixos/hardware-configuration.nix during the install process.
mount -t zfs root_pool/root/nixos /mnt
mkdir /mnt/home
mount -t zfs root_pool/home /mnt/home
mkdir -p /mnt/var/lib/postgres
mount -t zfs root_pool/postgres /mnt/var/lib/postgres
# Create a raid mirror for the efi boot
# see https://docs.hetzner.com/robot/dedicated-server/operating-systems/efi-system-partition/
# TODO check this though the following article says it doesn't work properly
# https://outflux.net/blog/archives/2018/04/19/uefi-booting-and-raid1/
mdadm --create --run --verbose /dev/md127 \
--level 1 \
--raid-disks 2 \
--metadata 1.0 \
--homehost=$MY_HOSTNAME \
--name=boot_efi \
$DISK1-part2 $DISK2-part2
# Assembling the RAID can result in auto-activation of previously-existing LVM
# groups, preventing the RAID block device wiping below with
# `Device or resource busy`. So disable all VGs first.
vgchange -an
# Wipe filesystem signatures that might be on the RAID from some
# possibly existing older use of the disks (RAID creation does not do that).
# See https://serverfault.com/questions/911370/why-does-mdadm-zero-superblock-preserve-file-system-information
wipefs -a /dev/md127
# Disable RAID recovery. We don't want this to slow down machine provisioning
# in the rescue mode. It can run in normal operation after reboot.
echo 0 > /proc/sys/dev/raid/speed_limit_max
# Filesystems (-F to not ask on preexisting FS)
mkfs.vfat -F 32 /dev/md127
# Creating file systems changes their UUIDs.
# Trigger udev so that the entries in /dev/disk/by-uuid get refreshed.
# `nixos-generate-config` depends on those being up-to-date.
# See https://github.com/NixOS/nixpkgs/issues/62444
udevadm trigger
mkdir -p /mnt/boot/efi
mount /dev/md127 /mnt/boot/efi
# Installing nix
# Allow installing nix as root, see
# https://github.com/NixOS/nix/issues/936#issuecomment-475795730
mkdir -p /etc/nix
echo "build-users-group =" > /etc/nix/nix.conf
# using determinate systems installer, for more information
# check https://github.com/DeterminateSystems/nix-installer
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Keep in sync with `system.stateVersion` set below!
nix-channel --add https://nixos.org/channels/nixos-23.05 nixpkgs
nix-channel --update
# TODO use something like nix shell nixpkgs#nixos-generate-config nixpkgs#nixos-install nixpkgs#nixos-enter nixpkgs#manual.manpages
# Getting NixOS installation tools
nix-env -iE "_: with import <nixpkgs/nixos> { configuration = {}; }; with config.system.build; [ nixos-generate-config nixos-install nixos-enter manual.manpages ]"
# TODO
# perl: warning: Please check that your locale settings:
# LANGUAGE = (unset),
# LC_ALL = "en_US.UTF-8",
# LANG = "en_US.UTF-8"
# are supported and installed on your system.
nixos-generate-config --root /mnt
# Find the name of the network interface that connects us to the Internet.
# Inspired by https://unix.stackexchange.com/questions/14961/how-to-find-out-which-interface-am-i-using-for-connecting-to-the-internet/302613#302613
export RESCUE_INTERFACE=$(ip route get 8.8.8.8 | grep -Po '(?<=dev )(\S+)')
# Find what its name will be under NixOS, which uses stable interface names.
# See https://major.io/2015/08/21/understanding-systemds-predictable-network-device-names/#comment-545626
# NICs for most Hetzner servers are not onboard, which is why we use
# `ID_NET_NAME_PATH`otherwise it would be `ID_NET_NAME_ONBOARD`.
export INTERFACE_DEVICE_PATH=$(udevadm info -e | grep -Po "(?<=^P: )(.*${RESCUE_INTERFACE})")
export UDEVADM_PROPERTIES_FOR_INTERFACE=$(udevadm info --query=property "--path=$INTERFACE_DEVICE_PATH")
export NIXOS_INTERFACE=$(echo "$UDEVADM_PROPERTIES_FOR_INTERFACE" | grep -o -E 'ID_NET_NAME_PATH=\w+' | cut -d= -f2)
echo "Determined NIXOS_INTERFACE as '$NIXOS_INTERFACE'"
export IP_V4=$(ip route get 8.8.8.8 | grep -Po '(?<=src )(\S+)')
echo "Determined IP_V4 as $IP_V4"
# Find what its name will be under NixOS, which uses stable interface names.
# See https://major.io/2015/08/21/understanding-systemds-predictable-network-device-names/#comment-545626
# NICs for most Hetzner servers are not onboard, which is why we use
# `ID_NET_NAME_PATH`otherwise it would be `ID_NET_NAME_ONBOARD`.
export INTERFACE_DEVICE_PATH=$(udevadm info -e | grep -Po "(?<=^P: )(.*${RESCUE_INTERFACE})")
export UDEVADM_PROPERTIES_FOR_INTERFACE=$(udevadm info --query=property "--path=$INTERFACE_DEVICE_PATH")
export NIXOS_INTERFACE=$(echo "$UDEVADM_PROPERTIES_FOR_INTERFACE" | grep -o -E 'ID_NET_NAME_PATH=\w+' | cut -d= -f2)
echo "Determined NIXOS_INTERFACE as '$NIXOS_INTERFACE'"
# Determine Internet IPv6 by checking route, and using ::1
# (because Hetzner rescue mode uses ::2 by default).
# The `ip -6 route get` output on Hetzner looks like:
# # ip -6 route get 2001:4860:4860:0:0:0:0:8888
# 2001:4860:4860::8888 via fe80::1 dev eth0 src 2a01:4f8:151:62aa::2 metric 1024 pref medium
export IP_V6="$(ip route get 2001:4860:4860::8888 | head -1 | cut -d' ' -f7 | cut -d: -f1-4)::1"
echo "Determined IP_V6 as $IP_V6"
# From https://stackoverflow.com/questions/1204629/how-do-i-get-the-default-gateway-in-linux-given-the-destination/15973156#15973156
read _ _ DEFAULT_GATEWAY _ < <(ip route list match 0/0); echo "$DEFAULT_GATEWAY"
echo "Determined DEFAULT_GATEWAY as $DEFAULT_GATEWAY"
# Generate `configuration.nix`. Note that we splice in shell variables.
cat > /mnt/etc/nixos/configuration.nix <<EOF
{ config, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
# Use GRUB2 as the boot loader.
# We don't use systemd-boot because Hetzner uses BIOS legacy boot.
boot.loader.systemd-boot.enable = false;
boot.loader.grub = {
enable = true;
efiSupport = false;
devices = ["$DISK1" "$DISK2"];
copyKernels = true;
};
boot.supportedFilesystems = [ "zfs" ];
networking.hostName = "$MY_HOSTNAME";
networking.hostId = "$MY_HOSTID";
# enable flakes by default
nix = {
package = pkgs.nixFlakes;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
# Set your time zone.
time.timeZone = "Etc/UTC";
environment = {
enableDebugInfo = true;
# just a couple of packages to make our lives easier
systemPackages = with pkgs; [ vim ];
};
# Network (Hetzner uses static IP assignments, and we don't use DHCP here)
networking.useDHCP = false;
networking.interfaces."$NIXOS_INTERFACE".ipv4.addresses = [
{
address = "$IP_V4";
prefixLength = 24;
}
];
networking.interfaces."$NIXOS_INTERFACE".ipv6.addresses = [
{
address = "$IP_V6";
prefixLength = 64;
}
];
networking.defaultGateway = "$DEFAULT_GATEWAY";
networking.defaultGateway6 = { address = "fe80::1"; interface = "$NIXOS_INTERFACE"; };
networking.nameservers = [
# cloudflare
"1.1.1.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
# google
"8.8.8.8"
"2001:4860:4860::8888"
"2001:4860:4860::8844"
];
# Initial empty root password for easy login:
users.users.root.initialHashedPassword = "";
services.openssh.permitRootLogin = "prohibit-password";
users.users.root.openssh.authorizedKeys.keys = ["$SSH_PUB_KEY"];
services.openssh.enable = true;
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database
# servers. You should change this only after NixOS release notes say you
# should.
system.stateVersion = "23.05"; # Did you read the comment?
}
EOF
# Install NixOS
PATH="$PATH" $(which nixos-install) \
--no-root-passwd --root /mnt --max-jobs 40
umount /mnt
reboot
# if you need to debug something
# - connect to the rescue system
# - install zfs
# ```
# zpool import -f root_pool temp_pool
# mount -t zfs temp_pool/root/nixos /mnt
# journalctl --directory=/mnt/var/log/journal

View file

@ -61,9 +61,13 @@ mdadm --stop --scan
echo 'AUTO -all
ARRAY <ignore> UUID=00000000:00000000:00000000:00000000' > /etc/mdadm/mdadm.conf
# Create wrapper for parted >= 3.3 that does not exit 1 when it cannot inform
# the kernel of partitions changing (we use partprobe for that).
echo -e "#! /usr/bin/env bash\nset -e\n" 'parted $@ 2> parted-stderr.txt || grep "unable to inform the kernel of the change" parted-stderr.txt && echo "This is expected, continuing" || echo >&2 "Parted failed; stderr: $(< parted-stderr.txt)"' > parted-ignoring-partprobe-error.sh && chmod +x parted-ignoring-partprobe-error.sh
# Create partition tables (--script to not ask)
parted --script /dev/sda mklabel gpt
parted --script /dev/sdb mklabel gpt
./parted-ignoring-partprobe-error.sh /dev/sda mklabel gpt
./parted-ignoring-partprobe-error.sh /dev/sdb mklabel gpt
# Create partitions (--script to not ask)
#
@ -80,8 +84,8 @@ parted --script /dev/sdb mklabel gpt
# ... part-type is one of 'primary', 'extended' or 'logical', and may be specified only with 'msdos' or 'dvh' partition tables.
# A name must be specified for a 'gpt' partition table.
# GPT partition names are limited to 36 UTF-16 chars, see https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries_(LBA_2-33).
parted --script --align optimal /dev/sda -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
parted --script --align optimal /dev/sdb -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
./parted-ignoring-partprobe-error.sh --align optimal /dev/sda -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
./parted-ignoring-partprobe-error.sh --align optimal /dev/sdb -- mklabel gpt mkpart 'BIOS-boot-partition' 1MB 2MB set 1 bios_grub on mkpart 'data-partition' 2MB '100%'
# Relaod partitions
partprobe

View file

@ -57,12 +57,16 @@ set -e
# Stop all mdadm arrays that the boot may have activated.
mdadm --stop --scan
# Create wrapper for parted >= 3.3 that does not exit 1 when it cannot inform
# the kernel of partitions changing (we use partprobe for that).
echo -e "#! /usr/bin/env bash\nset -e\n" 'parted $@ 2> parted-stderr.txt || grep "unable to inform the kernel of the change" parted-stderr.txt && echo "This is expected, continuing" || echo >&2 "Parted failed; stderr: $(< parted-stderr.txt)"' > parted-ignoring-partprobe-error.sh && chmod +x parted-ignoring-partprobe-error.sh
# Create partition tables (--script to not ask)
parted --script /dev/sda mklabel gpt
parted --script /dev/sdb mklabel gpt
parted --script /dev/sdc mklabel gpt
parted --script /dev/sdd mklabel gpt
parted --script /dev/nvme0n1 mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/sda mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/sdb mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/sdc mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/sdd mklabel gpt
./parted-ignoring-partprobe-error.sh --script /dev/nvme0n1 mklabel gpt
# Create partitions (--script to not ask)
#
@ -84,10 +88,10 @@ parted --script /dev/nvme0n1 mklabel gpt
# ... part-type is one of 'primary', 'extended' or 'logical', and may be specified only with 'msdos' or 'dvh' partition tables.
# A name must be specified for a 'gpt' partition table.
# GPT partition names are limited to 36 UTF-16 chars, see https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries_(LBA_2-33).
parted --script --align optimal /dev/sda -- mklabel gpt mkpart 'ESP-partition0' fat32 1MB 551MB set 1 esp on mkpart 'OS-partition0' 551MB 500GB mkpart 'data-partition0' 500GB '100%'
parted --script --align optimal /dev/sdb -- mklabel gpt mkpart 'ESP-partition1' fat32 1MB 551MB set 1 esp on mkpart 'OS-partition1' 551MB 500GB mkpart 'data-partition1' 500GB '100%'
parted --script --align optimal /dev/sdc -- mklabel gpt mkpart 'ESP-partition2-unused' fat32 1MB 551MB set 1 esp off mkpart 'data-partition2' 551MB '100%'
parted --script --align optimal /dev/sdd -- mklabel gpt mkpart 'ESP-partition3-unused' fat32 1MB 551MB set 1 esp off mkpart 'data-partition3' 551MB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal /dev/sda -- mklabel gpt mkpart 'ESP-partition0' fat32 1MB 551MB set 1 esp on mkpart 'OS-partition0' 551MB 500GB mkpart 'data-partition0' 500GB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal /dev/sdb -- mklabel gpt mkpart 'ESP-partition1' fat32 1MB 551MB set 1 esp on mkpart 'OS-partition1' 551MB 500GB mkpart 'data-partition1' 500GB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal /dev/sdc -- mklabel gpt mkpart 'ESP-partition2-unused' fat32 1MB 551MB set 1 esp off mkpart 'data-partition2' 551MB '100%'
./parted-ignoring-partprobe-error.sh --script --align optimal /dev/sdd -- mklabel gpt mkpart 'ESP-partition3-unused' fat32 1MB 551MB set 1 esp off mkpart 'data-partition3' 551MB '100%'
# Relaod partitions
partprobe