iMessage 🍎

iMessage 🍎

September 23, 2023
CS, Docker, Apple, OSX-KVM

Introduction #

I love Apple products. The build quality of their products is no doubt, exceptional; and for what they do, I love my 2020 Macbook Pro. However, I also own a variety of other electronics, which utilize not just MacOS. As an engineer, there are a number of engineering tools which I can’t run on my Macbook; as a result, my desktop runs both Windows and Linux, which I find myself using a majority of the time.

The wonderful thing about having an iPhone is, if I have a computer running MacOS, I can respond to the text from that computer. Now what if I’m not currently using my Macbook? I don’t particularly want to keep my laptop open every time I’m using my desktop, and I really dislike typing on my phone. It would be really great if I could somehow text from my desktop. If I had an android, it would be really quite easy; however I have an iPhone, and Apple does not like you using their software on hardware they did not create. This is really quite annoying.

Luckily for us, there are some wonderful groups out there who feel the exact same way. Enter Sunbird, BlueBubbles, Beeper, AirMessage, and a plethora of other messaging apps. Each app comes with it’s own set of quirks, but fundamentally they all operate by forwarding your messages from a machine running MacOS to your (non-MacOS) machine. In order to use iMessage, you need to send messages through iMessage on Apple hardware. Beeper and Sunbird are one-and-done apps, where BlueBubbles and AirMessage require you to run your own hardware.

This article will focus on the latter of the two solutions, which happens to be a bit more involved.

How to Create an iMessage Server #

The solutions offered by BlueBubbles and AirMessage require you to run your own hardware; hardware created by, or at least pretending to be, Apple.

One option, of course, is to take an old Mac or MacBook and setup the server according to the guides written by both services. This is fairly straight forward and if you have the hardware lying around, it’s an easy solution. However, for the more adventurous out there, this can also be done on a Hackintosh.

Table of Contents #

  1. Installing Prerequisites
  2. Building the Hackintosh Image
  3. Prepping the Hackintosh Server
  4. Installing the iMessage Server
  5. Daemonizing

Non-Apple Hardware #

This tutorial is a bit more involved. It requires a semi-fluent knowledge of process daemonization, docker, qemu, networking, and bash. In order to accomplish this we’re first going to build the Hackintosh, install either a BlueBubbles or AirMessage server, transfer it to the server, and daemonize the process. Let’s dive in.

⚠️ This tutorial will NOT WORK if not using a linux distro or WSL ⚠️

Installing Prerequisites #

This process is a bit complicated; a comprehensive video walk-through of this process can be found here, linked on SickCodes' repository.

The gist of it here is you need kvm enabled, and qemu installed; there is a more in-depth explanation of the prerequisites here. If your device does not support kvm, you’re a bit stuck in the water. Otherwise, execute the following lines according to your OS as following:

sudo pacman -S qemu libvirt dnsmasq virt-manager bridge-utils flex bison iptables-nft edk2-ovmf

sudo apt install qemu qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager libguestfs-tools

sudo yum install libvirt qemu-kvm

Then, regardless of your distro, run the following and make sure the last line returns 1.

sudo systemctl enable --now libvirtd
sudo systemctl enable --now virtlogd

echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs

sudo modprobe kvm

Building the Hackintosh Image #

Most of the magic in this tutorial is made possible by the works of an incredibly talented security researcher, SickCodes. We’ll be using his project called Docker-OSX, linked here, to spin up our Hackintosh. Let’s start by first cloning the repo.

git clone

BlueBubbles and Airmessage are great as they allow you to react to messages, respond in threads, and do nearly everything, plus a little more, in their clients. However, this functionality is only available if your Hackintosh can support it. As time of writing, the most recent MacOS version is Ventura. The code snippet below will build a MacOS Ventura docker image, however you can choose whichever version suits your needs best listed in the repository.

docker run -it \
    --device /dev/kvm \
    -p 50922:10022 \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e "DISPLAY=${DISPLAY:-:0.0}" \
    -e GENERATE_UNIQUE=true \
    -e MASTER_PLIST_URL='' \

Once you spin up the docker image, you should see the apple start-up menu followed by the MacOS utilities screen, asking you to pick a boot drive; pick the largest volume, rename it to whatever you’d like, and once you’re satisfied, click “Erase”. DO NOT FORMAT ANY OTHER DISK. This will erase and format the fake volume for use in your Hackintosh. It should be noted here that the size of the fake volume is not representative of the real size of the docker image. Once installed it should be somewhere in the range of ~20 Gb for Ventura.

Once the volume has been erased and formatted, we need to install MacOS. Go back to the MacOS utilities screen and select “Reinstall macOS”. Click through the prompts to install the OS. This should take some time, but once installed, you should have a MacOS VM running.

Prepping the Hackintosh Server #

