diff --git a/README.md b/README.md
index 74ee0a61..bc874178 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
A Nix Flake to build NixOS and run it on one of several Type-2
-Hypervisors on NixOS/Linux. The project is intended to provide a more
+Hypervisors on NixOS/Linux or macOS. The project is intended to provide a more
isolated alternative to `nixos-container`. You can either build and
run MicroVMs like Nix packages, or alternatively install them as
systemd services declaratively in your host's Nix Flake or
@@ -26,8 +26,8 @@ imperatively with the provided `microvm` command.
- MicroVMs are Virtual Machines but use special device interfaces
(virtio) for high performance.
-- This project runs them on NixOS hosts.
-- You can choose one of five hypervisors for each MicroVM.
+- This project runs them on NixOS/Linux hosts and macOS (via vfkit).
+- You can choose from eight hypervisors for each MicroVM.
- MicroVMs have a fixed RAM allocation (default: 512 MB) but can be
shrunk using `microvm-balloon`
- MicroVMs have a read-only root disk with either a prepopulated
@@ -40,20 +40,21 @@ imperatively with the provided `microvm` command.
a block device, or alternatively as a shared directory hierarchy
through *9p* or *virtiofs*.
- Zero, one, or more virtual tap ethernet network interfaces can be
- attached to a MicroVM. `qemu` and `kvmtool` also support *user*
+ attached to a MicroVM. `qemu`, `kvmtool`, and `vfkit` also support *user*
networking which requires no additional setup on the host.
## Hypervisors
-| Hypervisor | Language | Restrictions |
-|-------------------------------------------------------------------------|----------|------------------------------------------|
-| [qemu](https://www.qemu.org/) | C | |
-| [cloud-hypervisor](https://www.cloudhypervisor.org/) | Rust | no 9p shares |
-| [firecracker](https://firecracker-microvm.github.io/) | Rust | no 9p/virtiofs shares |
-| [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/) | Rust | 9p shares broken |
-| [kvmtool](https://github.com/kvmtool/kvmtool) | C | no virtiofs shares, no control socket |
-| [stratovirt](https://github.com/openeuler-mirror/stratovirt) | Rust | no 9p/virtiofs shares, no control socket |
-| [alioth](https://github.com/google/alioth) | Rust | no virtiofs shares, no control socket |
+| Hypervisor | Language | Restrictions |
+|-------------------------------------------------------------------------|----------|-------------------------------------------------------|
+| [qemu](https://www.qemu.org/) | C | |
+| [cloud-hypervisor](https://www.cloudhypervisor.org/) | Rust | no 9p shares |
+| [firecracker](https://firecracker-microvm.github.io/) | Rust | no 9p/virtiofs shares |
+| [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/) | Rust | 9p shares broken |
+| [kvmtool](https://github.com/kvmtool/kvmtool) | C | no virtiofs shares, no control socket |
+| [stratovirt](https://github.com/openeuler-mirror/stratovirt) | Rust | no 9p/virtiofs shares, no control socket |
+| [alioth](https://github.com/google/alioth) | Rust | no virtiofs shares, no control socket |
+| [vfkit](https://github.com/crc-org/vfkit) | Go | macOS only, no 9p shares, no tap/bridge networking |
## Installation
@@ -85,6 +86,9 @@ nix run microvm#cloud-hypervisor-example
nix run microvm#crosvm-example
nix run microvm#kvmtool-example
nix run microvm#stratovirt-example
+
+# On macOS only:
+nix run microvm#vfkit-example
```
### Run a MicroVM example with nested MicroVMs on 5 different Hypervisors
@@ -96,12 +100,15 @@ nix run microvm#vm
Check `networkctl status virbr0` for the DHCP leases of the nested
MicroVMs. They listen for ssh with an empty root password.
-### Experimental: run graphical applications in cloud-hypervisor with Wayland forwarding
+### Experimental: run graphical applications with graphics support
+On Linux with cloud-hypervisor and Wayland forwarding:
```shell
nix run microvm#graphics neverball
```
+On macOS with vfkit, enable graphics with `microvm.graphics.enable = true`.
+
## Commercial support
Accelerate your operations and secure your infrastructure with support from a
diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md
index 4f9e1175..18d89968 100644
--- a/doc/src/SUMMARY.md
+++ b/doc/src/SUMMARY.md
@@ -8,6 +8,7 @@
- [Device pass-through](./devices.md)
- [CPU emulation](./cpu-emulation.md)
- [Output options](./output-options.md)
+ - [Using Rosetta with vfkit (macOS)](./vfkit-rosetta.md)
- [MicroVM options reference ⚙️](./microvm-options.md)
- [Running a MicroVM as a package](./packages.md)
- [Preparing a host for declarative MicroVMs](./host.md)
diff --git a/doc/src/interfaces.md b/doc/src/interfaces.md
index 9c494abd..73b85a9d 100644
--- a/doc/src/interfaces.md
+++ b/doc/src/interfaces.md
@@ -20,12 +20,15 @@ configuration:
## `type = "user"`
-User-mode networking is only provided by qemu and kvmtool, providing
+User-mode networking is provided by qemu, kvmtool, and vfkit, providing
outgoing connectivity to your MicroVM without any further setup.
As kvmtool seems to lack a built-in DHCP server, additional static IP
configuration is necessary inside the MicroVM.
+**Note:** vfkit (macOS) only supports user-mode networking. TAP and bridge
+networking are not available.
+
## `type = "tap"`
Use a virtual tuntap Ethernet interface. Its name is the value of
diff --git a/doc/src/intro.md b/doc/src/intro.md
index dbce17d6..f4886c63 100644
--- a/doc/src/intro.md
+++ b/doc/src/intro.md
@@ -1,9 +1,9 @@
# Intro
**microvm.nix** is a Flake to run lightweight NixOS virtual machines
-on NixOS. Starting with the reasons why for the remainder of this
+on NixOS and macOS. Starting with the reasons why for the remainder of this
chapter, this handbook guides you through the provisioning of MicroVMs
-on your NixOS machine.
+on your NixOS or macOS machine.
## Compartmentalization
@@ -47,4 +47,5 @@ overhead has been reduced a lot by replacing emulated devices with
This Flake offers you to run your MicroVMs not only on QEMU but with
other Hypervisors that have been explicitly authored for
*virtio*. Some of them are written in Rust, a programming language
-that is renowned for being safer than C.
+that is renowned for being safer than C. On macOS, vfkit leverages
+Apple's native Virtualization.framework for running Linux VMs.
diff --git a/doc/src/options.md b/doc/src/options.md
index 71b5852f..308779f9 100644
--- a/doc/src/options.md
+++ b/doc/src/options.md
@@ -15,6 +15,9 @@ available for customization. These are the most important ones:
| `microvm.socket` | Control socket for the Hypervisor so that a MicroVM can be shutdown cleanly |
| `microvm.user` | (qemu only) User account which Qemu will switch to when started as root |
| `microvm.forwardPorts` | (qemu user-networking only) TCP/UDP port forwarding |
+| `microvm.vfkit.extraArgs` | (vfkit only) Extra arguments to pass to vfkit |
+| `microvm.vfkit.logLevel` | (vfkit only) Log level: "debug", "info", or "error" (default: "info") |
+| `microvm.vfkit.rosetta.enable` | (vfkit only) Enable Rosetta for running x86_64 binaries on ARM64 (Apple Silicon only) |
| `microvm.kernelParams` | Like `boot.kernelParams` but will not end up in `system.build.toplevel`, saving you rebuilds |
| `microvm.storeOnDisk` | Enables the store on the boot squashfs even in the presence of a share with the host's `/nix/store` |
| `microvm.writableStoreOverlay` | Optional string of the path where all writes to `/nix/store` should go to. |
diff --git a/doc/src/shares.md b/doc/src/shares.md
index 290d71d6..53a60122 100644
--- a/doc/src/shares.md
+++ b/doc/src/shares.md
@@ -10,7 +10,7 @@ In `microvm.shares` elements the `proto` field allows either of two
values:
- `9p` (default) is built into many hypervisors, allowing you to
- quickly share a directory tree
+ quickly share a directory tree. Not supported by vfkit on macOS.
- `virtiofs` requires a separate virtiofsd service which is started as
a prerequisite when you start MicroVMs through a systemd service
@@ -21,6 +21,9 @@ values:
Expect `virtiofs` to yield better performance over `9p`.
+ **Note:** vfkit (macOS) has built-in virtiofs support and does not
+ require a separate virtiofsd service.
+
```nix
microvm.shares = [ {
proto = "virtiofs";
diff --git a/doc/src/vfkit-rosetta.md b/doc/src/vfkit-rosetta.md
new file mode 100644
index 00000000..bc987284
--- /dev/null
+++ b/doc/src/vfkit-rosetta.md
@@ -0,0 +1,78 @@
+# Using Rosetta with vfkit on Apple Silicon
+
+Rosetta support enables running x86_64 (Intel) binaries in your ARM64 Linux VM on Apple Silicon Macs. This is useful for running legacy applications or development tools that haven't been ported to ARM yet.
+
+## Requirements
+
+- Apple Silicon (M1/M2/M3/etc.) Mac
+- macOS with Rosetta installed
+- vfkit hypervisor
+
+## Configuration
+
+Enable Rosetta in your MicroVM configuration:
+
+```nix
+{
+ microvm = {
+ hypervisor = "vfkit";
+
+ vfkit.rosetta = {
+ enable = true;
+ # Optional: install Rosetta automatically if missing
+ install = true;
+ # Optional: customize mount point (defaults to /run/rosetta)
+ mountPoint = "/run/rosetta";
+ };
+ };
+}
+```
+
+The NixOS module automatically handles mounting the Rosetta virtiofs share and configuring binfmt to use Rosetta for x86_64 binaries. No additional guest configuration is required.
+
+## Usage
+
+Once configured, you can run any x86_64 binary in your ARM64 VM. To verify Rosetta is working:
+
+```nix
+# Add an x86_64 package to your configuration
+environment.systemPackages = with pkgs; [
+ file # to verify binary architecture
+ (pkgsCross.gnu64.hello) # x86_64 version of hello
+];
+```
+
+Then in the VM:
+
+```bash
+# Verify you're running on ARM64
+uname -m
+# Output: aarch64
+
+# Check the binary architecture
+file $(which hello)
+# Output: ELF 64-bit LSB executable, x86-64, ...
+
+# Run the x86_64 binary via Rosetta
+hello
+# Output: Hello, world!
+```
+
+You can use `pkgsCross.gnu64.` to cross-compile any package from nixpkgs to x86_64 and run it via Rosetta.
+
+## Options Reference
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `microvm.vfkit.rosetta.enable` | bool | `false` | Enable Rosetta support |
+| `microvm.vfkit.rosetta.mountTag` | string | `"rosetta"` | Mount tag for the virtiofs share |
+| `microvm.vfkit.rosetta.mountPoint` | string | `"/run/rosetta"` | Directory where Rosetta will be mounted in the guest |
+| `microvm.vfkit.rosetta.install` | bool | `false` | Auto-install Rosetta if missing |
+| `microvm.vfkit.rosetta.ignoreIfMissing` | bool | `false` | Continue if Rosetta unavailable |
+
+## Limitations
+
+- Only works on Apple Silicon Macs (M-series chips)
+- vfkit will fail to start on Intel Macs if Rosetta is enabled
+- Performance is slower than native ARM64 execution
+- Not all x86_64 binaries may work perfectly
diff --git a/flake.nix b/flake.nix
index 23ee97c7..4a98b3ec 100644
--- a/flake.nix
+++ b/flake.nix
@@ -106,18 +106,23 @@
};
} //
# wrap self.nixosConfigurations in executable packages
- builtins.foldl' (result: systemName:
- let
- nixos = self.nixosConfigurations.${systemName};
- name = builtins.replaceStrings [ "${system}-" ] [ "" ] systemName;
- inherit (nixos.config.microvm) hypervisor;
- in
- if nixos.pkgs.stdenv.hostPlatform.system == nixpkgs.lib.replaceString "-darwin" "-linux" system
- then result // {
- "${name}" = nixos.config.microvm.runner.${hypervisor};
+ nixpkgs.lib.listToAttrs (
+ nixpkgs.lib.concatMap (configName:
+ let
+ config = self.nixosConfigurations.${configName};
+ packageName = nixpkgs.lib.replaceString "${system}-" "" configName;
+ # Check if this config's guest system matches our current build system
+ # (accounting for darwin hosts building linux guests)
+ guestSystem = config.pkgs.stdenv.hostPlatform.system;
+ buildSystem = nixpkgs.lib.replaceString "-darwin" "-linux" system;
+ in
+ nixpkgs.lib.optional (guestSystem == buildSystem)
+ {
+ name = packageName;
+ value = config.config.microvm.runner.${config.config.microvm.hypervisor};
}
- else result
- ) {} (builtins.attrNames self.nixosConfigurations);
+ ) (builtins.attrNames self.nixosConfigurations)
+ );
# Takes too much memory in `nix flake show`
# checks = import ./checks { inherit self nixpkgs system; };
@@ -151,12 +156,29 @@
nixosConfigurations =
let
+ inherit (nixpkgs.lib) map;
+
hypervisorsWith9p = [
"qemu"
# currently broken:
# "crosvm"
];
- hypervisorsWithUserNet = [ "qemu" "kvmtool" ];
+ hypervisorsWithUserNet = [ "qemu" "kvmtool" "vfkit" ];
+ hypervisorsDarwinOnly = [ "vfkit" ];
+ # Hypervisors that work on darwin (qemu via HVF, vfkit natively)
+ hypervisorsOnDarwin = [ "qemu" "vfkit" ];
+ hypervisorsWithTap = builtins.filter
+ # vfkit supports networking, but does not support tap
+ (hv: hv != "vfkit")
+ self.lib.hypervisorsWithNetwork;
+
+ isDarwinOnly = hypervisor: builtins.elem hypervisor hypervisorsDarwinOnly;
+ isDarwinSystem = system: nixpkgs.lib.hasSuffix "-darwin" system;
+ hypervisorSupportsSystem = hypervisor: system:
+ if isDarwinSystem system
+ then builtins.elem hypervisor hypervisorsOnDarwin
+ else !(isDarwinOnly hypervisor);
+
makeExample = { system, hypervisor, config ? {} }:
nixpkgs.lib.nixosSystem {
system = nixpkgs.lib.replaceString "-darwin" "-linux" system;
@@ -172,12 +194,21 @@
nixpkgs.overlays = [ self.overlay ];
microvm = {
inherit hypervisor;
- # share the host's /nix/store if the hypervisor can do 9p
- shares = lib.optional (builtins.elem hypervisor hypervisorsWith9p) {
- tag = "ro-store";
- source = "/nix/store";
- mountPoint = "/nix/.ro-store";
- };
+ # share the host's /nix/store if the hypervisor supports it
+ shares =
+ if builtins.elem hypervisor hypervisorsWith9p then [{
+ tag = "ro-store";
+ source = "/nix/store";
+ mountPoint = "/nix/.ro-store";
+ proto = "9p";
+ }]
+ else if hypervisor == "vfkit" then [{
+ tag = "ro-store";
+ source = "/nix/store";
+ mountPoint = "/nix/.ro-store";
+ proto = "virtiofs";
+ }]
+ else [];
# writableStoreOverlay = "/nix/.rw-store";
# volumes = [ {
# image = "nix-store-overlay.img";
@@ -206,22 +237,28 @@
config
];
};
- in
- (builtins.foldl' (results: system:
- builtins.foldl' ({ result, n }: hypervisor: {
- result = result // {
- "${system}-${hypervisor}-example" = makeExample {
- inherit system hypervisor;
- };
- } //
- nixpkgs.lib.optionalAttrs (builtins.elem hypervisor self.lib.hypervisorsWithNetwork) {
- "${system}-${hypervisor}-example-with-tap" = makeExample {
+
+ basicExamples = nixpkgs.lib.flatten (
+ map (system:
+ map (hypervisor: {
+ name = "${system}-${hypervisor}-example";
+ value = makeExample { inherit system hypervisor; };
+ shouldInclude = hypervisorSupportsSystem hypervisor system;
+ }) self.lib.hypervisors
+ ) systems
+ );
+
+ tapExamples = nixpkgs.lib.flatten (
+ map (system:
+ nixpkgs.lib.imap1 (idx: hypervisor: {
+ name = "${system}-${hypervisor}-example-with-tap";
+ value = makeExample {
inherit system hypervisor;
config = _: {
microvm.interfaces = [ {
type = "tap";
id = "vm-${builtins.substring 0 4 hypervisor}";
- mac = "02:00:00:01:01:0${toString n}";
+ mac = "02:00:00:01:01:0${toString idx}";
} ];
networking = {
interfaces.eth0.useDHCP = true;
@@ -233,9 +270,14 @@
};
};
};
- };
- n = n + 1;
- }) results self.lib.hypervisors
- ) { result = {}; n = 1; } systems).result;
+ shouldInclude = builtins.elem hypervisor hypervisorsWithTap
+ && hypervisorSupportsSystem hypervisor system;
+ }) self.lib.hypervisors
+ ) systems
+ );
+
+ included = builtins.filter (ex: ex.shouldInclude) (basicExamples ++ tapExamples);
+ in
+ builtins.listToAttrs (builtins.map ({ name, value, ... }: { inherit name value; }) included);
};
}
diff --git a/lib/default.nix b/lib/default.nix
index 25223679..87b7fa29 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -8,6 +8,7 @@ rec {
"kvmtool"
"stratovirt"
"alioth"
+ "vfkit"
];
hypervisorsWithNetwork = hypervisors;
diff --git a/lib/runners/vfkit.nix b/lib/runners/vfkit.nix
new file mode 100644
index 00000000..6394defb
--- /dev/null
+++ b/lib/runners/vfkit.nix
@@ -0,0 +1,147 @@
+{ pkgs
+, microvmConfig
+, macvtapFds
+, withDriveLetters
+, ...
+}:
+
+let
+ inherit (pkgs) lib;
+ inherit (vmHostPackages.stdenv.hostPlatform) system;
+ inherit (microvmConfig) vmHostPackages;
+
+ vfkit = vmHostPackages.vfkit;
+
+ inherit (microvmConfig)
+ hostName vcpu mem user interfaces volumes shares socket
+ storeOnDisk kernel initrdPath storeDisk kernelParams
+ balloon devices credentialFiles vsock graphics;
+
+ inherit (microvmConfig.vfkit) extraArgs logLevel rosetta;
+
+ volumesWithLetters = withDriveLetters microvmConfig;
+
+ # vfkit requires uncompressed kernel
+ kernelPath = "${kernel.out}/${pkgs.stdenv.hostPlatform.linux-kernel.target}";
+
+ kernelConsole = if graphics.enable then "tty0" else "hvc0";
+
+ kernelCmdLine = [ "console=${kernelConsole}" "reboot=t" "panic=-1" ] ++ kernelParams;
+
+ bootloaderArgs = [
+ "--bootloader"
+ "linux,kernel=${kernelPath},initrd=${initrdPath},cmdline=\"${builtins.concatStringsSep " " kernelCmdLine}\""
+ ];
+
+ deviceArgs =
+ [
+ "--device" "virtio-rng"
+ ]
+ ++ (if graphics.enable then [
+ "--device" "virtio-gpu"
+ "--device" "virtio-input,keyboard"
+ "--device" "virtio-input,pointing"
+ ] else [
+ "--device" "virtio-serial,stdio"
+ ])
+ ++ (builtins.concatMap ({ image, ... }: [
+ "--device" "virtio-blk,path=${image}"
+ ]) volumesWithLetters)
+ ++ (builtins.concatMap ({ proto, source, tag, ... }:
+ if proto == "virtiofs" then [
+ "--device" "virtio-fs,sharedDir=${source},mountTag=${tag}"
+ ]
+ else
+ throw "vfkit does not support ${proto} share. Use proto = \"virtiofs\" instead."
+ ) shares)
+ ++ (builtins.concatMap ({ type, id, mac, ... }:
+ if type == "user" then [
+ "--device" "virtio-net,nat,mac=${mac}"
+ ]
+ else if type == "bridge" then
+ throw "vfkit bridge networking requires vmnet-helper which is not yet implemented. Use type = \"user\" for NAT networking."
+ else
+ throw "vfkit does not support ${type} networking on macOS. Use type = \"user\" for NAT networking."
+ ) interfaces)
+ ++ lib.optionals rosetta.enable (
+ let
+ rosettaArgs = builtins.concatStringsSep "," (
+ [ "rosetta" "mountTag=${rosetta.mountTag}" ]
+ ++ lib.optional rosetta.install "install"
+ ++ lib.optional rosetta.ignoreIfMissing "ignoreIfMissing"
+ );
+ in
+ [ "--device" rosettaArgs ]
+ );
+
+ allArgsWithoutSocket = [
+ "${lib.getExe vfkit}"
+ "--cpus" (toString vcpu)
+ "--memory" (toString mem)
+ ]
+ ++ lib.optionals (logLevel != null) [
+ "--log-level" logLevel
+ ]
+ ++ lib.optionals graphics.enable [
+ "--gui"
+ ]
+ ++ bootloaderArgs
+ ++ deviceArgs
+ ++ extraArgs;
+
+in
+{
+ tapMultiQueue = false;
+
+ preStart = lib.optionalString (socket != null) ''
+ rm -f ${socket}
+ '';
+
+ command =
+ if !vmHostPackages.stdenv.hostPlatform.isDarwin
+ then throw "vfkit only works on macOS (Darwin). Current host: ${system}"
+ else if vmHostPackages.stdenv.hostPlatform.isAarch64 != pkgs.stdenv.hostPlatform.isAarch64
+ then throw "vfkit requires matching host and guest architectures. Host: ${system}, Guest: ${pkgs.stdenv.hostPlatform.system}"
+ else if user != null
+ then throw "vfkit does not support changing user"
+ else if balloon
+ then throw "vfkit does not support memory ballooning"
+ else if rosetta.enable && !vmHostPackages.stdenv.hostPlatform.isAarch64
+ then throw "Rosetta requires Apple Silicon (aarch64-darwin). Current host: ${system}"
+ else if devices != []
+ then throw "vfkit does not support device passthrough"
+ else if credentialFiles != {}
+ then throw "vfkit does not support credentialFiles"
+ else if vsock.cid != null
+ then throw "vfkit vsock support not yet implemented in microvm.nix"
+ else if storeOnDisk
+ then throw "vfkit does not support storeOnDisk. Use virtiofs shares instead (already configured in examples)."
+ else
+ let
+ baseCmd = lib.escapeShellArgs allArgsWithoutSocket;
+ vfkitCmd = lib.concatStringsSep " " (map lib.escapeShellArg allArgsWithoutSocket);
+ in
+ # vfkit requires absolute socket paths, so expand relative paths
+ if socket != null
+ then "bash -c ${lib.escapeShellArg ''
+ SOCKET_ABS=${lib.escapeShellArg socket}
+ [[ "$SOCKET_ABS" != /* ]] && SOCKET_ABS="$PWD/$SOCKET_ABS"
+ exec ${vfkitCmd} --restful-uri "unix:///$SOCKET_ABS"
+ ''}"
+ else baseCmd;
+
+ canShutdown = socket != null;
+
+ shutdownCommand =
+ if socket != null
+ then ''
+ SOCKET_ABS="${lib.escapeShellArg socket}"
+ [[ "$SOCKET_ABS" != /* ]] && SOCKET_ABS="$PWD/$SOCKET_ABS"
+ echo '{"state": "Stop"}' | ${vmHostPackages.socat}/bin/socat - "UNIX-CONNECT:$SOCKET_ABS"
+ ''
+ else throw "Cannot shutdown without socket";
+
+ supportsNotifySocket = false;
+
+ requiresMacvtapAsFds = false;
+}
diff --git a/nixos-modules/microvm/default.nix b/nixos-modules/microvm/default.nix
index 4752fdf4..7fa27df5 100644
--- a/nixos-modules/microvm/default.nix
+++ b/nixos-modules/microvm/default.nix
@@ -19,6 +19,7 @@ in
./pci-devices.nix
./virtiofsd
./graphics.nix
+ ./rosetta.nix
./optimization.nix
./ssh-deploy.nix
];
diff --git a/nixos-modules/microvm/options.nix b/nixos-modules/microvm/options.nix
index 4aab449f..9431b357 100644
--- a/nixos-modules/microvm/options.nix
+++ b/nixos-modules/microvm/options.nix
@@ -590,6 +590,65 @@ in
description = "Custom CPU template passed to firecracker.";
};
+ vfkit.extraArgs = mkOption {
+ type = with types; listOf str;
+ default = [];
+ description = "Extra arguments to pass to vfkit.";
+ };
+
+ vfkit.logLevel = mkOption {
+ type = with types; nullOr (enum ["debug" "info" "error"]);
+ default = "info";
+ description = "vfkit log level.";
+ };
+
+ vfkit.rosetta = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Enable Rosetta support for running x86_64 binaries in ARM64 Linux VMs.
+ Only works on Apple Silicon (ARM) Macs.
+
+ When enabled, the Rosetta virtiofs share will be automatically mounted
+ and binfmt will be configured to use Rosetta for x86_64 binaries.
+ '';
+ };
+
+ mountTag = mkOption {
+ type = types.str;
+ default = "rosetta";
+ description = "Mount tag for the Rosetta virtiofs share.";
+ };
+
+ install = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Automatically install Rosetta if missing.
+ If false and Rosetta is not installed, vfkit will fail to start.
+ '';
+ };
+
+ ignoreIfMissing = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Continue execution even if Rosetta installation fails or is unavailable.
+ Useful for configurations that should work on both ARM and Intel Macs.
+ '';
+ };
+
+ mountPoint = mkOption {
+ type = types.str;
+ default = "/run/rosetta";
+ description = ''
+ Directory where the Rosetta virtiofs share will be mounted in the guest.
+ The Rosetta binary will be available at {mountPoint}/rosetta.
+ '';
+ };
+ };
+
prettyProcnames = mkOption {
type = types.bool;
default = true;
diff --git a/nixos-modules/microvm/rosetta.nix b/nixos-modules/microvm/rosetta.nix
new file mode 100644
index 00000000..de5bf448
--- /dev/null
+++ b/nixos-modules/microvm/rosetta.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+let
+ cfg = config.microvm.vfkit.rosetta;
+in
+lib.mkIf (config.microvm.hypervisor == "vfkit" && cfg.enable) {
+ # Mount the Rosetta share
+ fileSystems.${cfg.mountPoint} = {
+ device = cfg.mountTag;
+ fsType = "virtiofs";
+ };
+
+ # Configure binfmt to use Rosetta for x86_64 binaries
+ boot.binfmt.registrations.rosetta = {
+ interpreter = "${cfg.mountPoint}/rosetta";
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+}