Next: , Up: Containers   [Contents][Index]


4.1 Guix Containers

The easiest way to get started is to use guix shell with the --container option. See Invoking guix shell in GNU Guix Reference Manual for a reference of valid options.

The following snippet spawns a minimal shell process with most namespaces unshared from the system. The current working directory is visible to the process, but anything else on the file system is unavailable. This extreme isolation can be very useful when you want to rule out any sort of interference from environment variables, globally installed libraries, or configuration files.

guix shell --container

It is a bleak environment, barren, desolate. You will find that not even the GNU coreutils are available here, so to explore this deserted wasteland you need to use built-in shell commands. Even the usually gigantic /gnu/store directory is reduced to a faint shadow of itself.

$ echo /gnu/store/*
/gnu/store/…-gcc-10.3.0-lib
/gnu/store/…-glibc-2.33
/gnu/store/…-bash-static-5.1.8
/gnu/store/…-ncurses-6.2.20210619
/gnu/store/…-bash-5.1.8
/gnu/store/…-profile
/gnu/store/…-readline-8.1.1

There isn’t much you can do in an environment like this other than exiting it. You can use ^D or exit to terminate this limited shell environment.

You can make other directories available inside of the container environment; use --expose=DIRECTORY to bind-mount the given directory as a read-only location inside the container, or use --share=DIRECTORY to make the location writable. With an additional mapping argument after the directory name you can control the name of the directory inside the container. In the following example we map /etc on the host system to /the/host/etc inside a container in which the GNU coreutils are installed.

$ guix shell --container --share=/etc=/the/host/etc coreutils
$ ls /the/host/etc

Similarly, you can prevent the current working directory from being mapped into the container with the --no-cwd option. Another good idea is to create a dedicated directory that will serve as the container’s home directory, and spawn the container shell from that directory.

On a foreign system a container environment can be used to compile software that cannot possibly be linked with system libraries or with the system’s compiler toolchain. A common use-case in a research context is to install packages from within an R session. Outside of a container environment there is a good chance that the foreign compiler toolchain and incompatible system libraries are found first, resulting in incompatible binaries that cannot be used by R. In a container shell this problem disappears, as system libraries and executables simply aren’t available due to the unshared mount namespace.

Let’s take a comprehensive manifest providing a comfortable development environment for use with R:

(specifications->manifest
  (list "r-minimal"

        ;; base packages
        "bash-minimal"
        "glibc-locales"
        "nss-certs"

        ;; Common command line tools lest the container is too empty.
        "coreutils"
        "grep"
        "which"
        "wget"
        "sed"

        ;; R markdown tools
        "pandoc"

        ;; Toolchain and common libraries for "install.packages"
        "gcc-toolchain@10"
        "gfortran-toolchain"
        "gawk"
        "tar"
        "gzip"
        "unzip"
        "make"
        "cmake"
        "pkg-config"
        "cairo"
        "libxt"
        "openssl"
        "curl"
        "zlib"))

Let’s use this to run R inside a container environment. For convenience we share the net namespace to use the host system’s network interfaces. Now we can build R packages from source the traditional way without having to worry about ABI mismatch or incompatibilities.

$ guix shell --container --network --manifest=manifest.scm -- R

R version 4.2.1 (2022-06-23) -- "Funny-Looking Kid"
Copyright (C) 2022 The R Foundation for Statistical Computing
…
> e <- Sys.getenv("GUIX_ENVIRONMENT")
> Sys.setenv(GIT_SSL_CAINFO=paste0(e, "/etc/ssl/certs/ca-certificates.crt"))
> Sys.setenv(SSL_CERT_FILE=paste0(e, "/etc/ssl/certs/ca-certificates.crt"))
> Sys.setenv(SSL_CERT_DIR=paste0(e, "/etc/ssl/certs"))
> install.packages("Cairo", lib=paste0(getwd()))
…
* installing *source* package 'Cairo' ...
…
* DONE (Cairo)

The downloaded source packages are in
	'/tmp/RtmpCuwdwM/downloaded_packages'
> library("Cairo", lib=getwd())
> # success!

Using container shells is fun, but they can become a little cumbersome when you want to go beyond just a single interactive process. Some tasks become a lot easier when they sit on the rock solid foundation of a proper Guix System and its rich set of system services. The next section shows you how to launch a complete Guix System inside of a container.


Next: Guix System Containers, Up: Containers   [Contents][Index]