Install NixOS on Hetzner Cloud

From NixOS Wiki
Revision as of 21:53, 9 June 2024 by Hexa (talk | contribs) (→‎Network configuration: nixfmt, more commenting)
Note: This article is about installing NixOS on Hetzner Cloud, which provides VPS cloud servers. It is not to be confused with Hetzner Online, which provides dedicated bare-metal servers.

Hetzner Cloud in 2023 offers amd64 instances on both Intel and AMD hardware, as well as arm64 instanced based on the Ampere Altra Q80, in three locations in Europe, as well as two in America.

The pricing of the arm64 instances is generally considered very competitive, compared to similarly specced amd64 instances.

Installation

There are several ways to install NixOS, such as the "traditional" ISO installation, nixos-infect or nixos-anywhere.

From NixOS minimal ISO

  1. Create a new instance and power it off
  2. Switch to the ISO-Images tab and mount the NixOS minimal ISO
  3. Open the remote console (>_ button) and power the machine on
  4. Follow the usual installation guide
    • Use EFI for arm64 instances, MBR for x86 instances
  5. Unmount the ISO and reboot

disko

TODO: it would be neat to document how to boot from the NixOS ISO and create the machine based on an online description including https://github.com/nix-community/disko specs - should be a quick way to set up bespoke 'throwaway' machines.

nixos-infect

Beside the manual installation, one way to setup NixOS is to replace an existing installation, for example the latest Ubuntu image, with nixos-infect.

Cloud-init

You don't even need to log into the Ubuntu image, you can run nixos-infect from the 'could-init' as documented at https://github.com/elitak/nixos-infect/tree/master#hetzner-cloud

Manually

  • Boot into the existing operating system, preferably Ubuntu or Debian
  • Login as root or with root permissions
  • Deploy your SSH public key for the current root user. This key will be used later for authentication into the NixOS system.
  • Run following script. Replace NIX_CHANNEL variable with the version string you wish to install.
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-22.11 bash -x
  • Reboot into NixOS

Declarative

TODO are any of the 'declarative' configuration management systems like NixOps or Terraform able to spin up Hetzner machines according to a particular configuration? If so then it would be nice to document this here :)

Network configuration

Hetzner Cloud offers both IPv4 (/32 subnet) and IPv6 (/64 subnet) connectivity to each machine. The assigned addresses can be looked up on the Hetzner Cloud Console from the "Networking" tab on the instance details. The public IPv4 address of the server can automatically obtained be via DHCP. For IPv6 you have to statically configure both address and gateway.

  systemd.network.enable = true;
  systemd.network.networks."30-wan" = {
    matchConfig.Name = "ens3"; # either ens3 (amd64) or enp1s0 (arm64)
    networkConfig.DHCP = "ipv4";
    address = [
      # replace this subnet with the one assigned to your instance
      "2a01:4f8:aaaa:bbbb::1/64"
    ];
    routes = [ { routeConfig.Gateway = "fe80::1"; } ];
  };

Static IPv4 configuration

The IPv4 address can also be configured statically. The trick here is, that the gateway needs to be configured with the onlink flag, because it is not in the same subnet as your public IP address, but still very much on that same link.

  systemd.network.networks."30-wan" = {
    matchConfig.Name = "ens3"; # either ens3 (amd64) or enp1s0 (arm64)
    networkConfig.DHCP = "no";
    address = [
      # replace this address with the one assigned to your instance
      "A.B.C.D/32"
      # replace this subnet with the one assigned to your instance
      "2a01:4f8:aaaa:bbbb::1/64"
    ];
    routes = [ {
      routeConfig = { Gateway = "172.31.1.1"; GatewayOnLink = true; }; }
      { routeConfig.Gateway = "fe80::1"; }
    ];
  };

AArch64 (CAX instance type) specifics

If the screen goes blank after selecting the boot option in the bootloader, the following snippet makes sure that GPU drivers are available in initrd, and the correct device gets used for the serial console:

boot.initrd.kernelModules = [ "virtio_gpu" ];
boot.kernelParams = [ "console=tty" ];