Guix in a Linux Container

GNU Guix homepage
GNU Guix homepage

GNU Guix (as in geeks) is one of the new type of Linux distributions that focuses on reproducibility. It’s in the class of systems that can allow for immutability, in addition to atomic updates and root less user package management. NixOS is another similar new type in this class of reproducible operating systems.

Placing a Linux distribution into a container is a good way to learn about the underlying structure before installing it onto a real device. This The last time was NixOS in a Linux container. let’s play around with Guix.

Commands

The commands in this post are for running on Debian but it’s possible on any Linux distribution via the Guix shell installer script. Debian makes this easy because it has a guix package in its repository. It may be necessary to prefix sudo to the following commands for root privileges.

shell
apt install guix

There are other packages that might be required for guix to work correctly.

shell
apt install netbase gpg xz-utils ca-certificates

Guix needs a daemon to build and answer different requests. Running it without root is possible (from a container point of view). Start the daemon if it’s not running.

text
guix-daemon &

From there guix is now installed. Running guix pull brings in Running the pull subcommand is an important step — it’s best to be in sync with upstream at least once before starting anything configuration related. sources and upgrades guix to the latest version.

shell
guix pull

The version string should reflect the current git repository commit at the time of pulling. In this post the guix version is the short commit identifier 3ab983d.

shell
$ guix --version
guix (GNU Guix) 3ab983d630a95a29b9418b1ba8a26e5ca2836ec0

If the version string of guix isn’t up to date then check that the shell has properly sourced the guix specific environment variables like PATH from the home folder.

shell
. /etc/profile.d/guix.sh

In other instances, it might be necessary to hash or update the new location of guix using the built–in shell command hash.

shell
hash guix

To view the exact revision the system is running use the describe subcommand.

text
$ guix describe
Generation 1 Apr 11 2023 22:06:05 (current)
  guix 3ab983d
    repository URL: https://git.savannah.gnu.org/git/guix.git
    branch: master
    commit: 3ab983d630a95a29b9418b1ba8a26e5ca2836ec0

Searching for programs to install locally on Guix is powerful. There’s also a portal for previewing the available packages online.

Overview

The Guix configuration system works from a declared file that represents the desired state of the machine. That file, let’s say config.scm needs to describe a configuration in Guile Scheme that returns an operating-system.

scheme
(use-modules (gnu))
(operating-system)
config.scm

Guix already comes with a system subcommand that allows creating varied formats from a defined configuration for containers, images, and virtual machines (vm).

shell
guix system container config.scm
guix system image --image-type=tarball config.scm
guix system vm config.scm
Generating containers, images, and virtual machines with guix

The list below shows the various presets for different image output format types.

shell
$ guix system image --list-image-types
The available image types are:
   - rock64-raw
   - pinebook-pro-raw
   - pine64-raw
   - novena-raw
   - hurd-raw
   - hurd-qcow2
   - efi32-raw
   - qcow2
   - iso9660
   - wsl2
   - docker
   - uncompressed-iso9660
   - tarball
   - efi-raw
   - raw-with-offset
The various config.scm image output types

All of these work great and are extremely useful. In my case however, I’ll explore a bit further for a file system that can be plugged into a few more places like a LXC (Linux Containers). A brief look around the net seems to suggest that there are multiple approaches.

But nothing beats source code right? The guix system container commands appear to run from the gnu/system/linux-container.scm module definition. I’m a scheme neophyte. the source further reveals a gnu/system/examples folder that lists different configuration templates including a docker-image.tmpl and a vm-image.tmpl.

The linux-container.scm looks If there’s enough programming experience to make out the language constructs. If not — Lisp in Y minutes can help, maybe? Maybe not? enough to massage into a config.scm with a bit of guess work. Guix also has a %base-services list in gnu/services/base.scm that can work as a reference to build up a minimal service configuration. GNU Shepherd is the program that starts up the guix system.

Guix system init sets up a target directory with the needed files to start the OS (Operating System). A --dry-run tests the configuration file for correctness. The --no-bootloader argument is passed because well it’s not a real device — not yet at least.

