Skip to content

s-u/macosvm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

macosvm

macosvm is a command line tool which allows creating and running of virtual machines on macOS 12 (Monterey) and higher using the new Virtualization framework. It has been primarily developed for running macOS guest opearting systems inside virtual machines on M1-based Macs (arm64) with macOS hosts to support CI/CD such as GitHub M1-based runners and our R builds.

Download

See releases for downloads of released binaries (arm64 macOS 12 and higher only). See NEWS for latest changes.

Build

The project can be built either with xcodebuild or make. The former requires Xcode installation while the latter only requires command line tools (see xcode-select --install).

Quick Start

The tool requires at least macOS 12 (Monterey) since that is the first system implementing the necessary pieces of the Virtualization framework. To create a macOS guest VM you need the following steps:

## Download the desired macOS ipsw image, e.g.:
curl -LO https://updates.cdn-apple.com/2021FCSFall/fullrestores/002-23780/D3417F21-41BD-4DDF-9135-FA5A129AF6AF/UniversalMac_12.0.1_21A559_Restore.ipsw

## create a new VM with 32Gb disk image and install macOS 12:
macosvm --disk disk.img,size=32g --aux aux.img --restore UniversalMac_12.0.1_21A559_Restore.ipsw vm.json

## start the created image with GUI window:
macosvm -g vm.json

A full list of ipsw images for all versions of macOS is availabe here.

After your started the VM it will go through the Apple Setup Assistant - you need the GUI to get through that. Once done, I strongly recommend going to Sharing system preferences, setting a unique name and enabling Remote Login and Screen Sharing. Then you can shut down the VM (using Shut Down in the macOS guest). Note that the default is to use NAT networking and your VM will show up on your host's network (details below) so you can use Finder to connect to its screen even if you start without the GUI.

After the minimal setup it ts possible to create a "clone" of your image to keep for later, e.g.:

cp -c disk.img master.img

Note the -c flag which will make a copy-on-write clone, i.e., the cloned image doesn't use any actual space on disk (if you use APFS). This allows you to store different modifications of your base operating system without duplicating storage space. See also the --ephemeral option of macosvm if you don't want to persist any changes made while the VM runs.

See macosvm -h for a minimal help page. Note that this is an experimental expert tool. There is a lot of debugging output, errors include stack traces etc - this is intentional at this point, nothing horrible is happening, but you may need to read more text than you want to on errors.

See the Wiki for more tips and information.

Details

Each virtual machine requires one auxiliary storage (specified with --aux) and at least one root device (specified with --disk). You can specify multiple disk images for additional devices. The --disk option has the form --disk <file>[,<option>[,<option>...]]. Valid options are: ro = read-only device, size=<spec> allocate empty disk with that size. The <spec> argument allows k, m and g suffix for powers of 1024 so 32g means 32*(1024^3).

You can specify the number of CPUs with -c <cpus> and the available memory (RAM) with -r <spec>. If not specified, --restore uses the image's minimal requirements (for macOS 12 that is 2 CPUs and 4Gb RAM).

During the macOS installation (--restore) step a unique machine identifier (ECID) is generated. The resulting images only work with that one identifier. This identifier is stored in the configuration file (as machineId entry) and the VM won't boot without it. Since macosvm version 0.1-2 this information can be extracted from the auxiliary file if a configuration file is not present, but that is a hack that may not work with future macOS versions.

This is what the configuration file looks like generated by the above example:

{
  "hardwareModel":"YnBsaX[...]AAAAABt",
  "storage":[
    {"type":"disk", "file":"disk.img", "readOnly":false},
    {"type":"aux", "file":"aux.img", "readOnly":false}
  ],
  "ram":4294967296,
  "machineId":"YnBsaXN0MDDRAQJURUN[...]AAAACE=",
  "displays":[{"dpi":200, "width":2560, "height":1600}],
  "version":1,
  "cpus":2,
  "networks":[{"type":"nat"}],
  "audio":false
}

You can edit the file if you want to change the parameters (like CPUs, RAM, ...), but keep a copy in case you break something. The "hardwareModel" is just the OS/architecture spec from the image. (FWIW both hardwareModel and machineId are base64-encoded binary property lists so you can look at the payload with base64 -d | plutil -p - )

Note that the virtualization framework imposes some restrictions on what is allowed, e.g., you have to define at least one display even if you don't intend to use it (the displays entry is added automatically by --restore).

Unless run with -g/--gui the tool will run solely as a command line tool and can be run in the background. Terminating the tool terminates the VMs. However, if the GUI is used closing the window does NOT terminate the VM. Note that currently macOS guest systems don't support VM control, so even though it is in theory possible to request VM stop via the VZ framework, it is not actually honored by the macOS guest (as of 12.0.1), so you should use guest-side shutdown (e.g. sudo shutdown -h now works). When the guest OS shuts down the tool terminates with exit code 0.

Networking

The default setup is NAT networking which means your host will allocate a separate local network for the VM. You can use --net <type>[:{<mac>|<iface>}] to specify different network adapters and different types. macosvm currently implements nat, bridge and unix. bridge requires the name of the host interface to bridge to (if left blank the first interface is used). Note, however, that bridging requires a special entitlement that can only be obtained from Apple so it is not supported by "normal" binaries. Since 0.1-3 it is possible to override the MAC address of the first interface with --mac <MAC> which makes it possible to script the IP address retrieval from arp -a.

If you are not running any discovery/bonjour services in the guest to find the IPs, you can typically find the IP addresses of your VMs using arp -a. Currently macOS VMs in NAT mode will be on the interface bridge100, typically with 192.168.64.x IP address (where x=1 is the host so the other numbers are VMs). There doesn't seem to be any direct control over the networking, but apparently the guests can talk to the host and NAT out, but can't talk to each other even though they appear on the same subnet.

The unix target is useful in combination with a slirp proxy (for binaries see releases) which then allows NAT-like access in environments that are sensitive to network configuration.

File Sharing

Since macOS 13 (Ventura) VirtIOFS is supported both in the guest and host macOS. A typical use is something like --vol /Users/myself/shared:automount which will make the contents of the /Users/myself/shared directory on the host avaiable as /Volumes/My Shared Files in the guest macOS. If automount is not specified, then the guest OS has to mount the virtiofs share (via mount_virtiofs or similar) by specifying its name (tag) which defaults to macosvm, but can be set, e.g., by appending :name=mysharename.