手把手用Docker在Linux跑macOS虚拟机
本教程教你在Linux服务器上用Docker快速部署macOS虚拟机,支持图形界面、SSH连接和目录共享,15分钟搭建可持久化的开发/测试环境,无需购买Mac设备。

手把手用Docker在Linux跑macOS虚拟机
作为后端开发,我平时碰到的需求大多是写Java微服务、搞CI/CD。但上个月团队要打包一个macOS专属的客户端,还得做iOS安全测试——我手头只有Linux服务器。难道要我临时买台Mac?
后来我在GitHub上摸到了 Docker-OSX 这个项目(5.2万星,至今仍在活跃更新)。它的核心思路很直接:在Docker容器里跑QEMU + KVM,把macOS虚拟机包起来,支持X11图形转发和SSH。也就是说,你不需要买Mac,只要有一台支持虚拟化的Linux机器,就能获得一个可交互的macOS环境。
这篇文章带你一步步从零跑起来,最后你会得到:
- 一个带图形界面的macOS容器
- 通过SSH连入容器执行命令的能力
- 一套可持久保存、可复用的macOS工作流
话不多说,开干。
一、前置条件:跑起来之前需要准备什么
这个项目不是纯软件层面的「拉个镜像就行」,它底层依赖 KVM硬件虚拟化。所以你需要确认以下几件事:
- CPU支持虚拟化:在BIOS里开启Intel VT-x或AMD-V。大部分主板默认关闭,需要进去改。
- 一台x86_64的Linux机器:ARM架构不行,这个项目跑的是Intel版macOS。
- 至少20GB磁盘空间:如果要装Xcode,建议准备50GB+。
- 至少4GB内存:macOS本身就是内存大户,跑容器时建议主机留足余量。
确认好后,先在主机上装好QEMU和libvirt依赖。不同发行版命令不同,这里挑两个常见的:
bash
## Ubuntu / Debian
sudo apt install qemu qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager libguestfs-tools
## 启用libvirt并加载KVM模块
sudo systemctl enable --now libvirtd
sudo systemctl enable --now virtlogd
echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs
sudo modprobe kvm
为什么这样做?
ignore_msrs这个参数告诉KVM忽略某些MSR(Model-Specific Register)读写异常,这是跑macOS虚拟机必须的。libvirt则负责管理虚拟化资源。
装完后用 kvm-ok 验证一下,看到 KVM acceleration can be used 就说明环境没问题。
二、快速上手:一条命令拉起macOS
环境就绪后,我们就可以直接拉镜像启动了。Docker-OSX官方提供了多个macOS版本的镜像(从High Sierra 10.13到最新的Tahoe 16),我们先挑一个 Catalina (10.15) 试水:
bash
docker run -it \
--device /dev/kvm \
-p 50922:10022 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e "DISPLAY=${DISPLAY:-:0.0}" \
-e SHORTNAME=catalina \
sickcodes/docker-osx:latest
逐行拆解,让你知道到底在干什么:
| 参数 | 作用 |
|---|---|
--device /dev/kvm |
把宿主机的KVM设备直通给容器,这是性能保证 |
-p 50922:10022 |
端口映射,容器内SSH端口是10022,映射到宿主50922 |
-v /tmp/.X11-unix:/tmp/.X11-unix |
挂载X11 socket,让容器内的GUI能显示在你宿主机上 |
-e DISPLAY=... |
告诉容器图形输出到哪个display |
-e SHORTNAME=catalina |
指定macOS版本,也可以换成 big-sur、ventura、sonoma 等 |
为什么X11能跨容器显示? Linux的X Window System使用socket文件
/tmp/.X11-unix来通信。把这个socket挂载进容器后,容器里的进程就能把窗口绘制指令发回宿主机的X Server,你就直接看到界面了。
运行后首次启动会进入安装界面,流程是:
- 选
Disk Utility - 擦除最大的那个磁盘(约200GB)
- 选
Reinstall macOS,系统会自动下载安装 - 中间会重启几次,耐心等
默认用户名 user,密码 alpine。
三、实战示例:搭建可持久保存的macOS开发环境
光跑起来还不够,我们需要一个能保存状态、下次接着用的环境。这就要用上 naked模式——把自己的 .img 磁盘镜像挂进去。
第一步:先跑一次标准流程,装好系统
按上面第二步的流程走完,macOS安装完毕后进入桌面。
第二步:把磁盘镜像从容器里拷出来
bash
## 找到容器内磁盘镜像的位置
sudo find /var/lib/docker -name "mac_hdd_ng*.img" -size +10G
## 假设找到的是 /var/lib/docker/.../mac_hdd_ng.img,拷到当前目录
sudo cp /var/lib/docker/overlay2/.../mac_hdd_ng.img ./mac_hdd_ng_auto.img
第三步:用naked模式加载你自己的镜像
bash
docker run -it \
--device /dev/kvm \
-p 50922:10022 \
-v "${PWD}/mac_hdd_ng_auto.img:/image" \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e "DISPLAY=${DISPLAY:-:0.0}" \
-e NOPICKER=true \
sickcodes/docker-osx:naked
-e NOPICKER=true跳过启动时的磁盘选择菜单,直接进系统。如果你不想每次都手动选盘,就加这个参数。
第四步:通过SSH连进去
打开新终端:
bash
ssh user@localhost -p 50922
## 密码: alpine
连上后你可以干任何能在一台Mac上干的事:装Homebrew、跑Xcode、执行Shell脚本。我自己的习惯是连SSH后用命令操作,图形界面留着看效果。
第五步:目录共享(宿主机 ↔ macOS)
开发场景下经常要在宿主机和macOS之间传文件,最安全方便的方式是 sshfs:
bash
## 在Linux宿主机上
mkdir ~/mnt/osx
sshfs user@localhost: -p 50922 ~/mnt/osx
执行后 ~/mnt/osx 就会自动挂载macOS的根文件系统,像操作本地文件夹一样读写。如果SSH需要自动输密码,装上 sshpass 后一条命令搞定:
bash
sshpass -p alpine sshfs user@localhost:/ -p 50922 ~/mnt/osx
四、常见问题 / 踩坑提醒
1. LibGTK报错 / 图形窗口弹不出来
bash
## 检查DISPLAY变量
echo $DISPLAY
## 如果报错,装xhost并放行
sudo apt install x11-xserver-utils
xhost +
2. 内存不足告警
默认分配3GB RAM(-e RAM=3)。如果你报 Cannot allocate memory,说明宿主机buff/cache太多。清一下缓存:
bash
sudo tee /proc/sys/vm/drop_caches <<< 3
3. Docker守护进程没启动
bash
## 确认docker是否在跑
pgrep dockerd
## 没跑就启动
sudo systemctl enable --now docker
4. CPU核心分配
默认核心数可能不够用,你可以用 -e EXTRA='-smp 8,sockets=4,cores=2' 分配更多核心。注意:CPU是共享的,你可以把主机的核心全部分配给容器,不影响物理机。
5. Windows用户想用?
可以,但需要Windows 11 + WSL2,并在 .wslconfig 里开启 nestedVirtualization=true。X11输出用WSLg最方便。
五、总结
跟着这篇文章走一遍,你应该已经:
- ✅ 装了KVM + QEMU依赖
- ✅ 拉起了一台Catalina macOS容器
- ✅ 把磁盘镜像持久化到本地,下次可复用
- ✅ 学会SSH连接和目录共享
这只是一套macOS沙箱的起点。后续你可以:
- 给容器生成唯一序列号,用于iMessage / iCloud安全研究(项目自带
GENERATE_UNIQUE=true参数) - 配置headless模式跑CI/CD流水线
- 搭VNC远程访问
- 调整分辨率(
-e WIDTH=1920 -e HEIGHT=1080)
有5万多人给这个项目标了星,说明需求是真的大。与其花8000块买台二手Mac mini,不如花15分钟在服务器上跑一个Docker容器。遇到问题去项目的Discord或Telegram频道问,社区很活跃。
我是周小码,8年Java后端,平时也折腾开源和AI。这篇教程如果能帮你省下一台设备的钱,或者帮你搞定跨平台开发的痛点,就值了。有什么坑,评论区随时交流。