Deploying Guix images on Linode

Lobsters Hottest Tools

Summary

This article provides a technical guide on deploying Guix system images directly on Linode cloud servers, including code snippets for system configuration.

<p><a href="https://lobste.rs/s/mf2lic/deploying_guix_images_on_linode">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 09/27/26, 01:31 AM

# Deploying Guix images on Linode — dthompson Source: [https://dthompson.us/posts/deploying-guix-images-on-linode.html](https://dthompson.us/posts/deploying-guix-images-on-linode.html) Today, I find myself migrating from Digital Ocean to Linode \(now Akamai Cloud but I’m never gonna*really*call it that\) because, among other things, Digital Ocean recently[donated $3 million USD to Omarchy](https://www.digitalocean.com/blog/digitalocean-joins-omacom-foundation), a slop distro for fascists\. Raising money for free and open source software development is hard so it’s*pretty cool*when some guy[who doesn’t even write code anymore](https://jardo.dev/what-about-rails)gets dump trucks of money for nothing while honest projects compete for peanuts from small grant funding organizations\. But anyway\! The Guix cookbook has[some documentation about running Guix on Linode](https://guix.gnu.org/cookbook/en/html_node/Running-Guix-on-a-Linode-Server.html)\. It takes an approach where you start with one of Linode’s built\-in Debian \([RIP](https://toot.cat/@dthompson/117322463609022181)\) images and then convert it to a Guix system\. As a former devops guy, I found this unsatisfying\. What I would really like is to upload a Guix image that is ready to go for use on Linode\. I’m gonna save the story and just say: I figured it out\. My Linode disk image does the following: - Allows use of virtual disks in the initial RAM disk - Mounts the correct devices for`/`and swap - Resizes the root file system upon boot to use all the space in the underlying Linode volume \(needed a custom service for this as Guix’s own`resize\-file\-system\-service`didn’t work for this\) - Starts an SSH server that recognizes my public key - Allows passwordless`sudo`so I can use`guix deploy`later - Installs the bootloader files somewhere that Linode will recognize - Gets an IPv6 address over DHCP in addition to its IPv4 address Here’s the code I ended up with: ``` (define-module (dthompson linode) #:use-module (gnu) #:use-module (gnu packages linux) #:use-module (gnu packages ssh) #:use-module (gnu services) #:use-module (gnu services admin) #:use-module (gnu services networking) #:use-module (gnu services shepherd) #:use-module (gnu services ssh) #:use-module (gnu system linux-initrd) #:use-module (guix gexp) #:use-module (guix modules) #:use-module (guix profiles) #:use-module (nongnu packages linux) #:export (%linode-base-services linode-base-os)) (define ssh-authorized-keys `(("dave" ,(local-file "../keys/dave.pub")))) (define guix-signing-keys (list (local-file "../keys/signing-key.pub"))) ;; Ideally we'd just use Guix's resize-file-system-service but it ;; makes some assumptions that do not hold for our Linode setup. (define (resize2fs-shepherd-service device) (list (shepherd-service (provision '(resize2fs)) (requirement '(user-processes)) (one-shot? #f) (respawn? #f) (start (with-imported-modules (source-module-closure '((guix build utils))) #~(lambda _ (invoke #$(file-append e2fsprogs "/sbin/resize2fs") #$device)))) (documentation "Resize ext filesystem on boot.")))) (define resize2fs-service-type (service-type (name 'resize2fs) (extensions (list (service-extension shepherd-root-service-type resize2fs-shepherd-service))) (default-value #f) (description "Resize ext filesystem on boot"))) (define %linode-base-services (cons* (service dhcpcd-service-type (dhcpcd-configuration ;; Linode servers get their IPv6 address via SLAAC and ;; the default setting of "private" doesn't work. (slaac "hwaddr"))) ;; SSH access via public/private key pairs only. (service openssh-service-type (openssh-configuration (password-authentication? #f) (authorized-keys ssh-authorized-keys))) ;; Automatically resize root filesystem to take up entire allocated ;; space. (service resize2fs-service-type "/dev/sda") ;; Firewall that blocks nearly everything by default. (service nftables-service-type) (modify-services %base-services ;; Allow other Guix machines to push store items over via ;; 'guix deploy'. (guix-service-type config => (guix-configuration (inherit config) (authorized-keys (append guix-signing-keys %default-authorized-guix-keys))))))) (define linode-base-os (operating-system (locale "en_US.utf8") (timezone "America/New_York") (host-name "linode") (users (cons (user-account (name "dave") (comment "David Thompson") (group "users") (home-directory "/home/dave") (supplementary-groups '("wheel"))) %base-user-accounts)) (sudoers-file (plain-file "sudoers" (string-append (plain-file-content %sudoers-specification) ;; 'guix deploy' requires no password ;; sudo capability. "%wheel ALL=NOPASSWD: ALL\n"))) (packages (cons openssh %base-packages)) (services %linode-base-services) ;; Need virtio_scsi for using virtual disk devices. (initrd-modules (append '("virtio_scsi") (base-initrd-modules linux))) ;; Guix's default behavior of running grub-install on the boot ;; device doesn't work in the Linode environment. Instead, we ;; just do what Guix does for disk images: Install the font and ;; GRUB modules to the root file system. (bootloader (bootloader-configuration (bootloader (bootloader (inherit grub-bootloader) (installer #~(lambda (bootloader device mount-point) (let* ((install-dir (string-append mount-point "/boot")) (fonts (string-append install-dir "/grub/fonts"))) (mkdir-p fonts) (copy-file (string-append bootloader "/share/grub/unicode.pf2") (string-append fonts "/unicode.pf2")) (copy-recursively (string-append bootloader "/lib/") install-dir)))))) (targets '("/dev/sda")))) (file-systems (cons (file-system (device "/dev/sda") (mount-point "/") (type "ext4")) %base-file-systems)) (swap-devices (list (swap-space (target "/dev/sdb")))))) ;; Allow for building an initial disk image with 'guix system image'. (when (batch-mode?) linode-base-os) ``` This code can also be found in[this Git repository](https://git.dthompson.us/guix-config/tree/dthompson/linode.scm)\. Now this OS configuration needs to be turned into a usable disk image\. Guix can produce Linode\-compatible images using the`mbr\-raw`image type\. Linode images need to be gzip compressed before uploading\. I wrote a script to handle it: ``` #!/bin/sh set -e # First, we need to use Guix to generate a raw disk image that we can # upload to Linode. Guix's 'mbr-raw' image type gets us most of the # way there, but not quite. As stated in the name, these images have # a master boot record. They also have GRUB installed in a post-MBR # gap. After much trial and error we've discovered that what we want # for Linode is an image that *only* contains the root partition. image=$(guix system image -L . --image-type=mbr-raw dthompson/linode.scm) # Guix places the root partition 1048576 bytes away from the beginning # of the disk, so we need to skip over all of that when producing the # final image. # # Somewhat arbitrarily, a 128K block size for 'dd' was chosen to speed # up the operation vs. the default of 512 bytes. 8 blocks of 128K = # 1048576 bytes, hence the 'skip=8' flag. # # Linode also requires uploaded images to be compressed with gzip. dd if="$image" bs=128K skip=8 conv=sync,noerror | gzip -c > linode-base.gz ``` Once the disk image is created and uploaded, I can launch a new Linode instance using it\.*However*, Linode’s default configuration profile doesn’t work and the new instance simply kernel panics\. First, I stop the server\. Then I edit its configuration profile and make the following changes: - Open the “Select a kernel” dropdown under “Boot Settings” and choose “GRUB 2” - Turn off*all*of the filesystem/boot helper switches Then I save and boot the server\. At this point I can use`guix deploy`over SSH for all OS updates\. And that’s it\! It would probably be a good idea to update the Guix Cookbook with this improved process but I don’t have the energy for that right now so hopefully this blog post is helpful in the meanwhile\.

Similar Articles

Switching to GNU Guix: A Beginner's Perspective

Lobsters Hottest

A beginner shares their experience switching from Arch Linux to GNU Guix, discussing the benefits of declarative system management, comparisons with NixOS, and challenges faced.

How I like to install NixOS (declaratively)

Michael Stapelberg

A guide on declaratively installing NixOS over the network using tools like nixos-anywhere, with an emphasis on managing configuration files under version control.