Install NixOS on Hetzner Cloud: Difference between revisions
Install NixOS on Hetzner Cloud (view source)
Revision as of 21:10, 26 June 2024
, Wednesday at 21:10no edit summary
(add some library references) |
Oldhomemovie (talk | contribs) mNo edit summary |
||
(One intermediate revision by the same user not shown) | |||
Line 10: | Line 10: | ||
=== From NixOS minimal ISO === | === From NixOS minimal ISO === | ||
# Create a | The tutorial assumes you already have an account on Hetzner Cloud, and no prior access to a system with NixOS or nix CLI utility installed: | ||
# | # Create a temp folder for future use. Run:<syntaxhighlight lang="shell"> | ||
# Open the remote | mkdir /tmp/my-first-flake | ||
# | </syntaxhighlight>'''Note''': this folder will be mounted into docker container on the next step. Having the folder on the host system enables editing files using a familiar editor, available on the host system, such as VS Code or neovim. | ||
# | #Enter docker container. Run:<syntaxhighlight lang="shell"> | ||
# | docker run --rm --interactive --tty --mount type=bind,source=/tmp/my-first-flake,target=/tmp/my-first-flake alpine:3.20 ash | ||
</syntaxhighlight>'''Note''': this is done in a container in order to reduce the "setup footprint and residue", allowing to throw away this setup environment quickly. | |||
#Install <code>nix</code> and <code>hcloud</code> CLI utilities. Run:<syntaxhighlight lang="shell"> | |||
apk add nix hcloud openssh-client | |||
</syntaxhighlight> | |||
#Authenticate <code>hcloud</code> CLI utility. Run:<syntaxhighlight lang="shell"> | |||
hcloud context create my-first-context | |||
</syntaxhighlight> | |||
#When asked, enter value of the token in the prompt. '''Note''': the token with "Read/Write" permissions can be obtained on a project page inside Hetzner Cloud: <nowiki>https://console.hetzner.cloud/projects/0000000/security/tokens</nowiki> | |||
#Create a VM on Hetzner. Run:<syntaxhighlight lang="shell"> | |||
hcloud server create --name my-hetzner-vm --type cpx21 --image ubuntu-24.04 --location fsn1 --start-after-create=false | |||
</syntaxhighlight>'''Note 1''': this tutorial uses <code>cpx21</code> VM instance type which corresponds to an x86 architecture marchine with 3 CPU cores and 4GB of RAM, and <code>fsn1</code> location which corresponds to a data center in the city of Falkenstein in Germany. A list of all instance types can be obtained by running command <code>hcloud server-type list</code>, while a list of all locations can be obtained by running <code>hcloud location list</code> command. '''Note 2''': Hopefully, Hetzner Cloud team will support NixOS disk images soon, see [https://www.reddit.com/r/NixOS/comments/1desdbv/could_we_convince_hetzner_to_add_nixos_as_a/ Could we convince Hetzner to add Nixos as a standard image choice]. | |||
#Attach an ISO with NixOS installer. Run:<syntaxhighlight lang="shell"> | |||
hcloud server attach-iso my-hetzner-vm nixos-minimal-24.05.1503.752c634c09ce-aarch64-linux.iso | |||
</syntaxhighlight>'''Note''': Hetzner attempts to keep the image as up-to-date as possible, hence the hash of the nixos-minimal image at the time of following this tutorial is highly likely to have changed. Run <code>hcloud iso list</code> and look up an up-to-date name of the nixos-minimal ISO image. | |||
#Start a VM. Run:<syntaxhighlight lang="shell"> | |||
hcloud server poweron my-hetzner-vm | |||
</syntaxhighlight> | |||
#Open Hetzner Cloud console web page, find the <code>my-hetzner-vm</code> server, open a remote web terminal (aka "VNC over "wss://") and change password of <code>nixos</code> user to <code>my-temp-password-123</code>:[[File:Prompt_with_a_token.png|right|frameless|197x197px]] | |||
# On your host computer, create a folder. Run:<syntaxhighlight lang="shell"> | |||
mkdir -p /tmp/my-first-flake/my-systems/my-hetzner-vm/ | |||
</syntaxhighlight> | |||
#Using a code editor on your host computer, create 4 files. File contents, as well as the location of where to put corresponding file are indicated below:<syntaxhighlight lang="nix"> | |||
# /tmp/my-first-flake/my-systems/my-hetzner-vm/hardware-configuration.nix | |||
{ config, lib, pkgs, modulesPath, ... }: | |||
{ | |||
imports = [ | |||
(modulesPath + "/profiles/qemu-guest.nix") | |||
]; | |||
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ]; | |||
boot.initrd.kernelModules = [ ]; | |||
boot.kernelModules = [ ]; | |||
boot.extraModulePackages = [ ]; | |||
swapDevices = [ ]; | |||
networking.useDHCP = lib.mkDefault true; | |||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; | |||
} | |||
</syntaxhighlight><syntaxhighlight lang="nix"> | |||
# /tmp/my-first-flake/my-systems/my-hetzner-vm/disko-config.nix | |||
{ | |||
disko.devices = { | |||
disk = { | |||
main = { | |||
type = "disk"; | |||
device = "/dev/sda"; | |||
content = { | |||
type = "gpt"; | |||
partitions = { | |||
boot = { | |||
size = "1M"; | |||
type = "EF02"; | |||
priority = 1; | |||
}; | |||
ESP = { | |||
size = "512M"; | |||
type = "EF00"; | |||
content = { | |||
type = "filesystem"; | |||
format = "vfat"; | |||
mountpoint = "/boot"; | |||
}; | |||
}; | |||
root = { | |||
size = "100%"; | |||
content = { | |||
type = "filesystem"; | |||
format = "ext4"; | |||
mountpoint = "/"; | |||
}; | |||
}; | |||
}; | |||
}; | |||
}; | |||
}; | |||
}; | |||
} | |||
</syntaxhighlight><syntaxhighlight lang="nix"> | |||
# /tmp/my-first-flake/my-systems/my-hetzner-vm/configuration.nix | |||
{ config, lib, pkgs, ... }: | |||
{ | |||
imports = | |||
[ | |||
./hardware-configuration.nix | |||
./disko-config.nix | |||
]; | |||
boot.loader.grub.enable = true; | |||
services.openssh.enable = true; | |||
users.users.eugene = { | |||
isNormalUser = true; | |||
extraGroups = [ "wheel" ]; | |||
initialHashedPassword = "$y$j9T$2DyEjQxPoIjTkt8zCoWl.0$3mHxH.fqkCgu53xa0vannyu4Cue3Q7xL4CrUhMxREKC"; # Password.123 | |||
}; | |||
programs.neovim = { | |||
enable = true; | |||
defaultEditor = true; | |||
configure = { | |||
customRC = '' | |||
colorscheme base16-ashes | |||
''; | |||
packages.packages = { | |||
start = [ | |||
pkgs.vimPlugins.nvim-base16 | |||
]; | |||
}; | |||
}; | |||
}; | |||
system.stateVersion = "24.05"; | |||
} | |||
</syntaxhighlight>'''Note''': the value of <code>initialHashedPassword</code> was obtained using <code>mkpasswd</code> command in Linux, and corresponds to <code>Password.123</code> string used as password.<syntaxhighlight lang="nix"> | |||
# /tmp/my-first-flake/flake.nix | |||
{ | |||
inputs = { | |||
nixpkgs = { | |||
url = "github:NixOS/nixpkgs/nixos-24.05"; | |||
}; | |||
disko = { | |||
url = "github:nix-community/disko"; | |||
inputs = { | |||
nixpkgs = { | |||
follows = "nixpkgs"; | |||
}; | |||
}; | |||
}; | |||
}; | |||
outputs = inputs@{ self, nixpkgs, ... }: { | |||
nixosConfigurations = { | |||
my-hetzner-vm = nixpkgs.lib.nixosSystem { | |||
system = "x86_64-linux"; | |||
modules = [ | |||
./my-systems/my-hetzner-vm/configuration.nix | |||
inputs.disko.nixosModules.disko | |||
]; | |||
}; | |||
}; | |||
}; | |||
} | |||
</syntaxhighlight>'''Note''': all these files constitute what's known as a ''nix [[flake]]''. The flake in question is small, though not exactly a minimal one. | |||
#Get the service IP address. Run:<syntaxhighlight lang="shell"> | |||
hcloud server ip my-hetzner-vm | |||
</syntaxhighlight> | |||
#Build NixOS from flake. Run:<syntaxhighlight lang="shell"> | |||
nix run --extra-experimental-features 'nix-command flakes' github:nix-community/nixos-anywhere -- --flake /tmp/my-first-flake#my-hetzner-vm nixos@0.0.0.0 --build-on-remote | |||
</syntaxhighlight>'''Note''': replace <code>0.0.0.0</code> with an IP address obtained during the previous step. | |||
#Detach ISO from VM. Run:<syntaxhighlight lang="shell"> | |||
hcloud server detach-iso my-hetzner-vm | |||
</syntaxhighlight> | |||
#Reboot VM. Run:<syntaxhighlight lang="shell"> | |||
hcloud server reboot my-hetzner-vm | |||
</syntaxhighlight> | |||
The NixOS on Hetzner is installed! Let's do a few more steps to customize the installation. | |||
#First, "forget" existing key fingerprint for the Hetzner host. Run:<syntaxhighlight lang="shell"> | |||
ssh-keygen -f /root/.ssh/known_hosts -R 0.0.0.0 | |||
</syntaxhighlight>'''Note''': again, here and below, replace <code>0.0.0.0</code> with an IP address obtained via <code>hcloud server ip my-hetzner-vm</code>. | |||
#Copy flake files onto the server. Run:<syntaxhighlight lang="shell"> | |||
scp -r /tmp/my-first-flake eugene@0.0.0.0:~/ | |||
</syntaxhighlight> | |||
#Using <code>neovim</code> editor on the VM, modify <code>configuration.nix</code> to include a package containing Elixir programming language runtime for <code>eugene</code> user. Run:<syntaxhighlight lang="shell"> | |||
nvim my-first-flake/my-vms/my-hetzner-vm/configuration.nix | |||
</syntaxhighlight>Edit the <code>configuration.nix</code> so that <code>users</code> block looks like this:<syntaxhighlight lang="shell"> | |||
# ~/my-first-flake/my-vms/my-hetzner-vm/configuration.nix | |||
# ... | |||
users.users.eugene = { | |||
isNormalUser = true; | |||
extraGroups = [ "wheel" ]; | |||
initialHashedPassword = "$y$j9T$2DyEjQxPoIjTkt8zCoWl.0$3mHxH.fqkCgu53xa0vannyu4Cue3Q7xL4CrUhMxREKC"; # Password.123 | |||
packages = [ | |||
pkgs.beam.packages.erlang_26.elixir_1_16 | |||
]; | |||
}; | |||
# ... | |||
</syntaxhighlight> | |||
#Re-build NixOS. Run:<syntaxhighlight lang="shell"> | |||
sudo nixos-rebuild switch --flake ./my-first-flake#my-hetzner-vm | |||
</syntaxhighlight> | |||
=== disko === | === disko === |