Sops and MicroVM wiring

Hey folks I’m quite new to NixOS, trying to setup MicroVM for some of my containers.

here is my nix host config:

{ config, self, lib, pkgs, ... }:
{
  imports = [ self.inputs.microvm.nixosModules.host ];
  microvm.vms = {
    nextdns = {      
      config = import ./nextdns.nix;
    };
  };

  systemd.tmpfiles.rules =
    lib.flatten (lib.mapAttrsToList (name: _: [
      "d /persist/microvm/${config.microvm.vms.${name}.config.networking.hostName}/ssh 0700 root root"
      "d /persist/save/microvm/${config.microvm.vms.${name}.config.networking.hostName} 0755 root root"
    ]) config.microvm.vms);
}

here is nextdns.nix

{ config, lib, ...}:
{
  imports = [
    ./microvm.nix
    ../../../nixosModules/nextdns.nix
  ];

  networking.hostName = "nextdns";

  systemd.network.enable = true;
}

and microvm.nix

{ self, config, lib, ...}:
let
  hostName = config.networking.hostName;
  baseDir = "/var/lib/microvms/${hostName}";
in
{
  microvm = {
    hypervisor = lib.mkDefault "cloud-hypervisor";
    vcpu = lib.mkDefault 1;
    mem = lib.mkDefault 1536;

    shares = [
      {
        source = "/nix/store";
        mountPoint = "/nix/.ro-store";
        tag = "store";
        proto = "virtiofs";
        socket = "${baseDir}/store.socket";
      }
      {
        source = "/run/secrets";
        mountPoint = "/run/secrets";
        tag = "secrets";
        proto = "virtiofs";
        readOnly = true;
        socket = "${baseDir}/sops.socket";
      }
      {
        source = "/persist/microvm/${hostName}/ssh";
        mountPoint = "/etc/ssh";
        tag = "ssh";
        proto = "virtiofs";
        socket = "${baseDir}/ssh.socket";
      }
      {
        source = "/persist/microvm/${hostName}";
        mountPoint = "/persist";
        tag = "persist";
        proto = "virtiofs";
        socket = "${baseDir}/persist.socket";
      }
    ];

    writableStoreOverlay = "/nix/.rw-store";
    volumes = [
      {
        image = "nix-store-overlay.img";
        mountPoint = "/nix/.rw-store";
        size = 2048;
      }
    ];

    interfaces = [
      {
        type = "tap";
        id = "vm-${
          if builtins.stringLength hostName <= 8
          then hostName
          else builtins.substring (builtins.stringLength hostName - 8) 8 hostName
        }";
        mac = let
          hash = builtins.hashString "sha256" hostName;
          octets = lib.genList (i: builtins.substring (i * 2) 2 hash) 5;
        in "02:${lib.concatStringsSep ":" octets}";
      }
    ];
  };

  system.stateVersion = "25.11";
}

essentially I’m trying to mount my secrets to MicroVM.

 
       … while evaluating the attribute 'value'
         at /nix/store/4skf2pc138s9ki0ajdpll0ad2nqcg8s0-source/lib/types.nix:759:29:
          758|                             inherit (def) file;
          759|                             value = def';
             |                             ^
          760|                           }

       … while calling the 'elemAt' builtin
         at /nix/store/4skf2pc138s9ki0ajdpll0ad2nqcg8s0-source/lib/lists.nix:345:43:
          344|   */
          345|   imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
             |                                           ^
          346|

       error: attribute 'sops' missing
       at /nix/store/gsn2swk90yayi0fvyr1r4vh79zvmqi9a-source/nixosModules/nextdns.nix:4:16:
            3|   role = "server";
            4|   configFile = config.sops.secrets."nextdns-${role}-config".path;
             |                ^
            5| in

and nextdns.nix service:

{ config, lib,... }:
let 
  role = if 
    config.networking.hostName == "raspberry3"
    then "server"
    else "client";
  configFile = config.sops.secrets."nextdns-${role}-config".path;
in
{
  services.resolved.enable = false;
  
  services.nextdns = {
    enable = true;

    arguments = [ "-config-file" configFile ];
  };
}

I would like to get it agnostic from host or microvm (not hacks in NextDNS module)… Am I doing something silly?

I actually got a working prototype of secrets with microvms.

nix run github:michael-c-buckley/nix-microvm#m1 and you’ll have a populated/run/secrets by sops-nix. The project does have some examples and docs.

I dusted it off after not touching it for a while just now, but did successfully imperatively launch the VM and it decrypted the secrets.

You need to import the sops-nix module in your nextdns configuration (or in microvm.nix) with something like:
sops-nix.nixosModules.sops

(GitHub - Mic92/sops-nix: Atomic secret provisioning for NixOS based on sops)