My VM Was Frozen Half the Time and Nothing Told Me
I run Linux VMs under Lima on a MacBook Pro that lives on mains with the lid closed, and I reach them over Tailscale from elsewhere. SSH into them would hang. Not always — sometimes.
There was no error to chase. No refused connection, no timeout, no log line. The
host was up, limactl list said Running, and half the time it worked fine.
Prior art first, so you can leave if that’s what you need: the fix is
sudo pmset -a disablesleep 1, and Sleepless already documents that
properly — what the flag does, why assertion-based apps like Amphetamine and
KeepingYouAwake lose at lid-close, and that it works on Apple silicon. I’m not
going to re-explain any of it.
This post is the part I couldn’t find anywhere: that a Virtualization.framework guest can lose half its wall-clock life to the host’s own maintenance sleeps without emitting a single error, how to detect that, and what the same flag does to Power Nap.
Frozen half its life
When the host sleeps, Virtualization.framework suspends the guest and its clock
stops. tailscaled inside the guest notices:
monitor: time jump detected (slept 15m20s), probably wake from sleep
monitor: time jump detected (slept 16m8s), probably wake from sleep
monitor: time jump detected (slept 17m23s), probably wake from sleep
Nineteen in one day. 235 minutes — suspended for roughly half its wall-clock life, in twelve-to-seventeen minute blocks with one or two minutes of consciousness in between. SSH arriving during a block has nothing to answer it. That’s the entire bug, and it never produced an error message.
The host wasn’t idle-sleeping, which is what I’d have expected. It was taking scheduled maintenance sleeps:
Sleep Entering Sleep state due to 'Maintenance Sleep':TCPKeepAlive=active
Sleep Entering Sleep state due to 'Sleep Service Back to Sleep'
57 of my 63 sleep events since boot were those two — while Amphetamine held
PreventUserIdleSystemSleep continuously for 23h58m. The app wasn’t broken. That
assertion has no say over this category, so it sat there being held while the
machine slept 57 times underneath it.
Two ways to detect it
If the guest runs Tailscale, it’s free:
sudo journalctl -b | grep 'time jump detected'
If it doesn’t, uptime lies — and the lie is the measurement. /proc/uptime
doesn’t advance while the guest is frozen, so uptime -s reported a boot time
3h50m later than the journal’s first entry for that same boot, almost exactly the
3h55m of measured freeze:
uptime -s
sudo journalctl -b -o short-iso | head -1
Watch that gap over a day. If it grows, you’re being frozen. It needs nothing installed and works on any Linux guest.
Every resume costs more than the freeze. tailscaled follows each time jump
with LinkChange: major, rebinding … rebind-reason=[time-jumped(16m3s)], so
connections that survived the freeze get torn down on the way out.
One trap if you go measuring on the host: pmset -g log retains about a week of
history, not just the current boot. I divided a week of events by one boot’s
uptime and scared myself with a number six times too large. Filter by date and
cross-check Total Sleep/Wakes since boot.
Does the flag stop Power Nap too?
This is the question I couldn’t find answered. disablesleep obviously stops idle
and lid-close sleep. But does it stop the maintenance cycles — the ones doing
the damage here? Nobody I could find had run SleepDisabled=1 and reported
whether Maintenance Sleep still shows up in pmset -g log.
It stops them, with powernap left at 1. Eighty minutes after setting the
flag, no console input for the whole window:
host sleep events since the flag: 0
VM time-jump events since flag: 0
The pre-flag cadence was metronomic — one freeze roughly every 17 minutes: 05:41,
05:58, 06:17, 06:35, 06:52, 07:09, 07:27, 07:45, 08:01, 08:18, 08:35. That window
should have produced four or five. It produced none, and the uptime gap agreed
independently by holding at 3h50m instead of growing.
Which makes sense: Apple documents Power Nap as activating “when your Mac goes to sleep”. Veto the transition and nothing is left to trigger it. So you don’t need to disable Power Nap — just as well, since on Apple silicon you can’t.
Honest limit: eighty minutes is four or five expected events, not the dozen an overnight run gives, on one M3 Max on one OS version.
What the source says that the write-ups don’t
The flag is usually described as a lid-close trick. The kernel is broader than
that. In XNU’s IOPMrootDomain.cpp the gate carries Apple’s own comment:
if (userDisabledAllSleep)
{
err = 1; // 1. user-space sleep kill switch
The comment above it scopes that gate to “idle and demand system sleep”, which
undersells its own call sites. Grep them and you find
kIOPMSleepReasonClamshell, kIOPMSleepReasonLowPower,
kIOPMSleepReasonPowerButton — and kIOPMSleepReasonThermalEmergency.
That last one matters, and I haven’t seen it mentioned anywhere. The kill switch refuses thermal-emergency sleep too. So the flag doesn’t merely keep your Mac awake; it declines the sleep the machine would otherwise use to cool itself. Minutes after mine went on, 114 of these appeared in a two-minute burst:
ThermalEvent Ignored DarkWake thermal emergency signal
Nothing came of it — pmset -g therm showed no warning and no throttling, and it
never repeated. A hard SMC-level shutdown isn’t a sleep request, so that backstop
survives, but it’s a blunter one. If you’re doing this to a closed laptop, watch
the temperature. (Caveat: that source tree is older than what ships on 26.6.1.)
Three small corrections to what’s written down
- The plist has moved. Every source I found — Wikipedia,
DSSW — says pmset settings persist in
/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist. On 26.6.1 that path doesn’t exist. It’s now/Library/Preferences/com.apple.PowerManagement.plist. An NVRAM reset still wipes it. pmset darkwakesisn’t a key on 26.6.1 — not in the man page, not inpmset -g custom. If you’ve seen it suggested, it won’t do anything here.- Masking the guest’s
sleep.targetis a real fix for a different Lima problem, not this one. My guest never self-suspended:systemd-suspend.servicehad no entries at all. It was frozen from outside, and nothing inside Linux can refuse that.
Takeaway
The interesting failure wasn’t a sleep setting. It was that a machine can lose
half its life to suspension and report nothing at all. pmset -g log is ground
truth for what the host did; if there’s a VM involved, its own clock is ground
truth for what it lost.