Reprovisioning Guix on a new machine

2023/03/15

No matter how often you backup, if you never test your recovery procedures - you’d be surprised when it comes to it. So let’s make a recovery plan and give it a test.

Back up user files

Having configured restic-backup-service, it’s just one command:

restic-guix backup storage

Disaster happens

I see two options:

  1. I just need to move my digital stuff to a new machine.
  2. I’ve lost access to my current machine.

Let’s follow the first option for now and prepare everything to migrate smoothly.

Make a raw disk image

That’s as simple as running guix system image. The only trick is to add a couple more services to the operating-system.

(use-modules (gnu)
             (gnu system)
             (personal packages binary)
             (gnu services admin)
             (gnu services guix)
             (gnu services shepherd)
             (ice-9 textual-ports))

(define system (load "./system.scm"))
(define root-fs
  (first (filter (lambda (fs)
                   (string=? "/" (file-system-mount-point fs)))
                 (operating-system-file-systems system))))

(operating-system
  (inherit system)
  (services
   (cons*
    ;; auto-resize fs on boot
    (service resize-file-system-service-type
             (resize-file-system-configuration
              (file-system root-fs)))

    (simple-service 'activate-storage
                    activation-service-type
                    #~(begin
                        (use-modules (guix build utils))
                        (let ((dir "/storage")
                              (%user (getpw "sarg")))
                          (mkdir-p dir)
                          (chown dir (passwd:uid %user) (passwd:gid %user))
                          (chmod dir #o700))))

    ;; restore /storage
    (simple-service
     'restore-storage-from-backup shepherd-root-service-type
     (list (shepherd-service
            (requirement '(user-processes file-systems file-system-/media/500GB))
            (provision '(restore-storage))
            (one-shot? #t)
            (start #~(lambda _ (invoke #$(file-append restic "/bin/restic")
                                       "-r" "/media/500GB/restic"
                                       "-p" "/media/500GB/restic/pass"
                                       "restore" "latest")))
            (stop #f))))

    ;; install user profile
    (service guix-home-service-type
             `(("sarg" ,(load "./home.scm"))))

    (operating-system-user-services system))))
Code Snippet 1: system-image.scm
guix system image --volatile --image-type=efi-raw system-image.scm
The following derivations will be built:
  /gnu/store/q0gvw0s79wkjlanfq4yjjyf2f9ddk4iq-disk-image.drv
  /gnu/store/dp0p01bzgb9aarzx040iv7k5w1r7z5xx-genimage.cfg.drv
  /gnu/store/kr39hrsyvwp17ny5hcrj6ynfn56i2cq8-partition.img.drv
  /gnu/store/758yrvd3m7016i5vflzjyg9232j75k2r-system.drv
  /gnu/store/58xsyrsaqaqh3dsxzb7zc0h72v7ha3as-etc.drv
  /gnu/store/5kifgf0yqnki7k9wg0a8jlviqibirmxy-activate.scm.drv
  /gnu/store/rqyl2kcj5q84daw4qriza38l0vkrax0m-activate-service.scm.drv
  /gnu/store/5lnmmgrgpy58nnkcpy8z22a0kf54yv0k-boot.drv
  /gnu/store/ri65rz6rgh5kv6ff8g2q313fr9fjd5ac-provenance.drv
  /gnu/store/z62g9qwy9k65awaaxg2f3jzdadsk5iha-grub.cfg.drv
  /gnu/store/x1zyd14c5xnpd51ql6bcfdv0q06nla8a-partition.img.drv

building /gnu/store/ri65rz6rgh5kv6ff8g2q313fr9fjd5ac-provenance.drv...
building /gnu/store/58xsyrsaqaqh3dsxzb7zc0h72v7ha3as-etc.drv...
building /gnu/store/rqyl2kcj5q84daw4qriza38l0vkrax0m-activate-service.scm.drv...
building /gnu/store/5kifgf0yqnki7k9wg0a8jlviqibirmxy-activate.scm.drv...
building /gnu/store/5lnmmgrgpy58nnkcpy8z22a0kf54yv0k-boot.drv...
building /gnu/store/758yrvd3m7016i5vflzjyg9232j75k2r-system.drv...
building /gnu/store/z62g9qwy9k65awaaxg2f3jzdadsk5iha-grub.cfg.drv...
building /gnu/store/kr39hrsyvwp17ny5hcrj6ynfn56i2cq8-partition.img.drv...
building /gnu/store/x1zyd14c5xnpd51ql6bcfdv0q06nla8a-partition.img.drv...
building /gnu/store/dp0p01bzgb9aarzx040iv7k5w1r7z5xx-genimage.cfg.drv...
building /gnu/store/q0gvw0s79wkjlanfq4yjjyf2f9ddk4iq-disk-image.drv...

And the disk image is: /gnu/store/rj2i98lw7vjj0k48dbwfs0q9aqnd9nxk-disk-image

Let’s check its partition table:

fdisk -l $disk_image
Disk /gnu/store/rj2i98lw7vjj0k48dbwfs0q9aqnd9nxk-disk-image: 2.32 GiB, 2488737792 bytes, 4860816 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 389B45C2-0EC7-42EC-9D96-49B40F820311

Device                                                  Start     End Sectors  Size Type
/gnu/store/rj2i98lw7vjj0k48dbwfs0q9aqnd9nxk-disk-image1  2048   83967   81920   40M EFI System
/gnu/store/rj2i98lw7vjj0k48dbwfs0q9aqnd9nxk-disk-image2 83968 4860775 4776808  2.3G Linux filesystem

And filesystems labels are:

alias kpartx=$(guix build multipath-tools)/sbin/kpartx
kpartx -av $disk_image >/dev/null
lsblk -o NAME,FSTYPE,FSVER,LABEL,UUID /dev/loop0
kpartx -d $disk_image
NAME      FSTYPE FSVER LABEL      UUID
loop0
|-loop0p1 vfat   FAT16 GNU-ESP    572A-DDF3
`-loop0p2 ext4   1.0   Guix_image f6bc8ad8-ff1a-1754-eb2b-35d0f6bc8ad8

Note that the partition labels chosen by guix system image are not taken from the operating-system definition. They’re hardcoded in the guix sources and therefore I have to hardcode the same values in my operating-system. Without matching labels the system would just refuse to boot as they’re used in the EFI loader to find the boot partition and also in the fstab.

LABEL=Guix_image  /      ext4  defaults
LABEL=GNU-ESP     /boot  vfat  defaults

Putting disk image on a real machine

I have a Ventoy USB stick with Lubuntu Live CD on it. Let’s just copy the resulting image to the stick. Note, you might need to follow this recipe: Access USB stick on live ISO as by default the USB drive will not be accessible.

eval $(lsblk -P -o LABEL,MOUNTPOINT | grep Ventoy)
cp $disk_image $MOUNTPOINT/guixdisk.img
eject $MOUNTPOINT

And then reboot to the Live CD environment and install the image.

dd /media/lubuntu/Ventoy/guixdisk.img of=/dev/sda status=progress
reboot

Revitalising Guix

One-off shepherd services added to operating-system earlier take care of restoring /storage from backup and activate the user home profile. Check that everything worked as expected examining /var/log/messages.

echo "Wait guix-home-sarg to complete"
until herd status guix-home-sarg | grep 'It is stopped'; do echo -n .; sleep 1; done

passwd root
passwd sarg
login sarg

# app-specific restore commands
doom sync
mu init -m ~/.mail
mu index

logout # sarg
logout # root