← blog

Auto-login on a headless Mac, and why macOS 27 skips it

September 24, 2026 · 8 min read

A rented Mac has to survive a reboot on its own. No one is in the basement to type a password. The customer's browser desktop attaches to the graphical login session, so the machine has to come back logged in. Headless auto-login on Apple Silicon is a small pile of specific details, and four of them fail silently. One of them is new in macOS 27. Here they are.

The two settings that arm it

Auto-login is two pieces. A preference naming the user, and the password stored where the login window can read it.

# who logs in automatically
defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser runner

# the password, XOR-obfuscated, in /etc/kcpassword
# (padded to a multiple of 12 bytes, keyed with a fixed vector)

/etc/kcpassword is obfuscated with a fixed key, not encrypted. Anyone who can read the file can recover the password. That is acceptable only because the machine is reachable only over a private network, never the public internet. On a box with an open port it would be a credential sitting in the open.

The four silent stops

FileVault

FileVault encrypts the disk, and the disk must be unlocked before macOS can log anyone in. On a machine with a keyboard a human types the unlock. Headless, there is no one to type it, so auto-login and FileVault are mutually exclusive on a box you cannot touch. We run with FileVault off and lean on the private network as the boundary.

The screen-lock flag

There is a preference that re-locks the screen even after auto-login, intended for machines that lock on display sleep. If it is set, the session comes up but the screen is locked, which a remote viewer sees as black. We clear autoLoginUserScreenLocked whenever we arm auto-login.

The panic latch, which cost us a day

The subtle one. When a login session fails, macOS records lastLoginPanic in the login window preferences. It then deliberately skips auto-login on the next boot and shows the login screen instead. It is a safety feature, so a machine that crashes on login does not loop.

On a freshly enrolled Mac, the very first boot was busy indexing a large Xcode copy under a load average over 160. The login did not finish in time and the latch was set. Every boot after that skipped auto-login, on a machine whose configuration was completely correct.

sudo defaults delete /Library/Preferences/com.apple.loginwindow lastLoginPanic
sudo shutdown -r now

Cleared on a settled machine, auto-login worked immediately, with the same password and the same settings that had been in place all along. We nearly wrote this up as a bug in a new macOS release. That time it was not. The proof was a control: the same symptom cleared once the machine was not overloaded, and the underlying config had never been wrong. That story sits alongside the others in checks that cannot fail.

macOS 27: the latch on every boot

Then we enrolled our first M6 minis on macOS 27, and the latch came back. This time the machines were idle and settled. They still sat at the login window after every reboot, with lastLoginPanic set again each time.

Our image ships one LaunchAgent in /Library/LaunchAgents. It keeps an AI coding agent running in the customer's session. The script inside it already did nothing unless the desktop was up. So we suspected the file, not the script.

The test was a control. We took two identical M6 units in the same state. One had the plist, one did not. We rebooted them three seconds apart. The one without the plist logged in. The one with it panicked and stopped at the login window. Putting the plist back on a unit that had logged in fine stranded it again on the next boot. So it is every boot, not only the first.

On macOS 27, the login window loads LaunchAgents from /Library/LaunchAgents during the auto-login handshake. On our machines, that load was enough to fail the login and set the latch. Clearing the key does not help, because the next boot sets it again.

# the fix: keep the plist out of the auto-load path
sudo mv /Library/LaunchAgents/dev.example.agent.plist /opt/example/launchd/

# then load it into the user's session after login, from a root daemon
launchctl bootstrap gui/$(id -u runner) /opt/example/launchd/dev.example.agent.plist

We moved the plist out of /Library/LaunchAgents. A small root LaunchDaemon now waits for the user's session to exist and for the Dock to be running. Then it loads the agent into that session. Auto-login works on every boot, and the agent starts about a minute later.

Two more macOS 27 details bit us along the way. First, macOS 27 locks the screen a few seconds after auto-login. When it is locked, /dev/console is owned by root, so do not use it to check whether someone is logged in. Check for the user's gui/UID domain and a running Dock instead. Second, the first-run Setup Assistant is still attached to the session. Killing it drops the whole session to the login window and sets the latch again. Suppress it with its preference keys, and never kill it.

The working recipe

Questions

How does macOS auto-login store the password?
In /etc/kcpassword, obfuscated with a fixed XOR key, plus autoLoginUser in the loginwindow preferences. It is obfuscation, not encryption, which is why the machine must be reachable only over a private network.
Does FileVault break auto-login?
Yes. With FileVault on, the disk unlock happens before macOS can auto-login, so a headless machine cannot get past the pre-boot screen on its own. Auto-login needs FileVault off.
Why did auto-login work once and then stop?
A failed login sets lastLoginPanic in the loginwindow preferences, and macOS then skips auto-login on later boots. Clear that key and reboot on a settled machine.
Why does auto-login fail on macOS 27 when it worked on macOS 26?
On macOS 27, a LaunchAgent plist in /Library/LaunchAgents is loaded during the auto-login handshake. On our machines that load latched lastLoginPanic on every boot, and the Mac stopped at the login window. Moving the plist out of /Library/LaunchAgents and loading it after login fixed it.

Run your own numbers on the calculator or lease a runner.