Skip to content

feat(autostart): add LaunchDaemon support for headless macOS servers#4984

Open
resker wants to merge 8 commits into
lima-vm:masterfrom
resker:feature/launchdaemon-support
Open

feat(autostart): add LaunchDaemon support for headless macOS servers#4984
resker wants to merge 8 commits into
lima-vm:masterfrom
resker:feature/launchdaemon-support

Conversation

@resker
Copy link
Copy Markdown

@resker resker commented May 15, 2026

Closes #4983

Summary

Adds limactl autostart enable --condition=boot to register a Lima instance
as a system-level LaunchDaemon. The VM starts at boot without requiring a user
login session, enabling headless macOS server deployments.

Also unifies the existing start-at-login functionality under the new
limactl autostart command and deprecates limactl start-at-login.

Commands

# Start at user login (LaunchAgent on macOS, systemd user service on Linux)
limactl autostart enable k3s

# Start at system boot, before any user login (macOS only, prompts for sudo)
limactl autostart enable --condition=boot k3s

# Remove registration (auto-detects which type is installed)
limactl autostart disable k3s

Design

Privilege model: limactl itself stays non-root (the existing root check
is not bypassed). The --condition=boot path execs sudo internally for
exactly two operations — writing to /Library/LaunchDaemons/ and running
launchctl bootstrap system — and prompts for a password once at install time.

UserName plist key: launchd runs the daemon process as the specified user
(defaulting to $USER), so the Lima instance runs under the correct user's
home directory and config without requiring that user to be logged in.

Runtime: the daemon plist calls limactl start <instance> --foreground,
consistent with the existing LaunchAgent approach. Normal limactl start/stop
continues to work; no privileged runtime operations are needed.

Changes

  • pkg/autostart/launchd/io.lima-vm.daemon.INSTANCE.plist — daemon plist template
  • pkg/autostart/launchd/launchd.goGetDaemonPlistPath, DaemonServiceNameFrom, DaemonTemplate
  • pkg/autostart/managers.goextraTemplateVars field on TemplateFileBasedManager
  • pkg/autostart/managers_darwin.goDaemonManager(userName string) constructor
  • pkg/autostart/managers_linux.go, managers_others.goDaemonManager stubs
  • cmd/limactl/autostart.golimactl autostart command group (cross-platform)
  • cmd/limactl/autostart_darwin.go — macOS: login → LaunchAgent, boot → LaunchDaemon
  • cmd/limactl/autostart_others.go — non-macOS: login only, boot returns unsupported error
  • cmd/limactl/start-at-login.go — marked deprecated
  • cmd/limactl/main.go — register autostart subcommand
  • website/content/en/docs/usage/autostart.md — new documentation subpage

Testing

  • Unit tests added for GetDaemonPlistPath, DaemonServiceNameFrom, and daemon plist rendering
  • Manually tested on Apple Silicon Mac Mini (macOS 26, arm64) running a k3s instance
  • VM started automatically after reboot with the LaunchDaemon installed
  • HA (Home Assistant in k3s) confirmed healthy post-reboot

Notes

  • --condition=boot is macOS only; other platforms return an unsupported error
  • Developed with AI assistance

By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache 2.0 license and I have signed off all commits per the DCO.

resker added 2 commits May 14, 2026 22:22
Introduces `limactl daemon install/uninstall` to register Lima instances
as system LaunchDaemons, enabling VM startup at boot without requiring a
user session or auto-login.

LaunchAgents (the existing `start-at-login` mechanism) require a GUI
login session and do not start on headless macOS servers. A LaunchDaemon
with a `UserName` key runs as a specified user at system boot, before
any login, solving this gap.

Changes:
- pkg/autostart/launchd: add DaemonTemplate, GetDaemonPlistPath,
  DaemonServiceNameFrom; restore RequestStop to its original form
- pkg/autostart/managers: add extraTemplateVars to
  TemplateFileBasedManager to support templates requiring additional
  variables (e.g. UserName)
