Use home-manager inside a nixos container (Flakes)

I’m using home-manager inside the flake.nix the host works
Now I’m trying to run a graphical container (guest) based on:

This works but it manually fetches the home-manager. Can I just link/use the home-manager from flake.nix? how?

I tried adding home-manager to { config, lib, pkgs, home-manager, ... }: but this doesn’t work. It needs:

  home-manager = builtins.fetchGit {
    url = "https://github.com/rycee/home-manager.git";
    rev = "69536af27e86a9fc875d71cb9566ccccf47b5b60";  ##copied from flake.lock. Is there a way to use the flake's home-manager inside the containers
  };

and inside the container:

config.imports = [
  (import "${home-manager}/nixos")
];

complete file: gui-firefox.nix:

{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.ncfg.containers.gui-firefox;
  home-manager = builtins.fetchGit {
    url = "https://github.com/rycee/home-manager.git";
    rev = "69536af27e86a9fc875d71cb9566ccccf47b5b60";  ##TODO: copied from flake.lock. Is there a way to use the flake's home-manager inside the containers
  };
in
{
  options.ncfg.containers.gui-firefox.enable = mkEnableOption "Enable gui firefox container";

  config = mkIf cfg.enable {
    environment.systemPackages = let
      userName = config.ncfg.primaryUser.name;
      containerName = "graphicalExample";
      hostLauncher = pkgs.writeScriptBin "${containerName}-launcher" ''
        #!${pkgs.stdenv.shell}
        set -euo pipefail

        if [[ "$(systemctl is-active container@${containerName}.service)" != "active" ]]; then
          systemctl start container@${containerName}.service
        fi

        exec machinectl shell ${userName}@${containerName} /usr/bin/env bash --login -c "exec ${pkgs.wofi}/bin/wofi --show run"
      '';
    in [ hostLauncher ];

    ## Start the container:
    # systemctl start container@graphicalExample.service
    ## Enter shell inside container:
    # machinectl shell myuser@graphicalExample /usr/bin/env bash --login
    containers.graphicalExample = let
      hostCfg = config;
      userName = "myuser";
      userUid = hostCfg.users.users.${config.ncfg.primaryUser.name}.uid;
    in {
      bindMounts = {
        waylandDisplay = rec {
          hostPath = "/run/user/${toString userUid}";
          mountPoint = hostPath;
        };
        ## Only when running x11 applications in the guest
        x11Display = rec {
          hostPath = "/tmp/.X11-unix";
          mountPoint = hostPath;
          isReadOnly = true;
        };
      };

      config = {
        imports = [
          (import "${home-manager}/nixos")
        ];

        users.users."${userName}" = {
          uid = 1000;
          isNormalUser = true;
          initialPassword = "secret";
          extraGroups = lib.mkForce [];
        };

        # fonts.fonts = with pkgs; [
        #   dejavu_fonts  # Default font used by Alacritty.
        # ];

        hardware.opengl = {
          enable = true;
          extraPackages = hostCfg.hardware.opengl.extraPackages;
        };


        environment.systemPackages = with pkgs; [
          # vim
          # jetbrains.idea-community
          firefox
        ];

        home-manager = {
          useGlobalPkgs = true;
          users."${userName}" = {
            programs.bash.enable = true;

            wayland.windowManager.sway = {
              enable = true;
              wrapperFeatures.gtk = true;
              config = {
                # Use Alt/Meta instead of Super to decrease chance of conflict with host key mappings.
                modifier = "Mod1";

                # # And use terminal with some sane defaults.
                # terminal = "alacritty";
         
                # Application launcher.
                menu = "${pkgs.wofi}/bin/wofi --show run";
         
                window.titlebar = true;
              };
            };
         
            # home.packages = with pkgs; [
            #   alacritty
            # ];
         
            gtk.enable = true;

            home.sessionVariables = {
              WAYLAND_DISPLAY                     = "wayland-1";
              QT_QPA_PLATFORM                     = "wayland";
              QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
              SDL_VIDEODRIVER                     = "wayland";
              CLUTTER_BACKEND                     = "wayland";
              MOZ_ENABLE_WAYLAND                  = "1";
              _JAVA_AWT_WM_NONREPARENTING         = "1";
              _JAVA_OPTIONS                       = "-Dawt.useSystemAAFontSettings=lcd";
              XDG_RUNTIME_DIR                     = "/run/user/${toString userUid}";
              DISPLAY                             = ":0";
            };
          };
        };

        systemd.services.fix-nix-dirs = let
          profileDir = "/nix/var/nix/profiles/per-user/${userName}";
          gcrootsDir = "/nix/var/nix/gcroots/per-user/${userName}";
        in {
          script = ''
            #!${pkgs.stdenv.shell}
            set -euo pipefail

            mkdir -p ${profileDir} ${gcrootsDir}
            chown ${userName}:root ${profileDir} ${gcrootsDir}
          '';
          wantedBy = [ "multi-user.target" ];
          serviceConfig = {
            Type = "oneshot";
          };
        };

        systemd.services.fix-run-permission = {
          script = ''
            #!${pkgs.stdenv.shell}
            set -euo pipefail

            chown ${userName}:users /run/user/${toString userUid}
            chmod u=rwx /run/user/${toString userUid}
          '';
          wantedBy = [ "multi-user.target" ];
          serviceConfig = {
            Type = "oneshot";
          };
        };
      };
    };





    # containers.cliExample = let
    #   userName = "myuser";
    #   home-manager = builtins.fetchGit {
    #     url = "https://github.com/rycee/home-manager.git";
    #     ref = "release-21.05";
    #     rev = "35a24648d155843a4d162de98c17b1afd5db51e4";
    #   };
    # in {
    #   config = {
    #     # (2)
    #     imports = [
    #       (import "${home-manager}/nixos")
    #       ./desktop.nix
    #     ];

    #     # (3)
    #     home-manager = {
    #       useGlobalPkgs = true;
    #       users."${userName}" = {
    #         imports = [ ./home.nix ];
    #       };
    #     };
        
    #     # (4)
    #     users.users."${userName}".extraGroups = lib.mkForce [];

    #     # (5)
    #     systemd.services.fix-nix-dirs = let
    #       profileDir = "/nix/var/nix/profiles/per-user/${userName}";
    #       gcrootsDir = "/nix/var/nix/gcroots/per-user/${userName}";
    #     in {
    #       script = ''
    #         #!${pkgs.stdenv.shell}
    #         set -euo pipefail

    #         mkdir -p ${profileDir} ${gcrootsDir}
    #         chown ${userName}:root ${profileDir} ${gcrootsDir}
    #       '';
    #       wantedBy = [ "multi-user.target" ];
    #       serviceConfig = {
    #         Type = "oneshot";
    #       };
    #     };
        
    #     # (6)
    #     environment.systemPackages = with pkgs; [ vim ];
    #   };
    # };

  };
}

There are a handful of approaches to this. This thread lists them all: Install agenix in "environment.systemPackages" on nixos with flakes

To be a bit more explicit, you can either use the overlay, and then use the NixOS module: https://nix-community.github.io/home-manager/index.html#sec-install-nixos-module, or you can use _module.args and do what you’re doing now.

Hi, author here :slight_smile:

The way I resolved this during migration to flakes was to modify imports in every container in the flake.nix. Something like this:

outputs = { self, nixpkgs, home-manager, ... }: {
  nixosConfiguration = let
    containers-module = { config, lib, ... }: let cfg = config; in {
      config.containers = lib.mapAttrs (name: containerCfg: {
        config = {
          imports = [ home-manager.nixosModules.home-manager ];
          nixpkgs.overlays = [ (import self.overlay) ];
        };
      }) cfg.services.my-custom.containers.containers;
    };
  in {
    some-system = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        containers-module
        self.nixosModules.my-base
      ];
    };
  };
};

cfg.services.my-custom.containers.containers is configuration for my custom module handling all NixOS containers interactions. I’m using it here to get list of all containers and alter imports and overlays in each of them.
It is a bit ugly, but it’s working so far.

Thanks for your replies.

I found something which seems to works for me.
It might not be best practices…

add inputs:

{
  config,
  lib,
  pkgs,
  inputs,
  ...
}:

import:

config = {
    imports = [ inputs.home-manager.nixosModules.home-manager ];
    ...
};