Deploying Cuirass on GuixSD

9 min 1766 words
Peter Tillemans Profile picture

Table of Contents

  1. Install Cuirass in Guix
  2. Create a postgresql server
  3. Enable the cuirass service
  4. Enabling the Web Frontend
  5. Submitting jobs
    1. Figure out a more elegant way to submit jobs

Install Cuirass in Guix

I had a hard time installing cuirass on my system. I tried to collect some notes to move step by step to a working solution.

GUIX is great that it is really easy to revert your step to get out or a dead end and start over from a previous point. Something I made liberally use off to get something working. However it also means my deployment process was not really linear but much more error driven.

Create a postgresql server

Starting the cuirass service will pull in a postgres server, however it will use by default a version 10. Let's update that to 15 for some future proofing:

(service postgresql-role-service-type
           (list (postgresql-role
                  (name "cuirass")
                  (create-database? #t))
                  (name "xyz")
                  (create-database? #t))))))

I also added a database user for me (xyz here stands for my user account).

This will also create a database with the same name to allow the cuirass user (and me) to login to the postgres database and do database things.

Run a `guix system reconfigure` and check if the postgres is running with `sudo herd status postgres`.

Enable the cuirass service

With the database in place there is a fighting chance to get the service running :

(service cuirass-service-type
          (specifications #~(list))))

The example in the reference documentation offers something more interesting than an empty list but whatever I tried ended up with 'invalid field specifier' errors. But I get that too for nginx configuration parts so that is probably a skill issue on my part.

Edit: definitely skill issue. I did not see that I defined them in the operating-system expression instead of on toplevel because it starts several screens up and down. Duh. Moving the define of the specifications to toplevel and using them here works just fine.

With a bit of luck we'll see:

xyz@foo ~/.config/dotfiles/guix [env]$ sudo herd status cuirass
Status of cuirass:
  It is running since 05:19:42 PM (4 hours ago).
  Running value is 6513.
  It is enabled.
  Provides (cuirass).
  Requires (user-processes guix-daemon postgres postgres-roles networking).
  Will be respawned.
xyz@foo ~/.config/dotfiles/guix [env]$ sudo herd status cuirass-web
Status of cuirass-web:
  It is running since 05:22:41 PM (4 hours ago).
  Running value is 6679.
  It is enabled.
  Provides (cuirass-web).
  Requires (user-processes cuirass).
  Will be respawned.

If not there may be some info in `/var/log/cuirass.log` or `/var/log/cuirass-web.log`

Alternatively for debugging we can run the application from the git repository.

First of all we have to give our user access to the database:

xyz@foo ~/.config/dotfiles/guix [env]$ sudo -u cuirass psql
psql (15.4)
Type "help" for help.

cuirass=> grant all on database cuirass to xyz;

if the cuirass service has initialised the database already you can add:

cuirass=> grant all on all tables in schema public to xyz;
cuirass=> grant all on all sequences  in schema public to xyz;

This allows access without jumping through the sudo hoop.

The code in the guix-cuirass project folder will now just work. Except the postgresql socket should be exposed too in the proposed command of the reference manual:

guix shell -CPNW --expose=/var/log/guix/drvs \
--expose=/var/run/dbus --expose=/run/avahi-daemon \
--expose=/etc/ssl/certs --expose=/var/run/postgresql

Note that if the tables and sequences are created when running in your account it is quite possible that the cuirass user will not be able to access them and complain with access denied errors. In that case we have to do the grants for the cuirass user:

xyz@foo ~/.config/dotfiles/guix [env]$ psql cuirass
psql (15.4)
Type "help" for help.

xyz=> grant all on all tables in schema public to cuirass;
xyz=> grant all on all sequences  in schema public to cuirass;

Then restarting the service with `sudo herd restart cuirass` should make it start, or at least give a different error.

Once cuirass service is running, it will be possible to run the cuirass-web service. It relies on the existence of the /var/run/cuirass/bridge file which is created by the cuirass service.

Just to be sure that the web server is running :

xyz@foo ~/src/guix-cuirass [env]$ wget http://localhost:8081
--2024-06-11 21:59:09--  http://localhost:8081/
Resolving localhost (localhost)...
Connecting to localhost (localhost)||:8081... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6142 (6.0K) [text/html]
Saving to: ‘index.html’

index.html                    100%[================================================>]   6.00K  --.-KB/s    in 0s

2024-06-11 21:59:09 (378 MB/s) - ‘index.html’ saved [6142/6142]

Cool, we have a 200 Ok status code and some index.html file so the web UI is running. Now exposing it to the 'net.

Enabling the Web Frontend

In the reference manual there is a nice configuration to start from to configure nginx as a frontend for cuirass-web

However you cannot start nginx https without having the certificates and you cannot get the certificates without the server running. (Well you can but that is outside the scope of this post)

So we have to cut back the configuration to only start the http nginx server to do the first handschake with letsencrypt to get the initial certificates

Let's start with the certbot-service :

(service certbot-service-type
          (email "")
             (domains '("")))

Then add the minimal part for a nginx http server to do the letsencrypt dance.

(service nginx-service-type
            ;; TLS is required for authentication; serve the site via
            ;; HTTPS only.
             (listen '("80"))
              (list "return 308 https://$host$request_uri;")))


Doing a `guix system reconfigure` now will start the nginx server and download fresh certificates.

We can now add the https server proxy-ing the cuirass-web server:

(service nginx-service-type
            ;; TLS is required for authentication; serve the site via
            ;; HTTPS only.
             (listen '("80"))
              (list "return 308 https://$host$request_uri;")))

             (listen '("443 ssl"))
             (server-name '(""))
             (ssl-certificate "/etc/letsencrypt/live/")
             (ssl-certificate-key "/etc/letsencrypt/live/")
               ;; Proxy the whole Cuirass web site...
                (uri "/")
                (body (list "proxy_pass http://localhost:8081;")))
               ;; ... but require authentication for the admin pages.
                (uri "~ ^/admin")
                 (list "if ($ssl_client_verify != SUCCESS) \
            { return 403; } proxy_pass http://localhost:8081;")))))
             ;; (raw-content
             ;;  ;; Register your self-generated certificate authority.
             ;;  (list "ssl_client_certificate /etc/ssl/certs/Snamellit_CA.pem;"
             ;;        "ssl_verify_client optional;"))

Creating and installing the root CA is a bit out of scope. For this post we'll just wave our hands and assume the certificate magically appeared in the `/etc/ssl/certs/SnamellitCA.pem` location. Creating a client certificate for firefox and importing it allows to access the admin section and retrigger jobs etc.

Once that is setup the commented out section can be activated.

Interactivele Submitting jobs

For some reason I do not understand yet, the specification file must be in the loadpath of the cuirass program.

For testing I add them to the examples folder in the guix-cuirass folder in the home folder of the cuirass user

$ sudo -u cuirass -- bash              # start a shell in the cuirass user
$ cd guix-cuirass                      # enter the project folder
$ cp /foo/bar/snamguix.scm examples    # put spec somewhere on loadpath
$ # start dev environment in container
$ guix shell -CPNW --expose=/var/log/guix/drvs \
    --expose=/var/run/dbus --expose=/run/avahi-daemon \
    --expose=/etc/ssl/certs --expose=/var/run/postgresql
guix shell: loading environment from '/home/pti/src/guix-cuirass/guix.scm'...
$ ~/src/guix-cuirass [env]$ # register new recipe
$ ~/src/guix-cuirass [env]$ ./pre-inst-env cuirass register -S examples/snamguix.scm
2024-06-11T20:18:08 running Fibers on 8 kernel threads
2024-06-11T20:18:08 marking stale builds as "scheduled"...
2024-06-11T20:18:08 builds will be made via the local build daemon
2024-06-11T20:18:08 will perform up to 8 evaluations concurrently
2024-06-11T20:18:08 opening bridge socket at '/tmp/cuirass-tests/var/run/cuirass/bridge'
2024-06-11T20:18:08 retrieving list of pending builds...
2024-06-11T20:18:08 unused GC roots older than 2592000s will be deleted every 86400s
2024-06-11T20:18:08 deleting old GC roots from '/var/guix/gcroots/profiles/per-user/pti/cuirass'...
2024-06-11T20:18:08 selected 0 GC roots to remove
WARNING: (cuirass base): imported module (fibers) overrides core binding `sleep'
2024-06-11T20:18:08 heap: 12.18 MiB; threads: 17; file descriptors: 76
WARNING: (cuirass scripts register): imported module (fibers) overrides core binding `sleep'
2024-06-11T20:18:08 canceling 0 stale builds
2024-06-11T20:18:08 restarting 0 pending builds
2024-06-11T20:18:08 building 0 derivations in batches of 200
2024-06-11T20:18:08 done with 0 derivations
2024-06-11T20:18:08 outputs:

This actually starts the scheduler and it just keeps running (unless we also give the –one-shot flag) but the side effect is to add the specification to the database. It also allows to see immediately if there are syntax errors or similar. When satisfied Ctrl-C out of it.

From now on the channel will be checked and build any updated packages.

Permanently add the job

Once vetted the specification can be added to the system configuration.

Define the specifications on the toplevel of the system config file. (Be careful, I lost a lot of time because I did not see it was actually in the operating-system expression)

;; cuirass specifications
(define %cuirass-specifications
    (name "hello")
    (build 'hello))
    (name 'snamguix)
    (build '(channels . (snamguix)))
    (cons (channel
            (name 'snamguix)
            (url "")
            (branch "main"))

then replace the empty list in the cuirass-service :

       (service cuirass-service-type
                 (specifications %cuirass-specifications)))

reconfigure your system and restart cuirass with sudo herd restart cuirass.

It is not really needed as it was already added to the database, but this will be useful when the CI server need to be repaved.