Add extra modules in declarative nixos-containers

I am using declarative containers on one machine that is defined in a Nix flake and I want to use nixosModules from third party Nix flakes, in this case, simple-nixos-mailserver. For a normal nixosSystem, I’d just add it to the list of modules like so:

mail = nixosSystem' rec {
  system = "x86_64-linux";
  modules = [
    simple-nixos-mailserver.nixosModule
    {
      enable = true;
      fqdn = "mail.example.com";
      domains = [ "example.com" ];
    }
  ];
};

Instead, I want to use the mailserver inside of a declarative nixos-container:

{ config, pkgs, simple-nixos-mailserver, ... }:               
{
  containers.mail = {
    ephemeral = true;
    autoStart = true;
    privateNetwork = true;
    hostAddress = "10.0.0.20";
    localAddress = "10.0.0.21";
    
    config = 
      { config, pkgs, simple-nixos-mailserver, ... }:
      {
        mailserver = {
          enable = true;
          fqdn = "mail.example.com";
          domains = [ "example.com" ];
        };

        system.stateVersion = "23.11";
      };
  };
}

The issue I encounter is that the module-system of the container does not have mailserver available and there seems to be no obvious way to pass in additional modules for the container since I only specify the top-level config, but not a whole nixosSystem. My attempts at using something like:

imports = [ simple-nixos-mailserver.nixosModule ];

all fail with obscure errors about incompatible data types.

I found this documentation from Eelco where this is done for imperative containers and I am wondering how to achieve the same effect for declarative containers. Has anyone done this successfully?

Turns out, I was quite close. This is working for me right now:

{ config, pkgs, simple-nixos-mailserver, ... }:               
{
  containers.mail = {
    ephemeral = true;
    autoStart = true;
    privateNetwork = true;
    hostAddress = "10.0.0.20";
    localAddress = "10.0.0.21";
    
    config = 
      { config, pkgs, ... }:
      {
        imports = [ simple-nixos-mailserver.nixosModule ];
        mailserver = {
          enable = true;
          fqdn = "mail.example.com";
          domains = [ "example.com" ];
        };

        system.stateVersion = "23.11";
      };
  };
}
1 Like

Thanks for posting this. I am trying to do the same thing. However I keep getting an error of: containers.mail.mailserver does not exist when trying your final solution.

Are you certain that you have the config block under containers.mail? The error to me appears like you directly try to use containers.mail.mailserver. The complete config I currently use is here, in case you want to get inspired.

Thanks for the link to your config. Very helpful to see. As a work around, what I ended up doing was just going with the following inside the declarative container:

  imports = [
    (builtins.fetchTarball {
      # Pick a release version you are interested in and set its hash, e.g.
      url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-23.05/nixos-mailserver-nixos-23.05.tar.gz";
      # To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command:
      # release="nixos-23.05"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack
      sha256 = "1ngil2shzkf61qxiqw11awyl81cr7ks2kv3r3k243zz7v2xakm5c";
    })
  ];

since I was having trouble plumbing it up with flakes. In general, I really enjoy the flakes way of doing things, but wow, when it does not go as planned it is quite the rabbit hole to figure out! Again, thanks for your reply.