Setting up K3s on NIX#

Flake.nix#

Below is the flake I am using for this project. You may note I have a couple different outputs, including one configuration, and two packages (images using nixosgenerators). Being able to use QEMU to quickly launch an image from my local pc using the ./result/bin/ folder. The other two outputs are actual qcow and vhd images for use within my other testing environments (virt-manager and aws).

{
  description = "K3s on NIXOS";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; # or unstable if you prefer
    nixos-generators.url = "github:nix-community/nixos-generators";
  };

  outputs = {
    self,
    nixpkgs,
    nixos-generators,
  }: let
    system = "x86_64-linux";
  in {
    nixosConfigurations = {
      qemu = nixpkgs.lib.nixosSystem {
        inherit system;
        modules = [
          ./configuration.nix
          {
            virtualisation.vmVariant = {
              virtualisation = {
                emptyDiskImages = [4096];
                memorySize = 2048;
                diskSize = 8192;
                forwardPorts = [
                  {
                    from = "host";
                    host.port = 10443;
                    guest.port = 443;
                  }
                  {
                    from = "host";
                    host.port = 10022;
                    guest.port = 22;
                  }
                  {
                    from = "host";
                    host.port = 16443;
                    guest.port = 6443;
                  }
                  # Boot Service Ports
                  {
                    from = "host";
                    host.port = 18442;
                    guest.port = 8442;
                  }
                  {
                    from = "host";
                    host.port = 18443;
                    guest.port = 8443;
                  }
                ];
              };
              boot.growPartition = true;
              fileSystems."/".autoResize = true;
            };

            networking.hosts = {
              "127.0.0.1" = ["myhost.local test-vw.jmoore53.dev test-kb.jmoore53.dev test-f3.jmoore53.dev"];
            };
          }
        ];
      };
    };

    packages.${system} = {
      qemu-image = nixos-generators.nixosGenerate {
        inherit system;
        format = "qcow";
        modules = [
          ./configuration.nix
          {
            networking.useDHCP = true;
            services.openssh.enable = true;
            system.stateVersion = "25.05";
            virtualisation.diskSize = 8192;
            boot.growPartition = true;
            fileSystems."/".autoResize = true;
          }
        ];
      };
      amazon-image = nixos-generators.nixosGenerate {
        inherit system;
        format = "amazon";
        modules = [
          ./configuration.nix
          {
            networking.useDHCP = true;
            services.openssh.enable = true;
            system.stateVersion = "25.05";
            virtualisation.diskSize = 8192;
            boot.growPartition = true;
            fileSystems."/".autoResize = true;
            ec2.hvm = true;
          }
        ];
      };
    };
  };
}

Running a nix flake show gives the following:

$ nix flake show
git+file:///home/jack/projects/nik3xs?ref=refs/heads/task-885&rev=222a506e09dbd3fdcff9815f2a4b0d9769ee3b5d
├───nixosConfigurations
│   └───qemu: NixOS configuration
└───packages
    └───x86_64-linux
        ├───amazon-image: package 'nixos-image-amazon-25.05.20250112.2f9e2f8-x86_64-linux'
        └───qemu-image: package 'nixos-disk-image'

To test out the image I find myself running nix build .#nixosConfigurations.qemu.config.system.build.vm

Configuration.nix#

Below you will see a couple different things to note.

  1. I have imported modules, most of which can be ignored for now. The one we will look at in depth will be ./modules/k3s.nix
  2. Under services I have k3s enabled, but I have disabled traefik. This is so we can customize it with our own Traefik configuration
  3. I am allowing all users to SSH (maybe not as secure, but we need to ssh for testing)
  4. Password for user “jack” is Password123. Wow
  5. The firewall is disabled… for testing and for now.
{pkgs, ...}: {
  imports = [
    ./modules/boot-unlock-service.nix
    ./modules/k3s-secret.nix
    ./modules/k3s.nix
    ./modules/start-restore.nix
  ];

  networking.hostName = "l-nodet";
  time.timeZone = "UTC";

  nix.settings.experimental-features = ["nix-command" "flakes"];

  ##################
  # SERVICES
  ##################

  services = {
    k3s = {
      enable = true;
      role = "server";
      extraFlags = toString [
        "--debug"
        "--disable=traefik"
      ];
    };

    openssh = {
      enable = true;
      settings = {
        Port = 22;
        #PermitRootLogin = "no";
        PermitRootLogin = "prohibit-password";
        PasswordAuthentication = true;
        #AlowUsers = ["jack"];
        AlowUsers = null; #using null allows all users
      };
    };
  };

  ##################
  # Users
  ##################
  users.users.jack = {
    isNormalUser = true;
    description = "jack";
    initialPassword = "Password123"; # An idiot's luggage password, Password123
    extraGroups = ["wheel"]; # rootless podman doesn't need "docker"
    openssh.authorizedKeys.keys = [
      "ssh-ed25519 jack@pow"
      "ssh-ed25519 jack@nixos"
    ];
  };

  ##################
  # Firewall
  ##################
  networking.firewall = {
    enable = false;
    #enable = true;
    #allowedTCPPorts = [80 443 22];
  };

  ##################
  # Useful packages
  ##################
  environment.systemPackages = with pkgs; [
    vim
    curl
    wget
    jq
    openssl
    nettools
    restic
  ];

  system.stateVersion = "25.05";
}

With this setup k3s is running on our system. We are able to SSH to the server and the systemd service for k3s is running.