- pkg/autostart/managers_darwin: add DaemonManager(userName string) for
  rendering daemon plists and tracking registration state
- pkg/autostart/managers_{linux,others}: add DaemonManager stubs
  returning notSupportedManager
- cmd/limactl: add `daemon` subcommand with `install` and `uninstall`;
  runs as normal user; privileged operations (writing to
  /Library/LaunchDaemons/ and launchctl system domain) are performed via
  sudo internally so limactl itself never runs as root
- tests: add TestGetDaemonPlistPath, TestDaemonServiceNameFrom, and a
  daemon plist render test verifying UserName substitution

Developed with AI assistance.

Signed-off-by: Robert Esker <resker@gmail.com>
Signed-off-by: Robert Esker <resker@gmail.com>
@resker resker force-pushed the feature/launchdaemon-support branch from f2ca93d to 0c20526 Compare May 15, 2026 03:22
Signed-off-by: Robert Esker <resker@gmail.com>
Comment thread cmd/limactl/daemon.go Outdated
Comment thread website/content/en/docs/security/_index.md
Comment thread website/content/en/docs/usage/_index.md Outdated
Unifies LaunchAgent (login) and LaunchDaemon (boot) management under a
single `limactl autostart enable/disable` command per maintainer feedback.

- `limactl autostart enable --condition=login` (default): installs a
  LaunchAgent on macOS or a systemd user service on Linux, equivalent to
  the existing `start-at-login` command
- `limactl autostart enable --condition=boot --user=$USER`: installs a
  system LaunchDaemon on macOS (macOS only, prompts for sudo)
- `limactl autostart disable`: removes whichever registration is present

`limactl start-at-login` is kept but marked deprecated in favor of
`limactl autostart enable`.

Adds a dedicated usage/autostart.md subpage for the documentation.

Signed-off-by: Robert Esker <resker@gmail.com>
Comment thread cmd/limactl/autostart.go Outdated
Comment thread website/content/en/docs/usage/autostart.md
Comment thread cmd/limactl/start-at-login.go Outdated
- Call Flags() once in newAutostartEnableCommand via local var
- Mark autostart docs as requiring Lima >= 2.2

Signed-off-by: Robert Esker <resker@gmail.com>
@resker resker marked this pull request as ready for review May 15, 2026 04:02
@AkihiroSuda
Copy link
Copy Markdown
Member

@resker May I confirm that your commits were DCO-signed by you human, not by a bot?
Asking because the commits were pushed extremely quickly after my review 🙂

- Shorten start-at-login deprecated message to "use limactl autostart instead"
- Add Lima < 2.2 section to autostart.md documenting limactl start-at-login
- Add limactl start-at-login to deprecated features page

Signed-off-by: Robert Esker <resker@gmail.com>
Comment thread website/content/en/docs/releases/deprecated.md Outdated
Signed-off-by: Robert Esker <resker@gmail.com>
@resker
Copy link
Copy Markdown
Author

resker commented May 15, 2026

@resker May I confirm that your commits were DCO-signed by you human, not by a bot? Asking because the commits were pushed extremely quickly after my review 🙂

@AkihiroSuda - yes, confirmed... I am not now nor have I ever been a bot :-) The turnaround assisted by AI tools (everything reviewed and submitted by me though).

Signed-off-by: Robert Esker <resker@gmail.com>
@AkihiroSuda
Copy link
Copy Markdown
Member

@resker May I confirm that your commits were DCO-signed by you human, not by a bot? Asking because the commits were pushed extremely quickly after my review 🙂

@AkihiroSuda - yes, confirmed... I am not now nor have I ever been a bot :-) The turnaround assisted by AI tools (everything reviewed and submitted by me though).

Sorry for doubting you 😅
You are very productive!

@AkihiroSuda AkihiroSuda added this to the v2.2.0 milestone May 15, 2026
Copy link
Copy Markdown
Member

@AkihiroSuda AkihiroSuda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM after squashing commits.

WIll merge after the release of v2.1.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: LaunchDaemon support for headless macOS servers

2 participants