{ inputs = { self.submodules = true; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # nixpkgs.url = "git+https://github.com/valpackett/nixpkgs?shallow=1&submodules=1&ref=.."; flake-utils.url = "github:numtide/flake-utils"; virtwl.url = "git+https://github.com/valpackett/wayland-proxy-virtwl?shallow=1&submodules=1&ref=wip"; virtwl.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, flake-utils, virtwl, ... }: { nixosModules.default = { pkgs, lib, utils, config, ... }: let useTTY = { TTYPath = "/dev/hvc0"; StandardOutput = "tty"; StandardInput = "tty"; StandardError = "tty"; }; runtimeDir = "/run/vm-user"; in { boot.isContainer = true; fileSystems."/".device = lib.mkDefault "/dev/sda"; # dummy # Disable unused things environment.defaultPackages = lib.mkDefault [ ]; documentation = { enable = lib.mkDefault false; doc.enable = lib.mkDefault false; info.enable = lib.mkDefault false; man.enable = lib.mkDefault false; nixos.enable = lib.mkDefault false; }; services.logrotate.enable = false; services.udisks2.enable = false; system.tools.nixos-generate-config.enable = false; system.activationScripts.specialfs = lib.mkForce ""; systemd.coredump.enable = false; networking.firewall.enable = false; powerManagement.enable = false; boot.kexec.enable = false; console.enable = false; # Configure activation / systemd boot.initrd.systemd.enable = true; # for etc.overlay, but we don't have initrd system.etc.overlay.enable = true; # erofs system.etc.overlay.mutable = false; systemd.sysusers.enable = true; services.userborn.enable = false; services.udev.enable = lib.mkDefault true; services.udev.packages = lib.mkDefault []; environment.etc."resolv.conf".text = "# to be overridden with mount"; environment.etc."machine-id".text = "# to be overridden with mount"; environment.etc."systemd/system".source = lib.mkForce (utils.systemdUtils.lib.generateUnits { type = "system"; units = config.systemd.units; upstreamUnits = [ "sysinit.target" "local-fs.target" "nss-user-lookup.target" "umount.target" "shutdown.target" "reboot.target" "exit.target" "final.target" "systemd-exit.service" "systemd-journald.socket" "systemd-journald-audit.socket" "systemd-journald-dev-log.socket" "systemd-journald.service" "systemd-sysusers.service" "systemd-udevd-kernel.socket" "systemd-udevd-control.socket" "systemd-udevd.service" "systemd-tmpfiles-setup.service" "user.slice" ]; upstreamWants = ["multi-user.target.wants"]; }); # systemd.package = pkgs.systemdMinimal; # no sysusers systemd.defaultUnit = "microvm.target"; systemd.targets.microvm = { description = "Minimal microVM system"; wants = ["systemd-journald.socket" "systemd-udevd.service" "dbus.socket"]; unitConfig.AllowIsolate = "yes"; }; systemd.services.systemd-remount-fs.enable = lib.mkForce false; systemd.services.systemd-pstore.enable = lib.mkForce false; systemd.services.lastlog2-import.enable = lib.mkForce false; systemd.services.suid-sgid-wrappers.enable = lib.mkForce false; systemd.services.systemd-sysusers.serviceConfig = { ExecStartPre = lib.mkForce []; ExecStartPost = lib.mkForce []; }; systemd.services.microvm-nixos-activation = { enable = true; description = "NixOS Activation"; wantedBy = ["local-fs.target"]; before = ["systemd-tmpfiles-setup.service" "systemd-sysusers.service"]; requires = ["systemd-tmpfiles-setup.service" "systemd-sysusers.service"]; unitConfig.DefaultDependencies = false; serviceConfig = { Type = "oneshot"; PassEnvironment = ["MICROVM_CLOSURE" "MICROVM_UID" "MICROVM_GID"]; } // useTTY; script = '' PATH=$MICROVM_CLOSURE/sw/bin cp /etc/resolv.conf /run/ $MICROVM_CLOSURE/activate || true mount --bind /run/resolv.conf /etc/resolv.conf mount --bind /run/machine-id /etc/machine-id chown 1337:1337 /run ''; }; # Configure user account users.mutableUsers = false; users.users.appvm = { uid = 1337; group = "appvm"; isNormalUser = false; isSystemUser = true; home = "/home/appvm"; description = "microVM User"; extraGroups = [ "wheel" "video" "input" "systemd-journal" ]; }; users.groups.appvm.gid = 1337; users.allowNoPasswordLogin = true; systemd.tmpfiles.rules = ["d ${runtimeDir} 1337 1337 -"]; # Configure services systemd.services.muvm-remote = { enable = true; description = "microVM Application runner"; onFailure = ["exit.target"]; onSuccess = ["exit.target"]; after = ["microvm-nixos-activation.service"]; wantedBy = ["microvm.target"]; serviceConfig = { Type = "exec"; PassEnvironment = ["TERM" "MUVM_REMOTE_CONFIG"]; # "KRUN_CONFIG"]; Environment = ["XDG_RUNTIME_DIR=${runtimeDir}" "WAYLAND_DISPLAY=wayland-1" "PATH=/run/current-system/sw/bin"]; User = "appvm"; Group = "appvm"; ExecStart = "/opt/bin/muvm-remote"; ExecStopPost = ''+${pkgs.python3}/bin/python -c "import os,fcntl,struct;print(os.getenv('EXIT_STATUS', '1'));fcntl.ioctl(os.open('/', os.O_RDONLY), 0x7602, int(os.getenv('EXIT_STATUS', '1')))"''; } // useTTY; }; systemd.services.muvm-configure-network = { enable = true; description = "microVM Network configuration"; wantedBy = ["microvm.target"]; serviceConfig.Type = "oneshot"; serviceConfig.ExecStart = "/opt/bin/muvm-configure-network"; }; systemd.sockets.muvm-pwbridge = { enable = true; description = "PipeWire cross-domain proxy socket"; wantedBy = ["microvm.target"]; partOf = ["muvm-pwbridge.service"]; listenStreams = [ "${runtimeDir}/pipewire-0" ]; socketConfig = { SocketUser = "appvm"; SocketGroup = "appvm"; }; }; systemd.services.muvm-pwbridge = { enable = true; description = "PipeWire cross-domain proxy"; requires = ["muvm-pwbridge.socket"]; serviceConfig.Type = "exec"; serviceConfig.ExecStart = "/opt/bin/muvm-pwbridge"; }; systemd.services.wait-for-udev = { enable = true; description = "Wait for device rules being applied"; wantedBy = ["microvm.target"]; requires = ["systemd-udevd.service"]; after = ["systemd-udevd.service"]; serviceConfig = { Type = "oneshot"; ExecStart = [ "udevadm trigger --action=add" "udevadm settle" ]; }; }; systemd.sockets.wayland-proxy-virtwl = { enable = true; description = "Wayland cross-domain proxy socket"; wantedBy = ["microvm.target"]; partOf = ["wayland-proxy-virtwl.service"]; listenStreams = [ "${runtimeDir}/wayland-1" ]; socketConfig = { SocketUser = "appvm"; SocketGroup = "appvm"; FileDescriptorName = "wayland"; }; }; systemd.services.wayland-proxy-virtwl = { enable = true; description = "Wayland cross-domain proxy"; after = ["wait-for-udev.service"]; requires = ["wayland-proxy-virtwl.socket" "wait-for-udev.service"]; serviceConfig = { ExecStart = "${virtwl.packages.x86_64-linux.proxy}/bin/wayland-proxy-virtwl --virtio-gpu"; Environment = ["XDG_RUNTIME_DIR=${runtimeDir}"]; User = "appvm"; Group = "appvm"; }; }; hardware.graphics.enable = true; hardware.graphics.package = self.packages.${pkgs.system}.mesa; }; } // flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }; in { # packages.libkrunfw = pkgs.libkrunfw; packages.libkrunfw = pkgs.libkrunfw.overrideAttrs (old: { version = "5.0.0"; src = ./libkrunfw; # src = pkgs.fetchFromGitHub { # owner = "containers"; # repo = "libkrunfw"; # tag = "v4.10.0"; # hash = "sha256-mq2gw0+xL6qUZE/fk0vLT3PEpzPV8p+iwRFJHXVOMnk="; # }; kernelSrc = pkgs.fetchurl { url = "mirror://kernel/linux/kernel/v6.x/linux-6.12.44.tar.xz"; hash = "sha256-tlAhDtMCeyJJadFIqjd0Uqmq065/KFGr7dMa3+8Wva4="; }; # buildInputs = old.buildInputs; }); packages.libkrun = (pkgs.libkrun.override { withBlk = true; withGpu = true; withSound = true; withNet = true; libkrunfw = self.packages.${system}.libkrunfw; }).overrideAttrs (old: { src = ./libkrun; cargoDeps = pkgs.rustPlatform.importCargoLock { lockFile = ./libkrun/Cargo.lock; }; # mesonFlags = [ (pkgs.lib.mesonOption "decoders" "gles,vulkan,composer") ]; # no magma(?) }); packages.muvm = (pkgs.muvm.override { libkrun = self.packages.${system}.libkrun; }).overrideAttrs (old: { postPatch = ""; # no more sysctl; udevd now takes the var anyway; XXX: fex MUVM_UDEVD_PATH = "${pkgs.systemd}/lib/systemd/systemd-udevd"; src = ./muvm; cargoDeps = pkgs.rustPlatform.importCargoLock { lockFile = ./muvm/Cargo.lock; }; }); packages.munix = let munixScript = (pkgs.writeScriptBin "munix" (builtins.readFile ./munix)).overrideAttrs(old: { buildCommand = "${old.buildCommand}\n patchShebangs $out"; }); in pkgs.symlinkJoin { name = "munix"; paths = [ munixScript self.packages.${system}.muvm pkgs.passt pkgs.bubblewrap ]; buildInputs = [ pkgs.makeWrapper ]; postBuild = '' wrapProgram $out/bin/munix --prefix PATH : $out/bin ''; }; devShells.default = let projects = with self.packages.${system}; [ libkrun muvm ]; in pkgs.mkShell { MUVM_UDEVD_PATH = "${pkgs.systemd}/lib/systemd/systemd-udevd"; nativeBuildInputs = pkgs.lib.concatMap (pkg: pkg.nativeBuildInputs) projects; buildInputs = (pkgs.lib.concatMap (pkg: pkg.buildInputs) projects) ++ (with self.packages.${system}; [ # virglrenderer ]) ++ (with pkgs; [ cargo rust-analyzer rustfmt passt bubblewrap ]); }; packages.mesa = (pkgs.mesa.override { vulkanDrivers = [ "amd" "intel" "microsoft-experimental" # removing this breaks the build "nouveau" "swrast" "virtio" "gfxstream" # probably not going to use this though ]; }).overrideAttrs (new: old: { mesonFlags = old.mesonFlags ++ [ (pkgs.lib.mesonBool "amdgpu-virtio" true) ]; patches = old.patches ++ [ ./radvmmio.patch ]; # already merged to git }); nixosConfigurations.testvm = nixpkgs.lib.nixosSystem { inherit system; modules = [ self.nixosModules.default ({ ... }: { system.stateVersion = "25.11"; fonts.packages = [ pkgs.adwaita-fonts pkgs.dejavu_fonts ]; environment.systemPackages = [ pkgs.fastfetch pkgs.htop virtwl.packages.x86_64-linux.proxy pkgs.wayland-utils pkgs.weston pkgs.waycheck pkgs.vulkan-tools pkgs.glmark2 pkgs.mesa-demos pkgs.xorg.xeyes pkgs.xterm # pkgs.vkquake # build broken: Program 'spirv-remap' not found pkgs.veloren pkgs.kdePackages.kate pkgs.adwaita-icon-theme pkgs.amberol pkgs.bustle pkgs.d-spy pkgs.gnome-text-editor pkgs.firefox pkgs.ffmpeg-full pkgs.mpv pkgs.libva-utils pkgs.tailscale pkgs.zerotierone pkgs.localsend ]; }) ]; }; }); }