Headless macOS Server Setup: Udon & Mac Mini Homelab
macOS sleep states and TCC prompts break headless setups. Configure pmset overrides and deploy Udon to run a reliable, low-power Apple Silicon homelab.
An Apple Silicon Mac Mini idling between 4 and 8 watts sounds like the holy grail for a homelab. The hardware is dead silent, electricity costs barely register on a monthly utility bill, and the M-series CPU handles video transcoding, file synchronization, and database indexing without breaking a sweat.
Yet the moment you unplug the monitor, pull the keyboard, and slide the Mac Mini onto a network shelf, macOS makes its roots obvious. It was designed from the ground up as an interactive desktop operating system for a human sitting in front of a screen, not a headless server in a closet. Headless sleep states kick in without warning, unattended background scripts fail silently behind operating system permission checks, and Docker containers must run inside a virtual machine because the underlying kernel cannot execute Linux binaries natively.
Udon recently entered the self-hosting scene as a remote management dashboard built specifically for headless macOS machines. It aims to give Darwin the same lightweight administrative web interface that Cockpit provides for Linux servers.
Before Udon can run your services reliably, you need to strip away the desktop assumptions baked into macOS.
Taming the Headless macOS Trap: The Real pmset Rules
If you set up a Mac Mini over SSH and walk away, you will probably find it unreachable a few hours later. macOS defaults aggressively to power conservation. When no physical monitor is connected, standard GUI power settings in System Settings frequently reset or fail to apply across reboots.
Older Intel Macs often required a physical HDMI dummy plug to fool the system into keeping the graphics pipeline alive. Apple Silicon chips (M1 through M4) do not need dummy hardware; their display controllers and unified memory boot cleanly without a screen. The problem is not the hardware display pipe. The problem is power assertions inside the kernel.
You can take full control of your power management through pmset. Open your terminal and run this baseline command:
bash sudo pmset -a disablesleep 1 disksleep 0 womp 1 autorestart 1
Here is how each parameter works:
-a: Tellspmsetto apply these settings across every power profile (wall power, battery, and UPS). Even though a desktop Mac Mini only uses wall power, passing-aguarantees the setting writes to every internal profile profile in/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist.disablesleep 1: Completely disables system-level sleep. This is an absolute kernel override. It stops the operating system from suspending the CPU or powering down networking components when the machine sits idle.disksleep 0: Prevents internal flash storage and connected external USB or Thunderbolt drives from spinning down or entering low-power idle states.womp 1: Stands for "Wake on Magic Packet." It enables Wake-on-LAN across your physical Ethernet interface (en0), allowing you to wake the hardware remotely if it ever gets shut down.autorestart 1: Instructs the hardware power management controller (SMC) to automatically reboot the Mac Mini the instant power returns after an outage.
To verify that your settings applied properly, inspect the active kernel power assertions:
pmset -g assertions
Look closely at the output for PreventUserIdleSystemSleep and NoIdleSleepAssertion. Both flags must read 1. If a background process requests an idle state, disablesleep 1 acts as a hard ceiling, preventing the operating system from shutting down your network adapters.
To confirm the persistent configuration profile, run:
pmset -g custom
Check the AC Power block to make sure Sleep, Disk Sleep, and Display Sleep reflect your manual inputs.
You can verify your current disk encryption status by running:
fdesetup status
If it returns FileVault is On, an unassisted reboot will leave your Mac Mini locked at an EFI pre-boot screen. The network interfaces will stay completely uninitialized, your Tailscale or WireGuard tunnel will not start, and SSH connections will be rejected. On a secure home network, turning FileVault off for a dedicated server node is usually the most dependable operational choice.
Bypassing the TCC Permission Wall
The biggest hurdle on an unattended macOS server is Apple’s Transparency, Consent, and Control (TCC) security framework.
On a standard Linux server, permissions follow predictable POSIX rules: user, group, and mode bits (chmod 755, chown -R). If a service runs as root or belongs to the proper group, it can read your data drives without friction.
macOS works differently. Even if a background process runs as root through sudo, TCC halts disk access for protected paths until a user physically clicks "Allow" on a desktop dialog box. These protected locations include:
~/Documents~/Downloads~/Desktop- Mounted external drives (
/Volumes/*) - Network shares (SMB and NFS mounts)
If your automated backup script, media indexer, or sync tool runs unattended over SSH, it cannot display an interactive dialog box. Instead of asking for permission, the kernel kills the operation immediately with an unhelpful Operation not permitted error.
You can work around this sandbox behavior on a headless system by following three rules:
1. Store Server Data Outside User Home Directories
Never store container volumes, database files, or media libraries inside /Users/<username>/Documents or similar standard user folders.
Create dedicated directories directly at the root of the filesystem or under /srv:
# Create dedicated directories outside standard user folders
sudo mkdir -p /srv/storage /srv/data /srv/backups
# Assign ownership to your primary admin user
sudo chown -R $(whoami):staff /srv
# Set standard read, write, and execute permissions
chmod 755 /srv/storage /srv/data /srv/backups
Directories created under /srv, /opt, or /Users/Shared bypass Apple's user-folder sandboxing checks, preventing background daemons from tripping over unexpected TCC prompts.
2. Grant Full Disk Access in Advance
Before you unplug the display and stash the machine away, configure permissions through the desktop interface:
- Open System Settings > Privacy & Security > Full Disk Access.
- Click the
+button at the bottom of the list. - Press
Cmd + Shift + Gto bring up the path selection drawer. - Add your shell binaries:
/bin/bash,/bin/zsh, and any third-party shells installed via Homebrew (such as/opt/homebrew/bin/bashor/opt/homebrew/bin/fish). - Add your terminal multiplexer, such as
/opt/homebrew/bin/tmux.
3. Handle the SSH Remote Daemon Explicitly
When you connect to macOS remotely over an SSH connection, your shell runs inside a subshell managed by /usr/sbin/sshd. By default, macOS isolates this daemon inside an unprivileged security sandbox.
To allow commands run over SSH to manage files across all storage paths:
- In Full Disk Access, click the
+icon. - Press
Cmd + Shift + G. - Type
/usr/sbin/sshdand press Enter. - Toggle the switch next to
sshdto Enabled.
# Test file read access in a protected location over SSH
ls -la /Library/Application\ Support
If the command lists the directory contents without throwing Operation not permitted, your SSH session holds full administrative filesystem access.
What Udon Brings to Darwin Servers
Once power settings and filesystem permissions are configured, you need a straightforward way to monitor hardware and manage background services. This is where Udon comes into play.
On a Linux machine, web dashboards like Cockpit interact directly with systemd and D-Bus APIs. Darwin does not run systemd. macOS uses launchd, Apple’s unified service management framework, which relies on XML property lists (.plist files) stored across /Library/LaunchDaemons and ~/Library/LaunchAgents. Generic server management tools often struggle on macOS because they search for /etc/systemd/system and find nothing.
┌─────────────────────────────────────────────────────────┐
│ Web Browser │
│ (Local LAN or Tailscale Network) │
└────────────────────────────┬────────────────────────────┘
│ HTTP / HTTPS Management
┌────────────────────────────▼────────────────────────────┐
│ Udon Daemon │
│ • Hardware thermals • Memory pressure │
│ • launchd management • Process health │
└──────────────┬───────────────────────────┬──────────────┘
│ │
Darwin Calls │ VM API Calls │
┌──────────────▼──────────┐ ┌──────────────▼──────────────┐
│ macOS Bare Metal │ │ Virtualization.framework │
│ • CoreAudio engines │ │ • OrbStack / Colima VM │
│ • Native arm64 binaries│ │ • VirtioFS directory sync │
│ • Apple Silicon Media │ │ • Docker & Linux engines │
└─────────────────────────┘ └─────────────────────────────┘
Udon runs directly on bare metal as a lightweight native Go binary, providing an interface tailored to Apple Silicon hardware:
- Unified Memory Pressure: Standard Unix utilities like
topor generic server dashboards report "free memory." On macOS, this figure is misleading. The Darwin kernel actively caches file operations in unused RAM, meaning available free memory will frequently drop below 500 MB even when the system is sitting idle. Udon displays Apple's native Memory Pressure metric instead. This metric uses three simple states (Normal, Warning, Critical) to tell you if the system is actually starved for RAM or just caching data intelligently. - Hardware Thermals and Power: Udon queries the system management controller directly to surface per-core CPU and GPU cluster temperatures, total package wattage draw, and internal fan RPMs without requiring third-party kernel extensions.
- Native Service Control: Udon reads active
launchdjobs, letting you start, stop, and restart background services from a web browser without having to runlaunchctl kickstartorlaunchctl bootoutmanually inside a terminal.
This native approach avoids the high memory footprint of Electron-based desktop monitoring tools, keeping idle system consumption low.
Docker on macOS: OrbStack vs. Colima
While Udon handles native Darwin processes, the reality of modern self-hosting is that most homelab applications are packaged as Linux container images.
Because Darwin cannot run Linux ELF binaries natively, your Mac Mini must run a lightweight Linux virtual machine behind the scenes using Apple's Virtualization.framework.
Running Docker Desktop on a headless server causes unnecessary friction:
- It is built for interactive desktop use, not background server operation.
- It consumes between 2 GB and 4 GB of RAM just running idle helper tasks.
- It frequently fails to start after an unattended reboot because it expects an active graphical login session.
Homelab operators running headless Apple Silicon hardware generally choose between two alternatives: OrbStack and Colima.
# /srv/storage/docker-compose.yml
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /srv/storage/caddy/data:/data
- /srv/storage/caddy/Caddyfile:/etc/caddy/Caddyfile
Here is how the top container runtimes compare on an Apple Silicon Mac Mini:
| Criteria | OrbStack | Colima | Docker Desktop |
|---|---|---|---|
| Idle Memory Overhead | 150 MB – 300 MB | 1.0 GB – 1.8 GB | 2.5 GB – 4.0 GB |
| File Sync Driver | Custom VirtioFS engine | Native VirtioFS | VirtioFS / gRPC-FUSE |
| Headless Boot Reliability | Starts automatically via launchd | Requires user launchd agent | Stalls waiting for GUI login |
| Rosetta 2 x86 Emulation | Native integration | Supported via vz hypervisor |
Supported |
| Licensing Model | Free for personal use | Open Source (Apache 2.0) | Paid subscription tiers |
Setting Up Colima for Unattended Server Duty
OrbStack provides the lowest memory footprint, but it is proprietary software. If you want a completely open-source virtualization layer, Colima is the standard option. It wraps Apple's native Virtualization.framework (vz) and utilizes VirtioFS for fast host-to-guest directory sharing.
Install Colima and the Docker client tools using Homebrew:
brew install colima docker docker-compose
Start Colima with hardware acceleration, Rosetta translation for legacy x86 container images, and dedicated resources:
colima start \
--vm-type=vz \
--vz-rosetta \
--mount-type=virtiofs \
--cpu 4 \
--memory 4 \
--disk 60
Here is what these arguments do:
--vm-type=vz: Replaces legacy QEMU emulation with Apple’s native hypervisor framework, reducing CPU overhead.--vz-rosetta: Enables Apple’s Rosetta translation layer inside the Linux guest VM. This lets you run olderlinux/amd64container images at near-native speeds on the Apple Silicon ARM CPU.--mount-type=virtiofs: Routes directory mounts through shared memory rather than standard network file shares, significantly speeding up database reads and writes.
To make sure Colima starts automatically whenever your Mac Mini boots up—without requiring you to open an SSH session—create a launch daemon:
Create a file named ~/Library/LaunchAgents/com.user.colima.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.colima</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/colima</string>
<string>start</string>
<string>--foreground</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/colima.stdout.log</string>
<key>StandardErrorPath</key>
<string>/tmp/colima.stderr.log</string>
</dict>
</plist>
Load the configuration into launchd:
launchctl load -w ~/Library/LaunchAgents/com.user.colima.plist
Your container runtime will now boot cleanly alongside the operating system.
Common Headless Gotchas
Running macOS without an active display session exposes a few unique quirks. Keep an eye on these specific failure modes:
- Virtual Disk Expansion: Both Colima and OrbStack create dynamic sparse disk images on your APFS filesystem. When containers download large layers or write temporary cache files, the virtual disk expands automatically. When you delete those containers, APFS does not automatically reclaim that space. Run pruning routines regularly to clear unused layers:
docker system prune -a --volumes - CoreAudio Sleep Locks: If you host a media streaming server (such as Navidrome or Plex) that touches system audio drivers, the macOS
coreaudiodservice can occasionally pause background threads if an audio output device unexpectedly disappears. Avoid mapping audio output to an external HDMI monitor that gets disconnected. Lock your default output device to the internal hardware speaker. - Automatic OS Updates: macOS defaults to downloading and installing point updates overnight, which will reboot your server when you least expect it. Keep auto-updates disabled or carefully scheduled. Always ensure your SSH keys and remote networking tools (like Tailscale) are set to launch at boot so you do not lose remote access after an update finishes.
- Network Interface Flapping: If your Mac Mini has both Wi-Fi and physical Ethernet enabled, macOS may switch default routes after a reboot, changing your local IP address. Lock your traffic to the wired connection by setting the interface service order in your network configuration:
sudo networksetup -ordernetworkservices "Ethernet" "Wi-Fi"
FAQ
Can you run a headless home server on macOS without a display attached?
Yes. Apple Silicon Macs (M1 through M4) initialize their graphics and unified memory controllers properly on boot without a physical monitor or HDMI dummy plug connected. You can administer the machine entirely over SSH, a VPN, or a native web dashboard like Udon.
How does Udon manage macOS services compared to Linux tools like Cockpit or Webmin?
Linux dashboards rely on systemd and D-Bus APIs to read process states and trigger system actions. Udon interfaces directly with Darwin tools: it interacts with launchd for process management, reads memory pressure through Apple's native kernel metrics, and monitors per-core thermal sensors across the M-series SoC.
How do you stop macOS from going to sleep when running as a headless server?
Run sudo pmset -a disablesleep 1 disksleep 0 womp 1 autorestart 1 in your terminal. This instructs the kernel to ignore idle sleep requests across all power profiles, prevents storage drives from entering low-power spindown, enables Wake-on-LAN, and automatically boots the hardware back up if power drops.
What is the best way to run Docker containers on an Apple Silicon Mac Mini server?
For an unattended server, avoid Docker Desktop. It consumes significant memory at idle and requires an interactive graphical session. Instead, use OrbStack for low memory consumption, or Colima configured with --vm-type=vz and --mount-type=virtiofs for a completely open-source virtualization stack.
The Verdict
Using an Apple Silicon Mac Mini as a home server gives you exceptional compute density at an idle draw of just 5 watts. Udon helps bridge the administrative gap by giving Darwin a dedicated server interface, replacing manual terminal commands with a clean view of your system health and background daemons.
If your setup consists entirely of standard Linux containers and you do not need macOS, an entry-level x86 mini PC running Debian or Proxmox provides a more direct path with native container support. But if you have an Apple Silicon Mac sitting in a drawer, combining Udon, clean pmset rules, and a lightweight container runner turns that desktop hardware into a capable, quiet, and power-efficient homelab node.
Related in this cluster
- /en/posts/nso-whatsapp-exploit-analysis-hardening-messaging-bridges/
Ad space · not an Umbrel endorsement