# macOS VM (Docker-OSX) hosting the BlueBubbles server. # # ── Why a VM and not a container ──────────────────────────────────────────── # Containers share the host kernel. macOS binaries are Mach-O and need XNU plus # Cocoa/IOKit, which a Linux kernel cannot provide, so no macOS container exists # or can exist. Docker-OSX is QEMU running a macOS guest, packaged in a # container — a VM in a box, not a macOS container. # # ── Why this works on worker-2 ────────────────────────────────────────────── # Verified on the existing hardware: amd64, `vmx` (Intel VT-x) present, and # /dev/kvm exists on Talos nodes (KVM is compiled into Talos' kernel, not a # module). Bare metal, so no nested virtualisation needed. # # ── Read this before relying on it ────────────────────────────────────────── # 1. Setup is INTERACTIVE. First boot runs the macOS installer: connect over # VNC (:5999), erase the disk in Disk Utility, install, create a user, sign # into iMessage, THEN install BlueBubbles inside the guest. This manifest # only provides the machine; it does not provision macOS. # 2. iMessage activation on non-Apple hardware is a coin flip. BlueBubbles' # own guidance: "test sending an iMessage to yourself. If it does not # succeed, it's likely best to restart from the beginning." # 3. Apple's macOS licence permits virtualisation only on Apple hardware. This # is a Hackintosh. Use a throwaway Apple ID, not a primary one. # 4. BlueBubbles labels this path "not for beginners", "no guarantees or # warranty". # # Private API (reactions, typing indicators, edit/unsend) needs SIP disabled # inside the guest and is NOT required for plain send/receive. Skip it. apiVersion: apps/v1 kind: Deployment metadata: name: macos-bluebubbles labels: app.kubernetes.io/name: macos-bluebubbles app.kubernetes.io/part-of: sms spec: replicas: 1 # Recreate: the qcow2 disk is RWO and a second pod must never attach it # concurrently — two QEMU processes on one image corrupts it. strategy: type: Recreate selector: matchLabels: app.kubernetes.io/name: macos-bluebubbles template: metadata: labels: app.kubernetes.io/name: macos-bluebubbles app.kubernetes.io/part-of: sms spec: # Dedicated node. The taint keeps everything else off worker-2; this # toleration is what lets the VM on. Both halves are required. nodeSelector: workload: imessage tolerations: - key: workload operator: Equal value: imessage effect: NoSchedule containers: - name: macos image: sickcodes/docker-osx:latest@sha256:3a3c82c79bc4e73531f819ccdfa4053b3084efd7c1f645678dbf8b4b3a24369c # QEMU needs /dev/kvm; Talos enforces `baseline` cluster-wide, so this # only schedules because the sms namespace is labelled privileged. securityContext: privileged: true env: # Generates a unique serial / board-serial / UUID / MAC and persists # them to bootdisk.qcow2. This synthetic identity is what iMessage # activates against — it must stay stable across restarts, which is # why the PVC matters. - name: GENERATE_UNIQUE value: "true" # Identity is only plausible if it matches a real product line. - name: DEVICE_MODEL value: "iMacPro1,1" - name: RAM value: "12" - name: CORES value: "6" - name: EXTRA # Expose the BlueBubbles server port from the guest to the pod. # Guest :1234 (BlueBubbles default) -> pod :1234. value: "-device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::1234-:1234" ports: - name: vnc containerPort: 5999 protocol: TCP - name: bluebubbles containerPort: 1234 protocol: TCP resources: requests: cpu: "6" memory: 14Gi limits: cpu: "12" memory: 20Gi volumeMounts: - name: macos-disk mountPath: /home/arch/OSX-KVM/disk - name: kvm mountPath: /dev/kvm # No readiness probe on purpose. The guest takes many minutes to boot, # and until macOS + BlueBubbles are installed BY HAND there is nothing # listening on 1234. A probe here would crash-loop the pod through the # entire interactive install. volumes: - name: macos-disk persistentVolumeClaim: claimName: macos-disk - name: kvm hostPath: path: /dev/kvm type: CharDevice