Skip to content

Instantly share code, notes, and snippets.

@kristianlm
Last active August 26, 2022 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kristianlm/089a6759a74dcd2e6f702847cf919ed2 to your computer and use it in GitHub Desktop.
Save kristianlm/089a6759a74dcd2e6f702847cf919ed2 to your computer and use it in GitHub Desktop.

Guix on Hetzner Cloud

Based on this guide and this blog-post, I decided I wanted to try "deploying" GNU Guix on a Hetzner Cloud Server (aka VPS).

It seems the only requirements for Hetzner guest images are:

  • virtio_* drivers
  • Plain non-EFI grub
  • dhcp

guix system image

The process was surprisingly straight-forward! The only necessary step was really to copy the disk image from guix system image hetzner.scm over to the VPS's /dev/sda.

First, a little background. You can use guix system vm ... to launch a VM on your host machine, but Guix also has a command to produce a complete disk image: guix system image .... It's not overly complicated to launch this manually with qemu. Here's how you might do that:

I think if=virtio only works since we have added the virtio_scsi initrd-module to our guest system (this is used by Hetzner and probably all VPS providers for performance reasons. If your guest doesn't have that virtio disk driver, you can just omit if=virtio and go for qemu's default at a small price in disk IO performance.

klm@pal ~$ disk=$(guix system image ~/src/guix/gnu/system/examples/hetzner.scm)
klm@pal ~$ file $disk
/gnu/store/19s26kg73vag3smmlfhw88q6pgs39k8h-disk-image: DOS/MBR boot sector
klm@pal ~$ qemu-system-x86_64 --enable-kvm -m 512M -drive file=$disk,format=raw,if=virtio -snapshot

image

So that's a GNU Guix operating system created for us and running in a qemu instance locally. Now, in order to get this working on Hetzner, we're simply going to overwrite the existing VPS disk image with our newly created $disk.

Hetzner Rescue Mode

I was happy to find this was relatively easy to do using Hetzner's "rescue mode". Navigate to the "RESCUE" tab in your web Cloud Console, and click "ENABLE RESCUE & POWER CYCLE". The reason we're using this mode is that we can't really overwrite /dev/sda which its partitions are mounted or otherwise in use. The rescue boot does not use /dev/sda.

enable rescure and power cycle

This should promt you for a ssh public key. Enter your suitable settings and hit "ENABLE RESCUE & POWER CYCLE":

rescue confirm

From here, you can use the web-console to make sure you rescue system is up and running. Click the "Console" button, located at the top-right. Once the "rescue boot" has completed, it should look like this:

rescue web console

Next, we're going to send our raw disk image over ssh and dump it onto /dev/sda. Let's check that our rescue mode ssh is up and running:

klm@pal ~$ ssh -o 'StrictHostKeyChecking no' root@167.235.141.165 lsblk
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
....
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
loop0    7:0    0  3.1G  1 loop
sda      8:0    0 19.1G  0 disk
├─sda1   8:1    0   40M  0 part
└─sda2   8:2    0  1.5G  0 part
sr0     11:0    1 1024M  0 rom

You'll obviously have to change the IP to your own. Also, I'm using StringHostKeyChecking no to skip testing of the remove host keys. These are generated each time we boot into rescue mode, and running ssh-keygen -R 167.235.141.165 became a bit of a hassle as I was doing all this repeatedly.

As you can see, I went for the smalles VPS Hetzner has with 20GB SDD. Still plenty for our 1.5G minimal Guix system image. And now for the grand finale!

This next command will delete everything on your VPS disk! Take backups!

klm@pal ~$ pv $disk | ssh -o 'StrictHostKeyChecking no' root@167.235.141.165 dd bs=1MiB of=/dev/sda
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
...
1.56GiB 0:00:22 [70.0MiB/s] [==========================================================>] 100%
0+50755 records in
0+50755 records out
1671286784 bytes (1.7 GB, 1.6 GiB) copied, 20.5225 s, 81.4 MB/s

Using pv instead of just cat gives us nice progress bar. Now, let's reboot into our new Guix system!

klm@pal ~$ ssh -o 'StrictHostKeyChecking no' root@167.235.141.165 reboot

Great Success!

success grub

We're in! Very exciting. Now, since the new guest system will have permanent SSH host keys, we'll want to keep them. So we delete the existing ones, if there are any, and accept them on first connection.

klm@pal ~$ ssh-keygen -R 167.235.141.165
klm@pal ~$ ssh root@167.235.141.165
The authenticity of host '167.235.141.165 (167.235.141.165)' can't be established.
ED25519 key fingerprint is SHA256:q9+ik7EQOog8ZQhYHTNYUEuEoS4wgc25lrLHgnvaCyk.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '167.235.141.165' (ED25519) to the list of known hosts.
root@my-guix-server ~# guix system describe
Generation 1	Aug 25 2022 21:29:34	(current)
  file name: /var/guix/profiles/system-1-link
  canonical file name: /gnu/store/br6hwhlrb575y0i72wh9y63xxvy7mpr6-system
  label: GNU with Linux-Libre 5.18.16
  bootloader: grub
  root device: UUID: 38af4c98-eb55-2b70-ad36-132e38af4c98
  kernel: /gnu/store/iz6xn1b1dyk6pwaf6dym3jm3vwnh4gz9-linux-libre-5.18.16/bzImage

From here, there are probably a number of thing's you'll want to do.

  • Resize /dev/sda2 to fill the entire VPS disk space.
  • Tweak hetzner.scm to your liking.

Also, it's be nice if this could be integrated with guix deploy for automation, but I haven't looked into that.

;;; I named this "hetzner", but the only VPS-specific feature is
;;; probably the virtio_scsi initrd module addition.
;;;
;;;
;;; OBS: change id_ed25519.pub below to id_rsa.pub unless you've used my favorite key algorithm:
;;;
;;; ssh-keygen -t ed25519
;;;
(use-modules (gnu)
(gnu packages ssh))
(use-service-modules networking ssh)
(operating-system
(host-name "my-guix-server")
(timezone "Etc/UTC")
(locale "en_US.UTF-8")
(bootloader (bootloader-configuration
(bootloader grub-minimal-bootloader)
(targets '("/dev/sda"))
(terminal-outputs '(console))))
;; Note that virtio_net and friends are already in %base-initrd-modules
(initrd-modules (cons* "virtio_scsi" ;; Needed for /dev/sda
%base-initrd-modules))
(file-systems (cons* (file-system
(mount-point "/")
(device "/dev/sda2")
(type "ext4"))
%base-file-systems))
(services
(append
(list
(service dhcp-client-service-type)
(service openssh-service-type
(openssh-configuration
(openssh openssh-sans-x)
(password-authentication? #f)
(authorized-keys
`(("root" ,(local-file (string-append (getenv "HOME") "/.ssh/id_ed25519.pub")))))
(permit-root-login 'prohibit-password))))
%base-services)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment