{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreieupxjiumvsnqyurw5lawkyndv7t6pjjkemvowlt5q6vqh47azg7a",
    "uri": "at://did:plc:uzai4vzxm37suvnjiwfzi63w/app.bsky.feed.post/3mexkghkttyw2"
  },
  "path": "/posts/guix-config.html",
  "publishedAt": "2026-05-18T04:52:35.054Z",
  "site": "https://www.terracrypt.net",
  "tags": [
    "here"
  ],
  "textContent": "I've been meaning to write up a post on how I manage my Guix System configurations for a while, because I've hit on a solution that feels kinda nice, inspired by how folks do things in NixOS.\n\nIn the NixOS world, there's a tool called `nixos-generate-config` that automatically creates both a `configuration.nix` and a `hardware-configuration.nix` for you. The idea is that the hardware-specific bits (like your partition layout, initrd modules, and such) go into `hardware-configuration.nix`, and the rest of your configuration goes into `configuration.nix`. We don't have an equivalent of `nixos-generate-config` in the Guix world to help automate this, but the general pattern seemed pretty nice, so I've roughly replicated it for my Guix config.\n\nIt ends up feeling a little messier in Guix than in Nix, because of some differences in how we build up our configs. But it's not _too_ bad. There's a couple different layers here, so let's start with my `config.scm` for one of my machines. I'm omitting a few things for brevity but you can see the full file contents here if you're curious.\n\n\n    (define-module (system machines wired config) ... #:use-module (system machines wired hardware-configuration)) (define-public wired-operating-system (operating-system (inherit hardware-configuration) (locale \"en_US.utf8\") (timezone \"America/New_York\") (host-name \"wired\") (users (cons* (user-account ...) (operating-system-users hardware-configuration))) (packages (append (list luanti-server minetest-game ...) (operating-system-packages hardware-configuration)))))\n\nThis defines the `operating-system` that you would actually use in, for example, a `guix system reconfigure`. A few things to note about this:\n\n  * We're inheriting a lot of this config from something called `hardware-configuration`, defined elsewhere.\n  * In a few cases (`users` and `packages`), we're extending things that were defined in `hardware-configuration`. One of the things I use this machine for is hosting a Luanti server, so I need Luanti installed on it - but of course that's not something I need on all my servers! If you can't already tell from this, `hardware-configuration` is itself an `operating-system` record, so we can pull out each of its fields via procedures like `operating-system-packages`. If I just defined the list of packages in this file and didn't append to the packages from `hardware-configuration`, I would overwrite the list - which isn't what I want, since I have a base set of packages elsewhere that I'd like to keep.\n\n\n\nOkay, let's take a look at `hardware-configuration`:\n\n\n    (define-module (system machines wired hardware-configuration) #:use-module (gnu) #:use-module (nongnu packages linux) #:use-module (system machines server-base)) (define-public hardware-configuration (operating-system (inherit jfred-server-base-system) (kernel linux) (firmware (list linux-firmware)) (keyboard-layout (keyboard-layout \"us\")) (bootloader (bootloader-configuration (bootloader grub-efi-bootloader) (targets (list \"/boot/efi\")) (keyboard-layout keyboard-layout))) (mapped-devices (list (mapped-device (source (uuid \"a1775b70-cfc4-44d5-9fc3-0ddaff9eb694\")) (target \"cryptroot\") (type luks-device-mapping)))) (file-systems (cons* (file-system (mount-point \"/boot/efi\") (device (uuid \"AF86-B19B\" 'fat32)) (type \"vfat\")) (file-system (mount-point \"/\") (device \"/dev/mapper/cryptroot\") (type \"ext4\") (dependencies mapped-devices)) %base-file-systems)) (host-name #f)))\n\nThis is where all my disk layout stuff lives, and it's where I keep any hardware-specific quirks. For example, this machine is an Intel NUC, so I need some proprietary firmware for all the hardware to work properly. So I use `linux-firmware` and the `linux` kernel from Nonguix here.\n\nHow exactly to split up the config between files is a bit of a judgement call, because not everything feels like it cleanly falls into one camp or the other. I moved things around a few times as I was putting this together.\n\nYou might have also noticed that the `hardware-configuration` itself inherits from another config (`jfred-server-base-system`). Yeah, I have multiple layers of inheritance here! Here's that definition:\n\n\n    (define-module (system machines server-base) ... #:use-module (system machines base)) (define-public jfred-server-base-system (operating-system ;; Set these in hardware-configuration.scm (bootloader #f) (host-name #f) (file-systems #f) (sudoers-file (plain-file \"sudoers\" (string-append (plain-file-content %sudoers-specification) (format #f \"~a ALL = NOPASSWD: ALL~%\" \"jfred\")))) ;; Base configs for my servers (locale \"en_US.utf8\") (keyboard-layout (keyboard-layout \"us\")) (timezone \"America/New_York\") (users (cons* (user-account (name \"jfred\") (comment \"Jonathan Frederickson\") (group \"users\") (home-directory \"/home/jfred\") (supplementary-groups '(\"wheel\" \"netdev\" \"audio\" \"video\"))) %base-user-accounts)) (packages %base-packages) (services (append (list (service openssh-service-type (openssh-configuration (use-pam? #f))) (service elogind-service-type) (service dhcpcd-service-type) (service dbus-root-service-type) (service docker-service-type) (service containerd-service-type) (service ntp-service-type)) (jfred-append-base-services %base-services)))))\n\nThis is where I keep configuration that should apply to _all_ of my servers, rather than just one. I always want to have a user for myself in sudoers on each of them, for it to be in my local timezone, and for it to have the `us` keyboard layout. And there's a base set of services that I'll almost certainly want on any server I'm running.\n\nThis is the part of my config that feels the messiest to me. You might notice the fields set to `#f` at the top there; that's because it's an error to have an `operating-system` record that does not define all the fields. I'll always need to set the bootloader, filesystems, and hostname for any new machine anyway, so in practice it's not an issue - it just feels a bit weird.\n\nAnd yes, there's yet another layer of composition with `jfred-append-base-services` at the end there. But I think this is where I'm going to stop. :)\n\nI hope this post is helpful to someone in the future! And if you have any other ideas, I'd love to hear about them.",
  "title": "How I manage my Guix System configs",
  "updatedAt": "2026-02-15T13:41:00.000Z"
}