OSX – Run Mac in a Docker container

If you see folders, then it worked. You can restart Docker, or just reboot if you want to be sure.

--build-arg SHORTNAME=high-sierra 
--build-arg SHORTNAME=mojave
--build-arg SHORTNAME=catalina
--build-arg SHORTNAME=big-sur
--build-arg SHORTNAME=monterey


  • High Sierra
  • Mojave
  • Catalina
  • Big Sur
  • Monterey
  • Auto (pre-made Catalina)
  • Naked (use your own .img)
  • Naked-Auto (user your own .img and SSH in)

High Sierra:




Monterey make your own image:

Pre-made Catalina system by Sick.Codes: username: user, password: alpine

Naked: Bring-your-own-image setup (use any of the above first):

Naked Auto: same as above but with -e USERNAME & -e PASSWORD and -e OSX_COMMANDS="put your commands here"

  • usbfluxd!
  • macOS Monterey VM on Linux!
  • Folder sharing-
  • USB passthrough (hotplug too)
  • SSH enabled (localhost:50922)
  • VNC enabled (localhost:8888) if using ./vnc version
  • iMessage security research via serial number generator!
  • X11 forwarding is enabled
  • runs on top of QEMU + KVM
  • supports Big Sur, custom images, Xvfb headless mode
  • you can clone your container with docker commit

helm directory.

Thanks, cephasara for contributing this major contribution.

Artifact HUB

issue, should you come across minor issues with running Docker-OSX or have any questions.

closed issues and confirm that you’re using the latest version of this repository — your issues may have already been resolved! You might also see your answer in our questions and answers section below.


@sickcodes on Twitter or click here.

GPL v3+. Contributions are welcomed and immensely appreciated. You are in fact permitted to use Docker-OSX as a tool to create proprietary software.

Is Hackintosh, OSX-KVM, or Docker-OSX legal?

Product names, logos, brands and other trademarks referred to within this project are the property of their respective trademark holders. These trademark holders are not affiliated with our repository in any capacity. They do not sponsor or endorse this project in any way.


This will install Ubuntu by default.

wsl --install

You can confirm WSL2 is enabled using wsl -l -v in PowerShell. To see other distributions that are available, use wsl -l -o.

If you have previously installed WSL1, upgrade to WSL 2. Check this link to upgrade from WSL1 to WSL2.

After WSL installation, go to C:/Users/<Your_Name>/.wslconfig and add nestedVirtualization=true to the end of the file (If the file doesn’t exist, create it). You may need to select “Show Hidden Files” and “Show File Extensions” in the File Explorer options. The result should be like this:


Go into your WSL distro (Run wsl in PowerShell) and check if KVM is enabled by using the kvm-ok command. The output should look like this:

INFO: /dev/kvm exists
KVM acceleration can be used

Now download and install Docker for Windows if it is not already installed.

After installation, go into Settings and check these 2 boxes:

General -> "Use the WSL2 based engine";
Resources -> WSL Integration -> "Enable integration with my default WSL distro", 

Ensure x11-apps is installed. Use the command sudo apt install x11-apps -y to install it if it isn’t.

Finally, there are 3 ways to get video output:

  • WSLg: This is the simplest and easiest option to use. There may be some issues such as the keyboard not being fully passed through or seeing a second mouse on the desktop – Issue on WSLg – but this option is recommended.

To use WSLg’s built-in X-11 server, change these two lines in the docker run command to point Docker-OSX to WSLg.

-e "DISPLAY=${DISPLAY:-:0.0}" \
-v /mnt/wslg/.X11-unix:/tmp/.X11-unix \

Or try:

-e "DISPLAY=${DISPLAY:-:0}" \
-v /mnt/wslg/.X11-unix:/tmp/.X11-unix \

For Ubuntu 20.x on Windows, see #458

  • VNC: See the VNC section for more information. You could also add -vnc argument to qemu. Connect to your mac VM via a VNC Client. Here is a how to
  • Desktop Environment: This will give you a full desktop Linux experience but it will use a bit more of the computer’s resources. Here is an example guide, but there are other guides that help set up a desktop environment. DE Example

closed issues. Someone else might have gotten a question as yours answered already even if you can’t find it in this document!



here, here.

For example (below) the buff/cache already contains 20 Gigabytes of allocated RAM:

[user@hostname ~]$ free -mh
               total        used        free      shared  buff/cache   available
Mem:            30Gi       3.5Gi       7.0Gi       728Mi        20Gi        26Gi
Swap:           11Gi          0B        11Gi

Clear the buffer and the cache:

sudo tee /proc/sys/vm/drop_caches <<< 3

Now check the RAM again:

[user@hostname ~]$ free -mh
               total        used        free      shared  buff/cache   available
Mem:            30Gi       3.3Gi        26Gi       697Mi       1.5Gi        26Gi
Swap:           11Gi          0B        11Gi

AppleALC, alcid and VoodooHDA-OC do not have codec support. However, IORegistryExplorer does show the controller component working.

docker run \
    --device /dev/kvm \
    -e AUDIO_DRIVER=pa,server=unix:/tmp/pulseaudio.socket \
    -v "/run/user/$(id -u)/pulse/native:/tmp/pulseaudio.socket" \
    -v /tmp/.X11-unix:/tmp/.X11-unix \

here and here and here.

cause the host to leak your IP, even if you’re using a VPN in the container.

