Guix on DigitalOcean

Posted on

Tags: Guix, DigitalOcean

I recently tried GNU Guix on a DigitalOcean VM. I used a Ubuntu GNU/Linux 19.09 VM (with 4GB RAM) to test it, as bootstrapping store requires around ~2GB of tmpfs space (usually half of the available RAM) to hold the bootstrapped store, with some workaround I believe one can do it in smaller VM as well. To install it follow the steps:

  1. Boot VM in recovery mode.

  2. Create the following script guix-cloud-config.sh on the host to build config.scm for the OS based on the VM metadata API:

#!/usr/bin/env bash
# guix-cloud-config.sh
# uses DigitalOcean metadata API to build the Guix configuration

set -e
set -u

JSON=$(mktemp)

wget -qO $JSON http://169.254.169.254/metadata/v1.json

HOSTNAME=$(jq .hostname $JSON)
PUBIP4=$(jq -r .interfaces.public[0].ipv4.ip_address $JSON)
PUBMASK4=$(jq -r .interfaces.public[0].ipv4.netmask $JSON)
PUBGW4=$(jq -r .interfaces.public[0].ipv4.gateway $JSON)
PUBIP6=$(jq -r .interfaces.public[0].ipv6.ip_address $JSON)
PUBMASK6=$(jq -r .interfaces.public[0].ipv6.cidr $JSON)
PUBGW6=$(jq -r .interfaces.public[0].ipv6.gateway $JSON)
DNS=$(jq -rc '.dns.nameservers[]' $JSON | xargs | sed -r -e 's/([^[:space:]]+)/"\1"/g')

keyid=0
PUBKEYS=$(jq '.public_keys[]' $JSON |while read key; do
    printf ",(plain-file \"key-%d\" %s)\n" $((keyid++)) "$key"
done)

cat <<EOF
(use-modules (gnu))
(use-service-modules networking ssh)
(use-package-modules screen certs ssh)

(operating-system
  (host-name $HOSTNAME)
  (timezone "Etc/UTC")
  (locale "en_US.utf8")

  (bootloader
     (bootloader-configuration
        (bootloader grub-bootloader)
        (target "/dev/vda")))

  (file-systems (append 
                  (list
                     (file-system
                          (device (file-system-label "cloudimg-rootfs"))
                          (mount-point "/")
                          (type "ext4"))
                     (file-system
                          (device (file-system-label "UEFI"))
                          (mount-point "/boot/efi")
                          (type "vfat")))
                  %base-file-systems))

  (users (cons (user-account
                 (name "guix-luser")
                 (comment "Guix luser")
                 (group "users")
                 (supplementary-groups '("wheel")))
               %base-user-accounts))

  (packages (append
              (list screen openssh nss-certs)
              %base-packages))

  (services (append
              (list
        (static-networking-service "eth0" "$PUBIP4"
                   #:netmask "$PUBMASK4"
                   #:gateway "$PUBGW4"
                   #:name-servers '($DNS))
                (service openssh-service-type
                  (openssh-configuration
                    (permit-root-login 'without-password)
                    (authorized-keys
               \`(("root" $PUBKEYS)
                  ("guix-user" $PUBKEYS)))))) 
        %base-services)))
EOF

rm -f $JSON
  1. Download the package signer’s GPG key.
    # wget https://sv.gnu.org/people/viewgpg.php?user_id=15145 -qO - | gpg --import -
  1. Download installer script.
    # wget https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh
  1. Run installer script:
    # ./guix-install.sh
  1. Install glibc-utf8-locales package, and setup UTF-8 locale:
    # guix package -i glibc-utf8-locales
    # export GUIX_LOCPATH="$HOME/.guix-profile/lib/locale" LC_ALL=en_US.UTF8 LANG=en_US
  1. Purge the Ubuntu OS:
    # mkfs.ext4 -L cloudimg-rootfs /dev/vda1
  1. Mount the filesystem:
    # mount -o noatime /dev/vda1 /mnt/
  1. Generate the basic configuration:
    # mkdir /mnt/etc
    # bash ./guix-cloud-config.sh >/mnt/etc/config.scm
  1. Verify the contents to make sure it is as expected, or make any changes:

  2. Now perform the installation on /mnt:

    # guix system init /mnt/etc/config.scm /mnt
  1. Power off the host, and switch to HDD booting. You should be able to SSH in to the host with root or guix-luser (although need to set password to be able to use sudo).

Happy Guix-ing :)