shell
mkdir filesystem
guix system --dry-run init --no-bootloader config.scm filesystem/

Configuration

A setup more suited for a container is added to the operating-system GNU Emacs is apparently the editor best suited to wrangle anything Lisp–like. To indent everything press M-x or Alt-x, then type mark-whole-buffer and finally hit TAB. based on the previously mentioned linux-container.scm source file.

scheme
(use-modules (gnu) (gnu system locale))
(use-service-modules networking ssh)
(use-package-modules ssh bash package-management)

(operating-system
 (host-name "guix")
 (timezone "America/Nassau")
 (locale "en_US.utf8")
 (firmware `())
 (initrd-modules `())
 (kernel hello)
 (packages (list guix coreutils))

 (essential-services (modify-services
                      (operating-system-default-essential-services this-operating-system)
                      (delete firmware-service-type)
                      (delete (service-kind %linux-bare-metal-service))))

 (locale-definitions (list (locale-definition
                            (name "en_US.utf8")
                            (source "en_US")
                            (charset "UTF-8"))))

 (bootloader (bootloader-configuration
              (bootloader grub-bootloader)
              (targets '("/dev/null"))))

 (file-systems (list (file-system
                      (device "/dev/null")
                      (mount-point "/")
                      (type "dummy"))))

 (services (list (service dhcp-client-service-type)
                 (service syslog-service-type)
                 (service guix-service-type)
                 (service static-networking-service-type
                          (list %loopback-static-networking))
                 (service special-files-service-type
                          `(("/bin/sh" ,(file-append bash "/bin/sh"))
                            ("/usr/bin/env" ,(file-append coreutils "/bin/env"))))
                 (service udev-service-type
                          (udev-configuration
                           (rules '())))
                 (service openssh-service-type
                          (openssh-configuration
                           (openssh openssh-sans-x))))))
config.scm

Generate the files needed to boot the system by running the init subcommand without a --dry-run.

text
guix system init --no-bootloader config.scm filesystem/

One of the best ways to pass around file systems like guix with hard links and such is to use rsync.

shell
rsync -aAXHv filesystem/ rootfs
rsync --archive --acls --xattrs --hard-links --verbose filesystem/ rootfs

Container

The configuration below contains guix specific options that allow the container to start up.

ini
# Distribution configuration
lxc.arch = linux64
lxc.include = /usr/share/lxc/config/common.conf

# Network configuration
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.hwaddr = 00:16:3e:48:0d:f8

# Container configuration
lxc.uts.name = guix
lxc.environment = HOME=/root
lxc.environment = GUIX_NEW_SYSTEM=/gnu/store/n3yd660amqnhcmyv3qj2ghc4xhnfcj5z-system
lxc.rootfs.path = dir:/home/thedro/.local/share/lxc/guix/rootfs
lxc.init.cmd = /gnu/store/vbcdwklck7cd28j4jv9wchfw60abivfd-guile-3.0.9/bin/guile /gnu/store/hgi3i9ydjhi5bxnpz7zmzaqn60iw3ak1-boot
~/.local/share/lxc/guix/config

Guix will try to activate-modprobe and activate-firmware. If those activation paths (proc or sys) are exposed to the container and are read only, then the container may fail to start. The work around is to remove the activation scripts by modifying the list of essential-services in the operating-system as seen in the configuration from before.

In addition, the lxc.init.cmd has to be populated with the system environment variable (GUIX_NEW_SYSTEM), the boot script path, and the guile executable path needed to start the system. Setting up a special-files-service-type for an equivalent to /sbin/init is probably a better approach after the first boot, but for now manually locating the paths with the find command from the root directory works well.

shell
find . -maxdepth 3 -regex '.*system' -print -quit
find . -maxdepth 3 -regex '.*boot' -print -quit
find . -maxdepth 3 -regex '.*guile-3.*' -print -quit

Entering the container with lxc-attach may require sourcing /etc/profile to populate all of the guix specific environment variables.

shell
. /etc/profile

Comparisons

The GNU Guix system is inspired by NixOS and there are some initial comparisons that can be made between the two within the context of containers. In terms of file sizes for the containers, consider the following shaved down NixOS container configuration.

nix
{
  imports = [
    <nixpkgs/nixos/modules/profiles/headless.nix>
    <nixpkgs/nixos/modules/profiles/minimal.nix>
  ];

  disabledModules = [
    <nixpkgs/nixos/modules/profiles/all-hardware.nix>
    <nixpkgs/nixos/modules/profiles/base.nix>
  ];

  system.stateVersion = "22.11";
  nixpkgs.system = "x86_64-linux";
  networking.hostName = "nixos-container";
  environment.defaultPackages = [ ];

  boot.isContainer = true;
  xdg.mime.enable = false;
  xdg.icons.enable = false;
  xdg.sounds.enable = false;
  users.mutableUsers = false;
  documentation.enable = false;
  security.polkit.enable = false;
  fonts.fontconfig.enable = false;
  services.udisks2.enable = false;
  nixpkgs.config.allowUnfree = true;
}
A container configuration.nix for NixOS

At a quick glance, operations on Guix are slightly slower than NixOS but as seen from both configurations Guix is much easier to hack on and with naive attempts at minimization clocks in at a Not exactly definitive but you get the idea. size.

shell
$ du -h guix
834M guix/
shell
$ du -h nixos
1.1G nixos/

Guix also runs in an unprivileged container easily as shown in the video below but for NixOS it’s rather difficult to do.

Conclusion

That’s a wrap. And more preferably virtual machines. setups allow for experimenting with lots of configurations and software before going all in on real devices! On another note — here’s a quick overview and rundown of the Guix system.

Poking around the container to run the guix command and as a bonus neofetch
29 April 2023 — Written
2 May 2023 — Updated
Thedro Neely — Creator
guix-in-a-linux-container.md — Article

More Content

Openring

Web Ring

Comments

References

  1. https://thedroneely.com/git/
  2. https://thedroneely.com/
  3. https://thedroneely.com/posts/
  4. https://thedroneely.com/projects/
  5. https://thedroneely.com/about/
  6. https://thedroneely.com/contact/
  7. https://thedroneely.com/abstracts/
  8. https://ko-fi.com/thedroneely
  9. https://thedroneely.com/tags/linux/
  10. https://thedroneely.com/posts/guix-in-a-linux-container/#isso-thread
  11. https://thedroneely.com/posts/rss.xml
  12. https://thedroneely.com/images/guix-in-a-linux-container.png
  13. https://guix.gnu.org/
  14. https://en.wikipedia.org/wiki/Linux_distribution
  15. https://nixos.org/
  16. https://thedroneely.com/posts/running-nixos-linux-containers/
  17. https://thedroneely.com/posts/guix-in-a-linux-container/#commands
  18. https://www.debian.org/
  19. https://guix.gnu.org/manual/en/html_node/Binary-Installation.html
  20. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-a5a8373
  21. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-199ffb1
  22. https://hpc.guix.info/blog/2017/10/using-guix-without-being-root/
  23. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-c3adc34
  24. https://guix.gnu.org/manual/en/html_node/Invoking-guix-pull.html
  25. https://guix.gnu.org/manual/en/html_node/Upgrading-Guix.html
  26. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-f69e533
  27. https://git.savannah.gnu.org/cgit/guix.git/log/
  28. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-afc3e5b
  29. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-96de66e
  30. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-483f9db
  31. https://guix.gnu.org/manual/en/html_node/Invoking-guix-describe.html
  32. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-daa39cd
  33. https://thedroneely.com/posts/guix-in-a-linux-container/#overview
  34. https://guix.gnu.org/manual/en/html_node/Using-the-Configuration-System.html
  35. https://www.gnu.org/software/guile/
  36. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-c933e4e
  37. https://guix.gnu.org/manual/en/html_node/Invoking-guix-system.html
  38. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-3e3cc08
  39. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-328cb31
  40. https://linuxcontainers.org/lxc/introduction/
  41. https://guix-devel.gnu.narkive.com/CWhVGj4c/guixsd-in-lxd-container
  42. https://lists.gnu.org/archive/html/guix-devel/2016-12/msg00777.html
  43. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/system/linux-container.scm?id=b8152d668d16faa464d2819af6f8ed4b2637538b#n147
  44. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/system/examples?id=b8152d668d16faa464d2819af6f8ed4b2637538b
  45. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/system/examples/docker-image.tmpl?id=b8152d668d16faa464d2819af6f8ed4b2637538b
  46. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/system/examples/vm-image.tmpl?id=b8152d668d16faa464d2819af6f8ed4b2637538b
  47. https://learnxinyminutes.com/docs/common-lisp/
  48. https://guix.gnu.org/manual/en/html_node/Base-Services.html
  49. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/services/base.scm?id=b8152d668d16faa464d2819af6f8ed4b2637538b#n3315
  50. https://guix.gnu.org/manual/en/html_node/Shepherd-Services.html
  51. https://www.gnu.org/software/shepherd/manual/shepherd.html#Jump-Start-1
  52. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-23289ab
  53. https://thedroneely.com/posts/guix-in-a-linux-container/#overview-1
  54. https://guix.gnu.org/manual/en/html_node/operating_002dsystem-Reference.html
  55. https://www.gnu.org/software/emacs/
  56. https://en.wikipedia.org/wiki/Lisp_%28programming_language%29
  57. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-ca26718
  58. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-915a6ab
  59. https://man.archlinux.org/man/rsync.1
  60. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-8f6c72e
  61. https://thedroneely.com/posts/guix-in-a-linux-container/#container
  62. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-ebb6b76
  63. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/build/activation.scm?id=b8152d668d16faa464d2819af6f8ed4b2637538b#n352
  64. https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/build/activation.scm?id=b8152d668d16faa464d2819af6f8ed4b2637538b#n362
  65. https://github.com/lxc/lxc/issues/2282
  66. https://github.com/opencontainers/runtime-spec/blob/main/config-linux.md#masked-paths
  67. https://man.archlinux.org/man/find.1
  68. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-e415edd
  69. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-8c3b127
  70. https://thedroneely.com/posts/guix-in-a-linux-container/#comparisons
  71. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-1433a3f
  72. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-d16cd65
  73. https://thedroneely.com/posts/guix-in-a-linux-container/#code-block-2dd527d
  74. https://thedroneely.com/posts/guix-in-a-linux-container/#conclusion
  75. https://rendaw.gitlab.io/blog/55daefcf49e2.html
  76. https://thedroneely.com/videos/guix-in-a-linux-container.mp4
  77. https://man.archlinux.org/man/neofetch.1
  78. https://www.thedroneely.com/posts/guix-in-a-linux-container.md
  79. https://thedroneely.com/posts/guix-in-a-linux-container/#more-content
  80. https://thedroneely.com/posts/declarative-user-package-management-in-nixos/
  81. https://thedroneely.com/projects/bulma-resume/
  82. https://thedroneely.com/posts/writing-with-vale/
  83. https://git.sr.ht/~sircmpwn/openring
  84. https://thedroneely.com/posts/guix-in-a-linux-container/#web-ring
  85. https://drewdevault.com/2023/02/20/2023-02-20-Helios-aarch64.html
  86. https://drewdevault.com/
  87. https://www.taniarascia.com/websockets-in-redux/
  88. https://www.taniarascia.com/
  89. https://mxb.dev/blog/seven-reasons-why-i-dont-write/
  90. https://mxb.dev/
  91. https://thedroneely.com/posts/guix-in-a-linux-container/#comments
  92. https://thedroneely.com/sitemap.xml
  93. https://thedroneely.com/index.json
  94. https://thedroneely.com/resume/
  95. https://gitlab.com/tdro
  96. https://github.com/tdro
  97. https://codeberg.org/tdro
  98. https://thedroneely.com/analytics
  99. https://thedroneely.com/posts/guix-in-a-linux-container/#
  100. https://creativecommons.org/licenses/by-sa/2.0/
  101. https://thedroneely.com/git/thedroneely/thedroneely.com
  102. https://opensource.org/licenses/GPL-3.0
  103. https://www.thedroneely.com/