However, if you’re trying to connect to an instance of Docker-OSX remotely (e.g. an instance of Docker-OSX hosted in a data center), this may improve your performance:

# enable for current session
sudo sysctl -w net.ipv4.ip_forward=1

# OR
# sudo tee /proc/sys/net/ipv4/ip_forward <<< 1

# enable permanently
sudo touch /etc/sysctl.conf
sudo tee -a /etc/sysctl.conf <<EOF
net.ipv4.ip_forward = 1

# or edit manually with the editor of your choice
nano /etc/sysctl.conf || vi /etc/sysctl.conf || vim /etc/sysctl.conf

# now reboot

Source & Explanation

Give permissions to the shared folder for the anonuid and anongid, where anonuid and anongid matches that of your Linux user; id -u

id -u ; id -g will print userid:groupid

chown 1000:985 /srv/nfs/share
chmod u+rwx /srv/nfs/share

Start the Docker-OSX container with the additional flag --network host

Create and mount the nfs folder from the mac terminal:

mkdir -p ~/mnt
sudo mount_nfs -o locallocks ~/mnt

docker build -t docker-osx:latest \
    --build-arg RANKMIRRORS=true \
    --build-arg MIRROR_COUNTRY=US \
    --build-arg MIRROR_COUNT=10 \
    --build-arg SHORTNAME=catalina \
    --build-arg SIZE=200G .

@localhost -p 50922 'ping'

# check your serial number
sshpass -p 'alpine' ssh user@localhost -p 50922 'ioreg -l | grep IOPlatformSerialNumber'

Click here to see those values for no reason.

  • This script will generate serial numbers, with Mac Addresses, plus output to CSV/TSV, plus make a bootdisk image.

You can create hundreds, ./custom/ --help

./custom/ \
    --count 1 \
    --tsv ./serial.tsv \
    --bootdisks \
    --output-bootdisk OpenCore.qcow2 \

Or if you have some specific serial numbers…

  • \
    --model "${DEVICE_MODEL}" \
    --serial "${SERIAL}" \
    --board-serial "${BOARD_SERIAL}" \
    --uuid "${UUID}" \
    --mac-address "${MAC_ADDRESS}" \
    --output-bootdisk OpenCore-nopicker.qcow2

Instead of mounting that disk, Docker-OSX will generate a new OpenCore.qcow2 by using this one cool trick:

-e WIDTH=800 \
-e HEIGHT=600 \

To use WIDTH/HEIGHT, you must use with either -e GENERATE_UNIQUE=true or -e GENERATE_SPECIFIC=true.

It will take around 30 seconds longer to boot because it needs to make a new boot partition using libguestfs.

-e WIDTH=1920 \
-e HEIGHT=1080 \
-e SERIAL="" \
-e UUID="" \

here for more details.

# hostbus and hostport correspond to the numbers from lsusb
# runs in privileged mode to enable access to the usb devices.
docker run \
  --privileged \
  --device /dev/kvm \
  -e RAM=4 \
  -p 50922:10022 \
  -e "DISPLAY=${DISPLAY:-:0.0}" \
  -e EXTRA="-device virtio-serial-pci -device usb-host,hostbus=1,hostport=2" \

You should see the device show up when you do system_profiler SPUSBDataType in the MacOS shell.

Important Note: this will cause the host system to lose access to the USB device while the VM is running!

-e OSX_COMMANDS lets you run any commands inside the container

docker pull sickcodes/docker-osx:auto

# boot to OS X shell + display + specify commands to run inside OS X!
docker run -it \
    --device /dev/kvm \
    -p 50922:10022 \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e "DISPLAY=${DISPLAY:-:0.0}" \
    -e "OSX_COMMANDS=/bin/bash -c \"put your commands here\"" \

# Boots in a minute or two!

OR if you have an image already and just want to log in and execute arbitrary commands:

docker pull sickcodes/docker-osx:naked-auto

# boot to OS X shell + display + specify commands to run inside OS X!
docker run -it \
    --device /dev/kvm \
    -p 50922:10022 \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e "DISPLAY=${DISPLAY:-:0.0}" \
    -e USERNAME=yourusername \
    -e PASSWORD=yourpassword \
    -e "OSX_COMMANDS=/bin/bash -c \"put your commands here\"" \

# Boots in a minute or two!

This is particularly helpful for CI/CD pipelines.

# run your own image headless + SSH
docker run -it \
    --device /dev/kvm \
    -p 50922:10022 \
    -v "${PWD}/mac_hdd_ng.img:/image" \

[email protected] -L 5999:, where is your remote server IP.

(Note: if you close port 5999 and use the SSH tunnel, this becomes secure.)

[email protected] -L 5999:, where is your remote server IP and is your LAN container IP.

Now you can direct connect VNC to any container built with this command!

spice manual for help setting up authenticated access (“Ticketing”).

  docker run \
    --device /dev/kvm \
    -p 3001:3001 \
    -p 50922:10022 \
    -e "DISPLAY=${DISPLAY:-:0.0}" \
    -e EXTRA="-monitor telnet::45454,server,nowait -nographic -serial null -spice disable-ticketing,port=3001" \

Then simply do remote-viewer spice://localhost:3001 and add --spice-debug for debugging.

@localhost -p 50922

We have future plans for development around this.

Source link

Related Articles

Leave a Reply

Your email address will not be published.

Back to top button