Now that we’ve built the Hackintosh image, we need to find it and get it ready to be daemonized. On a default installation you should be able to find your docker images stored in some subfolder of /var/lib/docker. The Hackintosh we just built should be quite large so it shouldn’t be too difficult to spot.

sudo find -name mac_hdd_ng.img | xargs ls -lha

Unless you’ve built some other image in the meantime, you should find the image you want as the most recent one, stamped by the current date and time. You can also find the image using docker ps -a and docker inspect.

# Show you containers that have been/are run/running
docker ps -a

# Select the container ID shown in docker ps -a and then docker inspect
docker inspect <container id> | grep Upper

Navigate to the directory shown by either of these methods and find the docker image mac_hdd_ng.img (the name of your image might vary depending on the OS installed) stored in the subdirectory .../diff/home/arch/OSX-KVM. You now want to move or copy this image to some folder you’d like to work in. This is your virtual volume.

Once the image has been moved out, you can now use it with the naked image.

docker run -it \
    --device /dev/kvm \
    -p 50922:10022 \
    -v "${PWD}/mac_hdd_ng.img:/image" \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e "DISPLAY=${DISPLAY:-:0.0}" \
    -e GENERATE_UNIQUE=true \

Make sure to replace the mac_hdd_ng.img with whatever the name of the image you copied/moved is. This will create and spin up a naked image on top of the OS you selected. There is a fair amount of customizability with this. Each -e flag is allows you to send commands directly to the bash/zsh pipes within the VM. The MASTER_PLIST_URL will give a serial number to your Hackintosh. This is necessary to sign into iCloud, which is, in turn, needed for iMessage.

Installing the iMessage Server #

This process will be very dependent on your selection of iMessage server. As mentioned before, there are plenty of options to do so, however I will be recommending AirMessage and BlueBubbles. Walk-throughs of both platforms can be found on their websites, however the gist of the process is installing either application on the Hackintosh (for BlueBubbles the GitHub link is here), acquiring the required files for both Cloudflare and Google Firebase (you don’t need Cloudflare if you have your own domain, but you might need Firebase for push notifications), and following the prompted steps in either installer.

Once finished, you should then install the client on whatever machine you’d like to text from, and test the waters. With both the client and server running, you should now be texting from your non-MacOS device.

Daemonizing #

Once you’ve got the iMessage server up and working, we need to daemonize the process; this isn’t too difficult. You should make sure that whatever client you’ve selected boots up at start and runs in the background. This should be an toggle for either option.

Once that’s sorted out we need to tweak our docker run command a little. When spinning up the container, we’re not only running an iMessage server, we’re also running all the background processes that comes alongside macOS. In order to save the lifetime of your machine we’ll limit the resources used by the docker image; qemu and kvm make this quite simple.

docker run -t \
    --device /dev/kvm \
    -p 50922:10022 \
    -v "<some direcotry>/mac_hdd_ng.img:/image" \
    -e RAM=2 \
    -e EXTRA='-smp 2,sockets=1,cores=2,threads=1' \
    -e "USERNAME=username" \
    -e "PASSWORD=password" \
    -e "OSX_COMMANDS=/bin/bash -c \"echo BOOT!\"" \

Now, we need to turn the script above into a process managed by systemd. You can start creating the file by running sudo systemctl edit --force --full hackintosh.service. This should open a text editor where you can paste in the following:

Description=Hackintosh Docker Image

ExecStart=/usr/bin/docker run \
	--rm \
	--name hackintosh \
	--device /dev/kvm \
    -p 50922:10022 \
    -v "<some direcotry>/mac_hdd_ng.img:/image" \
    -e RAM=2 \
    -e EXTRA='-smp 2,sockets=1,cores=2,threads=1' \
    -e "USERNAME=username" \
    -e "PASSWORD=password" \
    -e "OSX_COMMANDS=/bin/bash -c \"echo BOOT!\"" \

ExecStop=/usr/bin/docker container stop hackintosh

Once you’ve created the service file, you can enable and start the docker container by first running sudo systemctl daemon-reload followed by sudo systemctl enable --now bluebubbles.service. After a short moment, you should see your client establish a connection with your iMessage server.

SSH / Optimization #

There are a number of ways to optimize speed and resource consumption of your docker image detailed here, also by SickCodes. To perform these optimizations you’ll need to access a shell within your docker image. To ssh into your shell, run ssh localhost -p 50922, where the port number is specified by the -p 50922:10022 line in your docker run configuration. You’ll then need to login using the username and password also set in your docker run configuration.

Once in, you can run any of the optimizations such as the following:

# Disable spotlight search
sudo mdutil -i off -a

# Enable performance mode
sudo nvram boot-args="serverperfmode=1 $(nvram boot-args 2>/dev/null | cut -f 2-)"

# Disable Screen Locking
defaults write DisableScreenLock -bool true