[{"data":1,"prerenderedAt":176},["ShallowReactive",2],{"blog-post-unattended-mac-mini-fleet-setup":3},{"slug":4,"path":5,"title":6,"description":7,"publishedAt":8,"updatedAt":8,"author":9,"category":10,"tags":11,"featureImage":14,"ogImage":20,"canonical":25,"seoTitle":26,"seoDescription":27,"keywords":28,"readingTimeMinutes":37,"wordCount":38,"bodySegments":39,"hasMath":43,"headings":44,"mediaClips":72,"showFeatureImage":73,"leadFigureHtml":74,"relatedPosts":75,"citations":166},"unattended-mac-mini-fleet-setup","/blog/unattended-mac-mini-fleet-setup/","Running an unattended Mac mini fleet: a dedicated worker host, macOS 27 auto-login and deploy settings","How Diffio keeps its Mac mini inference fleet running unattended: a host that runs only the worker, disk cleanup, auto-login on macOS 27 with /etc/kcpassword, full disk access over SSH, Screen Sharing and deploy settings.","2026-11-05T06:54:55Z","Nathan Harmon","infrastructure",[12,13],"Inference","Apple Silicon",{"src":15,"alt":16,"widthPixels":17,"heightPixels":18,"srcset":19},"/media/blog/unattended-mac-mini-fleet-setup/lead-diagram.webp","Diagram of four steps for an unattended Mac mini: run only the worker, which warns on stray processes, swap and low disk; auto-login with /etc/kcpassword written by hand on macOS 27; full disk access over SSH and Screen Sharing set in System Settings; LaunchDaemon, KeepAlive, smoke restore and rolling deploys.",1440,810,"/media/blog/unattended-mac-mini-fleet-setup/responsive/lead-diagram-480w.webp 480w, /media/blog/unattended-mac-mini-fleet-setup/responsive/lead-diagram-800w.webp 800w, /media/blog/unattended-mac-mini-fleet-setup/responsive/lead-diagram-1200w.webp 1200w, /media/blog/unattended-mac-mini-fleet-setup/lead-diagram.webp 1440w",{"src":21,"alt":6,"widthPixels":22,"heightPixels":23,"srcset":24},"/media/blog/unattended-mac-mini-fleet-setup/social-card.png",1200,630,"/media/blog/unattended-mac-mini-fleet-setup/responsive/social-card-480w.webp 480w, /media/blog/unattended-mac-mini-fleet-setup/responsive/social-card-800w.webp 800w, /media/blog/unattended-mac-mini-fleet-setup/social-card.png 1200w","https://diffio.ai/blog/unattended-mac-mini-fleet-setup/","Unattended Mac mini fleet: macOS 27 auto-login and setup","Running Mac minis unattended: a worker-only host, disk cleanup, macOS 27 auto-login with /etc/kcpassword, full disk access over SSH and deploy settings.",[29,30,31,32,33,34,35,36],"unattended Mac mini server","macOS 27 auto-login","/etc/kcpassword","sysadminctl autologin error 22","full disk access over SSH","headless Mac Screen Sharing","audiomxd high CPU","LaunchDaemon deploy settings",11,2475,[40],{"kind":41,"html":42},"html","\u003Cp>Diffio 4.5 \u003Ca href=\"/audio-cleaner/\">restores speech\u003C/a> on a fleet of Mac minis (M4 Pro, 24 GB of unified memory). Each mini runs one worker, which can serve our production environment, our dev environment or both. This post covers the host side of that fleet: keeping each mini a dedicated worker host with free disk, logging it in without a person, the permissions set once in System Settings, and the host and deploy settings that keep it serving through reboots, crashes and failed deploys. How the worker overlaps downloads, encoding and uploads with inference is in \u003Ca href=\"/blog/mac-mini-inference-serving/\">Mac mini serving: overlapping I/O with inference, not batching, raised throughput\u003C/a>.\u003C/p>\n\u003Ch2 id=\"a-fleet-mini-runs-the-worker-and-nothing-else\">A fleet mini runs the worker and nothing else\u003C/h2>\n\u003Cp>One of our minis had also served as a desktop and a research machine: it ran the ChatGPT desktop app, started at login by a launch agent, Chrome, a research restore process left from a previous day, and its research checkouts. It had 4.4 GB of swap in use; after the cleanup, 0.5 GB. Any desktop working set competes directly with the model and the running jobs.\u003C/p>\n\u003Cdiv class=\"blog-chart blog-chart--bars\">\u003Cp class=\"blog-chart__title\">Swap in use on one mini (lower is better)\u003C/p>\u003Cdiv class=\"blog-bars__panel\">\u003Cdl class=\"blog-bars blog-bars--short\">\u003Cdiv class=\"blog-bars__row\">\u003Cdt>Before the cleanup\u003C/dt>\u003Cdd>\u003Cspan class=\"blog-bars__bar\" style=\"width: 100.0%\" aria-hidden=\"true\">\u003C/span>\u003Cspan class=\"blog-bars__value\">4.4 GB\u003C/span>\u003C/dd>\u003C/div>\u003Cdiv class=\"blog-bars__row blog-bars__row--strong\">\u003Cdt>After the cleanup\u003C/dt>\u003Cdd>\u003Cspan class=\"blog-bars__bar\" style=\"width: 11.4%\" aria-hidden=\"true\">\u003C/span>\u003Cspan class=\"blog-bars__value\">0.5 GB\u003C/span>\u003C/dd>\u003C/div>\u003C/dl>\u003C/div>\u003C/div>\n\u003Cp>The worker checks every minute for processes of its user outside macOS and its own folder, launch agents and daemons other than its own and Tailscale's, and active swapping, and reports any it finds as a warning in our analytics.\u003C/p>\n\u003Cdetails class=\"blog-details\">\u003Csummary>Why the worker warns on the swap-out rate, and what the deploy script checks\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>Swap occupancy was the first signal and proved too coarse, since macOS leaves cold pages in swap long after the pressure that put them there; the worker now warns on the swap-out rate, more than 256 MB written since its previous check. The deploy script refuses a mini that fails the process and launch-item checks, and checks swap only after the old worker has exited, since the old worker's swap is freed with it; if heavy swap remains, it restarts the previous release.\u003C/p>\u003C/div>\u003C/details>\n\u003Cp>Desktop software comes back on its own: after a reboot, macOS reopened Chrome from the saved login session, and Chrome reinstalled three Google updater launch agents. \u003Ccode>defaults write com.apple.loginwindow TALLogoutSavesState -bool false\u003C/code> stops apps from reopening at login.\u003C/p>\n\u003Ch2 id=\"the-worker-sweeps-its-work-and-temporary-directories-at-startup-alerts-below-20-gb-free-without-refusing-jobs-and-deploys-keep-two-releases\">The worker sweeps its work and temporary directories at startup, alerts below 20 GB free without refusing jobs, and deploys keep two releases\u003C/h2>\n\u003Cp>Each job writes into its own folder under the worker's work directory, removed when the job completes or fails. A crash or power loss mid-job leaves that folder behind, so the worker empties its work directory when it starts, when nothing can be running. Temporary files escaped that rule: one mini's per-user temporary directory held 14 GB of compiled Core ML models (\u003Ccode>.mlmodelc\u003C/code> directories) left by model builds and research runs.\u003C/p>\n\u003Cdetails class=\"blog-details\">\u003Csummary>How compiled Core ML models and temporary files stay inside the sweep\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>Core ML compiles a model package into a temporary location. Our loader copies each compiled model next to its package, so compiling happens once per machine, and it now deletes the temporary copy as soon as it has its own. The worker also points \u003Ccode>TMPDIR\u003C/code> at a folder inside its work directory, for itself and every process it starts, so the startup sweep covers Python's and ffmpeg's temporary files.\u003C/p>\u003C/div>\u003C/details>\n\u003Cp>A release's code is 6.6 MB, but the weights bundle (the checkpoints, the speech-encoder and codec models, and the converted Core ML and MLX copies) is 24 GB, so a deploy keeps only the new release and the previous one, the rollback target, and deletes weight bundles neither uses. Seeding a bundle from a folder already on the mini uses APFS copy-on-write clones (\u003Ccode>cp -c\u003C/code>), which share blocks until a file changes.\u003C/p>\n\u003Cdiv class=\"blog-chart blog-chart--numbers\">\u003Cul class=\"blog-numbers blog-numbers--four\">\u003Cli>\u003Cspan class=\"blog-numbers__value\">14 GB\u003C/span>\u003Cspan class=\"blog-numbers__label\">compiled Core ML models left in one mini's temporary directory\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-numbers__value\">24 GB\u003C/span>\u003Cspan class=\"blog-numbers__label\">a release's weights bundle, against 6.6 MB of code\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-numbers__value\">20 GB\u003C/span>\u003Cspan class=\"blog-numbers__label\">free space below which the worker warns\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-numbers__value\">5 GB\u003C/span>\u003Cspan class=\"blog-numbers__label\">free space below which it reports an error\u003C/span>\u003C/li>\u003C/ul>\u003C/div>\n\u003Cp>Below 20 GB free on the work volume (\u003Ccode>disk_alert_gb\u003C/code> in the worker's configuration) the worker reports a warning to our analytics, and below 5 GB an error, repeated while it lasts; a deploy prints the warning and goes ahead. A job that actually runs out of space fails on its own. The worker used to offer no slots below the threshold, which would have taken a mini out of service with 20 GB still free.\u003C/p>\n\u003Cdetails class=\"blog-details\">\u003Csummary>The Neural Engine's compiled-model cache, which SSH cannot measure\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>\u003Ccode>/Library/Caches/com.apple.aned\u003C/code>, where the Neural Engine service keeps the models it has compiled in a folder per macOS build, stays \u003Ccode>Operation not permitted\u003C/code> over SSH even with full disk access and sudo, so \u003Ccode>du\u003C/code> cannot measure it. On one of ours, a minor macOS update that changed the build took used space from about 408 GB to 50 GB, and the worker's first model load afterwards recompiled its Neural Engine graphs into the new build's folder. That cache grows as the Neural Engine compiles new models, so model experiments stay off fleet minis, and the low-disk warning reports a mini before its disk fills.\u003C/p>\u003C/div>\u003C/details>\n\u003Ch2 id=\"without-a-logged-in-user-macos-27s-audiomxd-and-configd-together-use-more-than-a-cpu-core\">Without a logged-in user, macOS 27's audiomxd and configd together use more than a CPU core\u003C/h2>\n\u003Cp>On a mini running macOS 27.0 (26A428) with no console user, \u003Ccode>audiomxd\u003C/code> used 70–74% of a core and \u003Ccode>configd\u003C/code> 45%, from boot onward. Auto-login for the service user provides a console user, and both processes drop to 0% CPU.\u003C/p>\n\u003Cdiv class=\"blog-chart blog-chart--bars\">\u003Cp class=\"blog-chart__title\">CPU use of one core, macOS 27.0, no console user\u003C/p>\u003Cdiv class=\"blog-bars__panel\">\u003Cdl class=\"blog-bars\">\u003Cdiv class=\"blog-bars__row\">\u003Cdt>audiomxd\u003C/dt>\u003Cdd>\u003Cspan class=\"blog-bars__bar\" style=\"width: 100.0%\" aria-hidden=\"true\">\u003C/span>\u003Cspan class=\"blog-bars__value\">70–74%\u003C/span>\u003C/dd>\u003C/div>\u003Cdiv class=\"blog-bars__row\">\u003Cdt>configd\u003C/dt>\u003Cdd>\u003Cspan class=\"blog-bars__bar\" style=\"width: 64.3%\" aria-hidden=\"true\">\u003C/span>\u003Cspan class=\"blog-bars__value\">45%\u003C/span>\u003C/dd>\u003C/div>\u003Cdiv class=\"blog-bars__row blog-bars__row--strong\">\u003Cdt>Both, after auto-login for the service user\u003C/dt>\u003Cdd>\u003Cspan class=\"blog-bars__bar\" style=\"width: 1.5%\" aria-hidden=\"true\">\u003C/span>\u003Cspan class=\"blog-bars__value\">0%\u003C/span>\u003C/dd>\u003C/div>\u003C/dl>\u003C/div>\u003C/div>\n\u003Cp>A \u003Ccode>sample\u003C/code> of audiomxd shows a tight loop: Bluetooth audio routing (\u003Ccode>-[BTAudioRoutingRequest updateAudioState:withState:]\u003C/code>) tries to start an XPC connection to the console user's session, asks configd for the console user (\u003Ccode>SCDynamicStoreCopyConsoleUser\u003C/code>), finds none, and retries. Its log repeats \u003Ccode>UpdateAudioState failed to start XPC: kUnexpectedErr (No user logged in)\u003C/code>. Bluetooth was on, with nothing paired.\u003C/p>\n\u003Cdetails class=\"blog-details\">\u003Csummary>What did not stop the loop\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>Bluetooth cannot be turned off headlessly on macOS 27: with \u003Ccode>ControllerPowerState\u003C/code> set to 0 in \u003Ccode>/Library/Preferences/com.apple.Bluetooth\u003C/code>, Bluetooth is still on after restarting bluetoothd or rebooting, and \u003Ccode>blueutil -p 0\u003C/code> cannot reach the controller without a GUI session, even as root. launchd restarts a killed audiomxd, which loops again within seconds. Our other mini, on macOS 26.7, has always logged in automatically and never showed the loop.\u003C/p>\u003C/div>\u003C/details>\n\u003Ch2 id=\"on-macos-27-auto-login-needs-etc-kcpassword-written-by-hand\">On macOS 27, auto-login needs /etc/kcpassword written by hand\u003C/h2>\n\u003Cp>\u003Ccode>sudo sysadminctl -autologin set -userName &lt;user&gt; -password -\u003C/code> prints \u003Ccode>SACSetAutoLoginPassword error:22\u003C/code> even with the correct password, then exits 0. It writes \u003Ccode>autoLoginUser\u003C/code> but not \u003Ccode>/etc/kcpassword\u003C/code>, so the mini still boots to the login window.\u003C/p>\n\u003Cdiv class=\"blog-chart blog-chart--steps\">\u003Cp class=\"blog-chart__title\">What auto-login needs, and what the script does\u003C/p>\u003Col class=\"blog-steps\">\u003Cli>\u003Cspan class=\"blog-steps__title\">Two pieces\u003C/span>\u003Cspan class=\"blog-steps__text\">\u003Ccode>autoLoginUser\u003C/code> in loginwindow preferences, and \u003Ccode>/etc/kcpassword\u003C/code>\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-steps__title\">Verify\u003C/span>\u003Cspan class=\"blog-steps__text\">the script checks the password with \u003Ccode>dscl -authonly\u003C/code>\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-steps__title\">Write\u003C/span>\u003Cspan class=\"blog-steps__text\">the password obfuscated with a fixed XOR key, mode 600\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-steps__title\">Check\u003C/span>\u003Cspan class=\"blog-steps__text\">after a reboot, \u003Ccode>stat -f %Su /dev/console\u003C/code> prints the service user\u003C/span>\u003C/li>\u003C/ol>\u003C/div>\n\u003Cdetails class=\"blog-details\">\u003Csummary>What auto-login needs, and how the script handles the password\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>Auto-login needs both the \u003Ccode>autoLoginUser\u003C/code> key in \u003Ccode>/Library/Preferences/com.apple.loginwindow\u003C/code> and \u003Ccode>/etc/kcpassword\u003C/code>, which holds the password obfuscated with a fixed XOR key. The script below verifies the password with \u003Ccode>dscl -authonly\u003C/code> and writes both. getpass keeps the password out of shell history; it appears in \u003Ccode>dscl\u003C/code>'s arguments only while dscl runs.\u003C/p>\u003C/div>\u003C/details>\n\u003Cpre class=\"blog-code shiki diffio-gray-light\" role=\"region\" aria-label=\"Code sample 1\" style=\"background-color:#fafafa;color:#262626\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#636363;font-style:italic\">#!/usr/bin/env python3\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#3F3F3F\">\"\"\"Turn on auto-login for USER. Run as: sudo python3 set_autologin.py USER\"\"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#0A0A0A;font-weight:bold\">import\u003C/span>\u003Cspan style=\"color:#262626\"> getpass\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#262626\"> os\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#262626\"> subprocess\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#262626\"> sys\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#0A0A0A\">KEY\u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\"> =\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> bytes\u003C/span>\u003Cspan style=\"color:#525252\">([\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\">0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">7D\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">89\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">52\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">23\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">D2\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">BC\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">DD\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">EA\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">A3\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">B9\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0x\u003C/span>\u003Cspan style=\"color:#0A0A0A\">1F\u003C/span>\u003Cspan style=\"color:#525252\">])\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">user \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">=\u003C/span>\u003Cspan style=\"color:#262626\"> sys\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#262626\">argv\u003C/span>\u003Cspan style=\"color:#525252\">[\u003C/span>\u003Cspan style=\"color:#0A0A0A\">1\u003C/span>\u003Cspan style=\"color:#525252\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">password \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">=\u003C/span>\u003Cspan style=\"color:#262626\"> getpass\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">getpass\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\">f\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"Password for \u003C/span>\u003Cspan style=\"color:#0A0A0A\">{user}\u003C/span>\u003Cspan style=\"color:#3F3F3F\">: \"\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#0A0A0A;font-weight:bold\">if\u003C/span>\u003Cspan style=\"color:#262626\"> subprocess\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">run\u003C/span>\u003Cspan style=\"color:#525252\">([\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"dscl\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#3F3F3F\"> \".\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#3F3F3F\"> \"-authonly\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> user\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> password\u003C/span>\u003Cspan style=\"color:#525252\">]).\u003C/span>\u003Cspan style=\"color:#262626;font-style:italic\">returncode\u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\"> !=\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> 0\u003C/span>\u003Cspan style=\"color:#525252\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">    sys\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">exit\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"wrong password\"\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">data \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">=\u003C/span>\u003Cspan style=\"color:#262626\"> password\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">encode\u003C/span>\u003Cspan style=\"color:#525252\">()\u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\"> +\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> bytes\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">12 \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">-\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> len\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">password\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">encode\u003C/span>\u003Cspan style=\"color:#525252\">())\u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\"> %\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> 12\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003Cspan style=\"color:#636363;font-style:italic\">  # always at least one terminating zero byte\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">encoded \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">=\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> bytes\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">b \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">^\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> KEY\u003C/span>\u003Cspan style=\"color:#525252\">[\u003C/span>\u003Cspan style=\"color:#0A0A0A\">i \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">%\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> len\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">KEY\u003C/span>\u003Cspan style=\"color:#525252\">)]\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> for\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> i\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> b \u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\">in\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> enumerate\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">data\u003C/span>\u003Cspan style=\"color:#525252\">))\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">fd \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">=\u003C/span>\u003Cspan style=\"color:#262626\"> os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">open\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"/etc/kcpassword\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">O_WRONLY \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">|\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">O_CREAT \u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">|\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">O_TRUNC\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0o\u003C/span>\u003Cspan style=\"color:#0A0A0A\">600\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#0A0A0A;font-weight:bold\">with\u003C/span>\u003Cspan style=\"color:#262626\"> os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">fdopen\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">fd\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#3F3F3F\"> \"wb\"\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> as\u003C/span>\u003Cspan style=\"color:#262626\"> f\u003C/span>\u003Cspan style=\"color:#525252\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">    f\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">write\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#0A0A0A\">encoded\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">chown\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"/etc/kcpassword\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> 0\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> 0\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">os\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">chmod\u003C/span>\u003Cspan style=\"color:#525252\">(\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"/etc/kcpassword\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A;font-weight:bold\"> 0o\u003C/span>\u003Cspan style=\"color:#0A0A0A\">600\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#262626\">subprocess\u003C/span>\u003Cspan style=\"color:#525252\">.\u003C/span>\u003Cspan style=\"color:#0A0A0A\">run\u003C/span>\u003Cspan style=\"color:#525252\">([\u003C/span>\u003Cspan style=\"color:#3F3F3F\">\"defaults\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#3F3F3F\"> \"write\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#3F3F3F\"> \"/Library/Preferences/com.apple.loginwindow\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#3F3F3F\">                \"autoLoginUser\"\u003C/span>\u003Cspan style=\"color:#525252\">,\u003C/span>\u003Cspan style=\"color:#0A0A0A\"> user\u003C/span>\u003Cspan style=\"color:#525252\">],\u003C/span>\u003Cspan style=\"color:#262626;font-style:italic\"> check\u003C/span>\u003Cspan style=\"color:#525252;font-weight:bold\">=\u003C/span>\u003Cspan style=\"color:#0A0A0A\">True\u003C/span>\u003Cspan style=\"color:#525252\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>After a reboot, \u003Ccode>stat -f %Su /dev/console\u003C/code> prints the service user.\u003C/p>\n\u003Ch2 id=\"full-disk-access-over-ssh-and-screen-sharing-are-turned-on-in-the-macs-system-settings-not-from-the-command-line\">Full disk access over SSH and Screen Sharing are turned on in the Mac's System Settings, not from the command line\u003C/h2>\n\u003Cp>Full disk access is a macOS privacy permission (TCC, Transparency, Consent and Control, whose database System Integrity Protection guards), which the command line cannot grant, even with sudo, and Screen Sharing turned on from the command line could not capture the screen. Both are set once in System Settings on the mini when it is set up; full disk access can also come from an MDM profile.\u003C/p>\n\u003Cdiv class=\"blog-table blog-table--notes\" role=\"region\" aria-label=\"Table 1: Setting, in System Settings, Why\" tabindex=\"0\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Setting, in System Settings\u003C/th>\n\u003Cth>Why\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>General &gt; Sharing &gt; the (i) next to Remote Login &gt; Allow full disk access for remote users on. On macOS versions without that switch: Privacy &amp; Security &gt; Full Disk Access &gt; +, then Cmd-Shift-G and \u003Ccode>/usr/libexec/sshd-keygen-wrapper\u003C/code>\u003C/td>\n\u003Ctd>maintenance runs over SSH, and without it \u003Ccode>du\u003C/code>, \u003Ccode>ls\u003C/code> and the like get \u003Ccode>Operation not permitted\u003C/code> in Desktop, Documents, Downloads, the Trash and other protected folders, even with sudo, so disk use cannot be fully accounted for\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>General &gt; Sharing &gt; Screen Sharing on; under its (i), Allow access for: Only these users, the account the worker runs as; &quot;VNC viewers may control screen with password&quot; off\u003C/td>\n\u003Ctd>System Settings, and anything else that needs the desktop, can be reached without going to the mini; clients authenticate with the macOS account\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C/div>\u003Cdetails class=\"blog-details\">\u003Csummary>Tailscale SSH: the Remote Login switch does not cover it\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>On a mini reached through Tailscale SSH, the session runs under \u003Ccode>tailscaled\u003C/code> rather than macOS's \u003Ccode>sshd\u003C/code>, so the Remote Login switch, which grants \u003Ccode>sshd\u003C/code>, does not cover it. Granting full disk access to \u003Ccode>tailscaled\u003C/code> itself (for the Homebrew build, \u003Ccode>/opt/homebrew/opt/tailscale/bin/tailscaled\u003C/code>) is the likely equivalent, but we have not yet confirmed that it opens protected folders over Tailscale SSH.\u003C/p>\u003C/div>\u003C/details>\n\u003Cdetails class=\"blog-details\">\u003Csummary>Why Screen Sharing is not turned on over SSH, and Remote Management stays off\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>Screen Sharing turned on over SSH (\u003Ccode>launchctl enable\u003C/code> and \u003Ccode>bootstrap\u003C/code> of \u003Ccode>com.apple.screensharing\u003C/code>) opened port 5900, but the client got a black screen, and after the display was woken \u003Ccode>screensharingd\u003C/code> still logged that it could not capture the screen; we turn Screen Sharing on in System Settings instead. While Remote Management is on, the Screen Sharing switch is greyed out as controlled by the Remote Management service. Remote Management grants much broader remote administration, so we turn it off and plain Screen Sharing on.\u003C/p>\u003C/div>\u003C/details>\n\u003Cdetails class=\"blog-details\">\u003Csummary>A black screen, and connecting from Linux\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cp>If a client shows a black screen, the mini's display may be asleep; \u003Ccode>caffeinate -u -t 5\u003C/code> over SSH wakes it. From Linux, Remmina with its VNC plugin (\u003Ccode>sudo apt install remmina remmina-plugin-vnc\u003C/code>) connects to the mini's Tailscale IP and logs in with the macOS account.\u003C/p>\u003C/div>\u003C/details>\n\u003Ch2 id=\"an-unattended-mini-needs-host-and-deploy-settings-that-survive-reboots-crashes-and-failed-deploys\">An unattended mini needs host and deploy settings that survive reboots, crashes and failed deploys\u003C/h2>\n\u003Cdiv class=\"blog-chart blog-chart--numbers\">\u003Cul class=\"blog-numbers blog-numbers--four\">\u003Cli>\u003Cspan class=\"blog-numbers__value\">3600 s\u003C/span>\u003Cspan class=\"blog-numbers__label\">\u003Ccode>ExitTimeOut\u003C/code>, so a deploy lets running jobs finish\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-numbers__value\">60 s\u003C/span>\u003Cspan class=\"blog-numbers__label\">grace period before the scheduler re-queues a dead worker's jobs\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-numbers__value\">11–16 s\u003C/span>\u003Cspan class=\"blog-numbers__label\">model load before the worker offers any slot\u003C/span>\u003C/li>\u003Cli>\u003Cspan class=\"blog-numbers__value\">About 5.5 minutes\u003C/span>\u003Cspan class=\"blog-numbers__label\">the Neural Engine's first compile on a new machine\u003C/span>\u003C/li>\u003C/ul>\u003C/div>\n\u003Cdiv class=\"blog-table blog-table--notes\" role=\"region\" aria-label=\"Table 2: Setting, Why\" tabindex=\"0\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Setting\u003C/th>\n\u003Cth>Why\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>The worker is a LaunchDaemon (system domain) with \u003Ccode>UserName\u003C/code> set, not a LaunchAgent\u003C/td>\n\u003Ctd>it starts at boot and needs no login; MLX, Metal, MPS and the Neural Engine all work in that context\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>FileVault off, auto-login for the service user, \u003Ccode>pmset\u003C/code> \u003Ccode>autorestart 1\u003C/code> and \u003Ccode>sleep 0\u003C/code>\u003C/td>\n\u003Ctd>an unattended mini must boot after a power loss into a logged-in session without anyone typing a password, and never sleep; auto-login requires FileVault off. Anyone with physical access gets an unencrypted disk and a logged-in session\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Spotlight indexing off (\u003Ccode>sudo mdutil -a -i off\u003C/code>)\u003C/td>\n\u003Ctd>every job writes and deletes audio files under the work directory, and indexing them would spend CPU, memory and disk writes the jobs need\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Wi-Fi off on a mini with Ethernet (\u003Ccode>networksetup -setairportpower en1 off\u003C/code>)\u003C/td>\n\u003Ctd>Ethernet and Wi-Fi on the same subnet gave the mini two default routes; the setting persists across reboots\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>KeepAlive\u003C/code> and \u003Ccode>ProcessType Interactive\u003C/code> in the plist\u003C/td>\n\u003Ctd>launchd restarts a crashed worker and applies no background resource limits; the scheduler re-queues a dead worker's jobs after a 60 s grace period\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATH\u003C/code> in the plist includes \u003Ccode>/opt/homebrew/bin\u003C/code>\u003C/td>\n\u003Ctd>launchd services and non-login SSH sessions do not get Homebrew's path, and ffmpeg lives there\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>ExitTimeOut\u003C/code> of 3600 s, and SIGTERM drains\u003C/td>\n\u003Ctd>a deploy's \u003Ccode>launchctl bootout\u003C/code> lets running jobs finish instead of killing them mid-upload\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Each remote deploy command prints its own exit status on a marker line\u003C/td>\n\u003Ctd>Tailscale SSH reported exit status 0 for failed remote commands\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>The scheduler stores an expected release per worker\u003C/td>\n\u003Ctd>a rolling deploy updates one mini at a time, and the minis not yet updated stay valid and keep taking jobs\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>A smoke restore runs after the release symlink switches and before the service restarts; a failure switches back\u003C/td>\n\u003Ctd>a release that cannot load the model or restore a file never takes jobs\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Code ships as a \u003Ccode>git archive\u003C/code>, weights from a bundle on the deploy host\u003C/td>\n\u003Ctd>the minis hold no GitHub or Hugging Face credentials\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>The model loads before the worker offers any slot (11–16 s; on a new machine the Neural Engine's first compile of all 4.5 graphs takes about 5.5 minutes)\u003C/td>\n\u003Ctd>a job never waits on a cold model\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C/div>\u003Ch2 id=\"sources\">Sources\u003C/h2>\n\u003Cul class=\"blog-sources\">\n\u003Cli>\u003Ca href=\"/media/blog/mac-mini-inference-serving/mac-mini-serving-measurements.json\">Diffio Mac mini serving measurements\u003C/a>\u003C/li>\n\u003Cli>Diffio fleet operations, October 21–22, 2026 (not published)\u003C/li>\n\u003Cli>\u003Ca href=\"https://keith.github.io/xcode-man-pages/launchd.plist.5.html\">launchd.plist(5)\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"https://developer.apple.com/documentation/coreml/downloading-and-compiling-a-model-on-the-user-s-device\">Core ML: Downloading and compiling a model on the user's device\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"https://support.apple.com/en-us/102316\">How to log in automatically to a Mac user account, Apple Support\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Cdetails class=\"blog-details blog-details--sources\">\u003Csummary>What each source supports\u003C/summary>\u003Cdiv class=\"blog-details__body\">\u003Cul>\n\u003Cli>\u003Ca href=\"/media/blog/mac-mini-inference-serving/mac-mini-serving-measurements.json\">Diffio Mac mini serving measurements\u003C/a>: the host figures, including swap in use on one mini before and after its cleanup, the compiled Core ML models left in its temporary directory, the release code and weights-bundle sizes, FileVault and \u003Ccode>pmset\u003C/code> settings, the model load time and the Neural Engine's first compile. The macOS 27 \u003Ccode>audiomxd\u003C/code> sample comes from a separate run on the same minis and is not in this file; thresholds and deploy settings come from our worker's code and configuration.\u003C/li>\n\u003Cli>Diffio fleet operations, October 21–22, 2026 (not published): one mini's used space before and after a macOS update changed the build, which folders stayed unreadable over SSH with and without full disk access, which process serves an SSH session on each mini, and Screen Sharing enabled over SSH, in the disk and the full disk access and Screen Sharing sections.\u003C/li>\n\u003Cli>\u003Ca href=\"https://keith.github.io/xcode-man-pages/launchd.plist.5.html\">launchd.plist(5)\u003C/a>: \u003Ccode>UserName\u003C/code>, \u003Ccode>KeepAlive\u003C/code>, \u003Ccode>ProcessType\u003C/code>, \u003Ccode>ExitTimeOut\u003C/code> and \u003Ccode>EnvironmentVariables\u003C/code>, in the settings table.\u003C/li>\n\u003Cli>\u003Ca href=\"https://developer.apple.com/documentation/coreml/downloading-and-compiling-a-model-on-the-user-s-device\">Core ML: Downloading and compiling a model on the user's device\u003C/a>: compiled models land in a temporary location and should be moved to a permanent one, in the startup-sweep section.\u003C/li>\n\u003Cli>\u003Ca href=\"https://support.apple.com/en-us/102316\">How to log in automatically to a Mac user account, Apple Support\u003C/a>: FileVault disables automatic login, in the settings table.\u003C/li>\n\u003C/ul>\u003C/div>\u003C/details>",false,[45,49,53,57,61,65,69],{"id":46,"text":47,"label":48},"a-fleet-mini-runs-the-worker-and-nothing-else","A fleet mini runs the worker and nothing else","A dedicated worker host",{"id":50,"text":51,"label":52},"the-worker-sweeps-its-work-and-temporary-directories-at-startup-alerts-below-20-gb-free-without-refusing-jobs-and-deploys-keep-two-releases","The worker sweeps its work and temporary directories at startup, alerts below 20 GB free without refusing jobs, and deploys keep two releases","Disk cleanup and releases",{"id":54,"text":55,"label":56},"without-a-logged-in-user-macos-27s-audiomxd-and-configd-together-use-more-than-a-cpu-core","Without a logged-in user, macOS 27's audiomxd and configd together use more than a CPU core","macOS background CPU use",{"id":58,"text":59,"label":60},"on-macos-27-auto-login-needs-etc-kcpassword-written-by-hand","On macOS 27, auto-login needs /etc/kcpassword written by hand","Auto-login on macOS 27",{"id":62,"text":63,"label":64},"full-disk-access-over-ssh-and-screen-sharing-are-turned-on-in-the-macs-system-settings-not-from-the-command-line","Full disk access over SSH and Screen Sharing are turned on in the Mac's System Settings, not from the command line","Disk access and Screen Sharing",{"id":66,"text":67,"label":68},"an-unattended-mini-needs-host-and-deploy-settings-that-survive-reboots-crashes-and-failed-deploys","An unattended mini needs host and deploy settings that survive reboots, crashes and failed deploys","Settings for unattended minis",{"id":70,"text":71,"label":71},"sources","Sources",[],true,null,[76,107,139],{"slug":77,"path":78,"title":79,"description":80,"publishedAt":81,"updatedAt":82,"author":9,"category":10,"tags":83,"featureImage":85,"ogImage":89,"canonical":92,"seoTitle":93,"seoDescription":94,"keywords":95,"readingTimeMinutes":104,"wordCount":105,"relatedNote":106},"mac-mini-inference-serving","/blog/mac-mini-inference-serving/","Mac mini inference serving: overlapping I/O with inference, not batching, raised throughput","How Diffio raised Mac mini throughput on I/O-bound speech restores by overlapping downloads, encoding and uploads with inference around one shared model, rather than batching.","2026-10-07T16:33:19Z","2026-11-05T06:57:25Z",[84,12,13],"ML",{"src":86,"alt":87,"widthPixels":17,"heightPixels":18,"srcset":88},"/media/blog/mac-mini-inference-serving/lead-diagram.webp","Bar chart: six 45 second jobs submitted at once to one Mac mini. Makespan 140 s one job at a time, 59 s three jobs at once, 42 s six jobs at once; median time to first audio 63 s, 16 s and 6.1 s.","/media/blog/mac-mini-inference-serving/responsive/lead-diagram-480w.webp 480w, /media/blog/mac-mini-inference-serving/responsive/lead-diagram-800w.webp 800w, /media/blog/mac-mini-inference-serving/responsive/lead-diagram-1200w.webp 1200w, /media/blog/mac-mini-inference-serving/lead-diagram.webp 1440w",{"src":90,"alt":79,"widthPixels":22,"heightPixels":23,"srcset":91},"/media/blog/mac-mini-inference-serving/social-card.png","/media/blog/mac-mini-inference-serving/responsive/social-card-480w.webp 480w, /media/blog/mac-mini-inference-serving/responsive/social-card-800w.webp 800w, /media/blog/mac-mini-inference-serving/social-card.png 1200w","https://diffio.ai/blog/mac-mini-inference-serving/","Mac mini inference: overlapping I/O beat batching","How Diffio raised Mac mini throughput on I/O-bound speech restores by overlapping downloads, encoding and uploads with inference instead of batching.",[96,97,98,99,100,101,102,103],"Mac mini inference serving","Apple silicon inference","overlapping I/O with inference","batching vs concurrency","MLX inference thread","shortest job first scheduling","macOS open file limit","botocore read timeout R2",24,5533,"How the worker on each of these minis runs several jobs at once around one copy of the model, and what limits it.",{"slug":108,"path":109,"title":110,"description":111,"publishedAt":112,"updatedAt":113,"author":9,"category":10,"tags":114,"featureImage":115,"ogImage":121,"canonical":124,"seoTitle":125,"seoDescription":126,"keywords":127,"readingTimeMinutes":136,"wordCount":137,"relatedNote":138},"diffio-apple-silicon","/blog/diffio-apple-silicon/","Diffio on Apple silicon: split MLPs let the Neural Engine run two of three guidance branches","How Diffio 4.0 reached 7.8x (pro) and 10.7x (flash) realtime on an M4 Pro Mac mini by splitting guidance branches across the GPU, the CPU's SME units and the Neural Engine, and what memory, precision and scheduling taught us.","2026-09-23T15:03:54Z","2026-11-05T06:54:16Z",[84,12,13],{"src":116,"alt":117,"widthPixels":118,"heightPixels":119,"srcset":120},"/media/blog/diffio-apple-silicon/feature.webp","Diagram of one guidance step on Apple silicon: one branch on the GPU and CPU, two on the Neural Engine with the MLP split into quarters, combined into guidance",1400,933,"/media/blog/diffio-apple-silicon/responsive/feature-480w.webp 480w, /media/blog/diffio-apple-silicon/responsive/feature-800w.webp 800w, /media/blog/diffio-apple-silicon/responsive/feature-1200w.webp 1200w, /media/blog/diffio-apple-silicon/feature.webp 1400w",{"src":122,"alt":110,"widthPixels":22,"heightPixels":23,"srcset":123},"/media/blog/diffio-apple-silicon/social-card.png","/media/blog/diffio-apple-silicon/responsive/social-card-480w.webp 480w, /media/blog/diffio-apple-silicon/responsive/social-card-800w.webp 800w, /media/blog/diffio-apple-silicon/social-card.png 1200w","https://diffio.ai/blog/diffio-apple-silicon/","Diffio on Apple silicon (M4 Pro): MLX, SME, Neural Engine","How Diffio 4.0 reached 7.8x realtime on an M4 Pro Mac mini by splitting guidance branches across the GPU, the CPU's SME units and the Neural Engine.",[128,129,130,131,132,133,134,135],"M4 Pro Mac mini","MLX","Apple Neural Engine","Core ML fp16","Scalable Matrix Extension (SME)","diffusion transformer inference","unified memory swap","classifier-free guidance",18,4063,"How one restore divides its guidance branches between the GPU, the CPU and the Neural Engine.",{"slug":140,"path":141,"title":142,"description":143,"publishedAt":144,"updatedAt":113,"author":9,"category":10,"tags":145,"featureImage":147,"ogImage":151,"canonical":154,"seoTitle":155,"seoDescription":74,"keywords":156,"readingTimeMinutes":136,"wordCount":165,"relatedNote":74},"efficient-model-deployment-modal","/blog/efficient-model-deployment-modal/","Cold starts on Modal: smaller model state did not show faster cold requests","What Diffio’s Modal experiments showed about GPU snapshots, quantization, CUDA graphs, transcription dependencies and cold-request latency.","2026-09-09T00:42:46Z",[84,12,146],"Modal",{"src":148,"alt":149,"widthPixels":17,"heightPixels":18,"srcset":150},"/media/blog/efficient-model-deployment-modal/lead-diagram.webp","Interval chart of the final smaller-state candidates on a 60 second input: ScoreQ allocator shrink on Diffio 3.5, median change −6.20 s (95% interval −63.00 to +77.84 s, 10 pairs); acoustic INT4 MLPs on Diffio 2.0, median +5.74 s (95% interval +4.47 to +66.32 s, 7 pairs).","/media/blog/efficient-model-deployment-modal/responsive/lead-diagram-480w.webp 480w, /media/blog/efficient-model-deployment-modal/responsive/lead-diagram-800w.webp 800w, /media/blog/efficient-model-deployment-modal/responsive/lead-diagram-1200w.webp 1200w, /media/blog/efficient-model-deployment-modal/lead-diagram.webp 1440w",{"src":152,"alt":142,"widthPixels":22,"heightPixels":23,"srcset":153},"/media/blog/efficient-model-deployment-modal/social-card.png","/media/blog/efficient-model-deployment-modal/responsive/social-card-480w.webp 480w, /media/blog/efficient-model-deployment-modal/responsive/social-card-800w.webp 800w, /media/blog/efficient-model-deployment-modal/social-card.png 1200w","https://diffio.ai/blog/efficient-model-deployment-modal/","Modal cold starts: GPU snapshots, CUDA graphs, quantization",[157,158,159,160,161,162,163,164],"Modal cold starts","Modal GPU snapshots","memory snapshots","CUDA graphs","INT4 quantization","ahead-of-time compilation","ONNX Runtime CUDA execution provider","cold-request latency",4134,[167,170,173],{"name":168,"url":169},"launchd.plist(5)","https://keith.github.io/xcode-man-pages/launchd.plist.5.html",{"name":171,"url":172},"Core ML: Downloading and compiling a model on the user's device","https://developer.apple.com/documentation/coreml/downloading-and-compiling-a-model-on-the-user-s-device",{"name":174,"url":175},"How to log in automatically to a Mac user account, Apple Support","https://support.apple.com/en-us/102316",1794173866913]