I have a workaround so this isn’t exactly a problem for me. I’m just curious about what is going on, and what best practices are.

I’m setting up Arion. I think it will be helpful for my development flow in a project where I have several services that need to run and communicate with each other. Docker-compose is a nice way to handle this, but you have to have a Docker image to run, and it’s a pain to create a new image after each code change. OTOH Arion will run an arbitrary command, and creates Nix-friendly images automatically. Very promising!

The Nix expression for the service I’m developing is exported from a flake, while the arion executable reads its configuration from a Nix expression that is not a flake. There is an example configuration that recommends importing a flake using builtins.getFlake which you can see here: https://github.com/hercules-ci/arion/blob/main/examples/flake/arion-pkgs.nix

The problem is that builtins.getFlake is slow. It adds >20s to every arion command I run. That includes starting services, reading logs, removing stopped containers, etc.

The example config includes a fallback that loads the flake using flake-compat instead of builtins.getFlake. When I use flake-compat loading the flake is nearly instant.

So I’m using flake-compat, and that seems to be working. (Many thanks to the flake-compat author!) But I’m curious why builtins.getFlake is so slow.

  • The other gotcha I ran into was a problem with volume permissions. This is on NixOS running MongoDB. This might not be a problem with other services. The Arion doc example for volumes has you store the volume in a repo directory. Here is what I tried:

    {
      services.mongodb = {
        service.image = "mongo:6-jammy";
        service.volumes = [ "${toString ./.}/mongo-data:/data/db" ]; # <-- volume defined here
      };
    }
    

    When I tried this the volume data in my repo directory is owned by avahi, and is not readable by my user account. Subsequent arion commands fail trying to read those files.

    This fix is to let docker-compose manage the volume for you:

    {
      services.mongodb = {
        service.image = "mongo:6-jammy";
        service.volumes = [ "mongo-data:/data/db" ]; # <-- volume defined here
      };
      docker-compose.volumes = {
        mongo-data = null; # <-- this name matches the volume name referenced in the mongodb service
      };
    }
    
  • I noticed that Arion has an interesting option, useHostStore = true, for services that are defined using a command or a nixos config instead of a Docker image. This mounts the host system’s /nix/store directory in the running container. It looks like the image that gets produced is only a customization layer with the command to run, environment variables, etc. It looks quite efficient.

    I haven’t checked, but I’d guess that if you don’t use useHostStore you get layered images. Those are great for image layer reuse, but they do involve copying store paths to the local image repository.