Virby is a module for nix-darwin that configures a lightweight linux VM as a remote build machine for nix, allowing linux packages to be built on macOS. This project is modeled after nix-rosetta-builder, which provides a similar service, using lima to manage the VM. Some parts of the code in this repository are directly borrowed and adapted from that project.
Add virby to your flake inputs:
{
inputs = {
virby.url = "github:quinneden/virby-nix-darwin";
# It is important that you dont add the line:
#
# inputs.nixpkgs.follows = "nixpkgs";
#
# until after you've activated with `darwin-rebuild`. This way, the cached
# image can be used and you won't have to build from source (which requires
# an existing aarch64-linux builder).
};
outputs = { virby, ... }: {
darwinConfigurations."myHost" = {
# Import the module
modules = [ virby.darwinModules.default ];
};
};
}Important
When enabling Virby for the first time, you must add the binary cache to your Nix configuration. This ensures that the prebuilt VM image is available for download, rather than having to be built locally, which requires an existing linux builder. You can do this in one of two ways:
Add the binary cache to your configuration before enabling Virby:
{
nix.settings.extra-substituters = [ "https://virby-nix-darwin.cachix.org" ];
nix.settings.extra-trusted-public-keys = [
"virby-nix-darwin.cachix.org-1:z9GiEZeBU5bEeoDQjyfHPMGPBaIQJOOvYOOjGMKIlLo="
];
services.virby.enable = false;
}Run darwin-rebuild, then enable Virby:
{
nix.settings.extra-substituters = [ "https://virby-nix-darwin.cachix.org" ];
nix.settings.extra-trusted-public-keys = [
"virby-nix-darwin.cachix.org-1:z9GiEZeBU5bEeoDQjyfHPMGPBaIQJOOvYOOjGMKIlLo="
];
# Don't configure any other Virby options until after you've switched to the new
# configuration. If the hash for the disk image derivation doesn't match the one
# in the binary cache, then nix will try to build the image locally.
services.virby.enable = true;
}Finally, rebuild again.
OR
Run the darwin-rebuild command with the following options:
sudo darwin-rebuild switch --flake .#myHost \
--option "extra-substituters" "https://virby-nix-darwin.cachix.org" \
--option "extra-trusted-public-keys" "virby-nix-darwin.cachix.org-1:z9GiEZeBU5bEeoDQjyfHPMGPBaIQJOOvYOOjGMKIlLo="If you prefer building the image locally, you can enable the nix.linux-builder option before enabling Virby:
{
nix.linux-builder.enable = true;
services.virby.enable = false;
}- On-demand activation (optional) - VM is started when needed, then shuts down after a period of inactivity
- Rosetta support (optional) - Build
x86_64-linuxpackages on Apple Silicon using Rosetta translation - Secure by default - Host-only access via loopback (i.e.
127.0.0.1), with automatic ED25519 key generation - Fully configurable - Adjust VM resources and add custom NixOS modules
| Option | Type | Default | Description |
|---|---|---|---|
enable |
bool | false |
Enable the service |
allowUserSsh |
bool | false |
Allow non-root users to SSH into the VM |
cores |
int | 8 |
CPU cores allocated to VM |
debug |
bool | false |
Enable debug logging for the VM |
driver |
string ("vfkit" or "krunkit") | "vfkit" |
The virtualization driver used to run the VM |
diskSize |
string | "100GiB" |
VM disk size |
extraConfig |
module | {} |
Additional NixOS modules to include in the VM's system configuration |
memory |
int or string | 6144 |
Memory in MiB or string format (e.g. "6GiB") |
onDemand.enable |
bool | false |
Enable on-demand activation of the VM |
onDemand.ttl |
int | 180 |
The number of minutes of inactivity which must pass before the VM shuts down |
port |
int | 31222 |
SSH port for VM access |
rosetta |
bool | true |
Enable Rosetta support for the VM |
sharedDirectories |
attrs of string | {} |
An attribute set of directories that will be shared with the VM as virtio-fs devices |
speedFactor |
int | 1 |
Speed factor for Nix build machine |
supportDeterminateNix |
bool | false |
Enable support for using Virby with Determinate Nix |
Use krunkit instead of vfkit
By default, vfkit is used to run the VM. Optionally, you can configure Virby to use krunkit instead:
{
services.virby.driver = "krunkit";
}Unlike vfkit, krunkit does not support NAT networking, so the network is proxied via vmnet-helper.
On-demand Activation
{
services.virby.onDemand.enable = true;
services.virby.onDemand.ttl = 180; # Idle timeout in minutes
}Rosetta Support
{
services.virby.rosetta = true;
}Custom NixOS Configuration
Warning
This option allows you to arbitrarily change the NixOS configuration, which could expose the VM to security risks.
{
services.virby.extraConfig = {
inherit (config.nix) settings;
# Some NixOS options which are defined in the default VM configuration cannot
# be overridden, such as `networking.hostName`. Others may be overridden with
# `lib.mkForce`. Also note that anything changed here will cause a rebuild of
# the VM image, and SSH keys will be regenerated.
};
}Debug Options (insecure, for troubleshooting only)
{
services.virby.debug = true; # Enable verbose logging
services.virby.allowUserSsh = true; # Allow non-root SSH access with a separate shared key copy
}Virby integrates three components:
- nix-darwin Module - Configures VM as a Nix build machine for host
- VM Image - Minimal NixOS disk image configured for secure ssh access and build isolation
- VM Runner - Go package managing VM lifecycle and SSH proxying
Security model:
- VM doesn't accept remote connections as it binds to the loopback interface
- SSH keys are generated and copied to the VM on first run.
builderuser has minimal permissions, root access is restricted by default
Debug logging
{
# Enable debug logging to `/tmp/virbyd.log`
services.virby.debug = true;
}# View daemon logs
tail -f /tmp/virbyd.logSSH into VM
# Requires `allowUserSsh = true`
ssh virby-vm
# or use sudo- Inspired by nix-rosetta-builder
- Incorporates vfkit, krunkit, and vmnet-helper
License: MIT - see LICENSE file for details.