Caffeine is a macOS menu bar app that keeps your Mac awake, including with the lid closed.
- Keep Awake prevents idle system and display sleep. Turn it on and off in the menu bar card, or from any app with ⌃⌥⌘A.
- Closed Lid Mode keeps the Mac awake with the lid closed while Keep Awake is on. It's on by default.
- Turn Off Screen on Close turns off only the built-in display when the lid closes while Keep Awake is active. External displays stay on. It's on by default.
- Lock Screen on Close locks the Mac when the lid closes while Keep Awake is active.
- Only When Charging keeps the Mac awake only while it's connected to external power.
- Timer: No Limit, 15 min, 30 min, 1 hour, 2 hours, 4 hours or 8 hours.
- A short sound confirms that Keep Awake is active.
- At first launch Caffeine sets itself to open at login, with Keep Awake off. You can turn this off in System Settings.
- Export Log… saves a diagnostic log to attach to a bug report.
- macOS 26 or later. Caffeine is developed and tested on macOS 27.
- Caffeine must run from
/Applications/Caffeine.appto set up its background service. - Turn Off Screen on Close and Lock Screen on Close need a Mac notebook. On a desktop Mac they do nothing.
- Download the app from the latest release.
- Move
Caffeine.appto the Applications folder and open it from there. - The setup screen asks you to turn on Caffeine in System Settings → General → Login Items & Extensions. This allows its background service. macOS may ask for an administrator password.
Caffeine appears in the menu bar. Click the coffee cup to open its card.
Caffeine has two parts: the menu bar app and a background service, CaffeineHelper. The service is a launchd daemon that the app registers with SMAppService. It runs as root, because only root can change the system-wide sleep setting that Closed Lid Mode needs.
- Keep Awake. The service holds two power assertions, against idle system sleep and idle display sleep, like
caffeinate -di. Without Closed Lid Mode, no setting changes. - Closed Lid Mode. The service also runs
pmset -a disablesleep 1, andpmset -a disablesleep 0when the session ends, reading the setting back after each change. Before the first change it saves a recovery record in/Library/Application Support/com.serhiital.Caffeine. After a crash or a power loss, the service restores the setting from that record when it starts again. If sleep was already disabled by something else, Caffeine doesn't start Keep Awake, leaves the setting alone and says so. - Supervision. A session belongs to the running app, which renews it every 5 seconds. If the app quits or crashes, the service ends the session at once; if the app stops responding, after 20 seconds. When the Mac goes to sleep, the service releases the hold; after the Mac wakes, the hold resumes with the app's next renewal. Quitting Caffeine waits until the service confirms that the sleep setting is restored. If that can't be confirmed, or the built-in display can't be turned back on, Caffeine stays open and shows what needs attention.
- Lid actions run in the app. Turn Off Screen on Close disconnects the built-in display through a display configuration that macOS reverts when the app exits; when the built-in display is the only one, it runs
pmset displaysleepnowinstead. Lock Screen on Close calls the system's lock screen function. Both features use private macOS interfaces, so a future macOS version may break them.
- Caffeine has no network code: no analytics, no telemetry and no update checks.
- Logs stay on your Mac:
~/Library/Logs/Caffeine/Caffeine.logfor the app and/Library/Logs/Caffeine/CaffeineHelper.logfor the service. Each log keeps at most 1 MB plus four older files. The logs record Caffeine's actions and errors, never passwords, documents or screen contents. - Export Log… saves both logs to a file you choose, with a summary: the versions of Caffeine and macOS, the Mac model, the settings, the service status and the names of recent crash reports. Nothing is sent anywhere.
The service accepts connections only from the Caffeine app signed by the same developer team, and the app talks only to the matching service. SECURITY.md describes the security model and how to report a vulnerability.
You need Xcode 27 or later and a signing certificate issued by Apple (see below). Clone the repository and open Caffeine.xcodeproj.
The app and its service trust each other only through code signatures, and the expected identity is compiled into both. A build signed by another team can't set up or reach the service until you change the Team ID in both places listed below. Change the other values too, so that your build can't collide with the official app, and keep them consistent:
| Value | Where to change it |
|---|---|
| Team ID | Team in Signing & Capabilities of both targets, for Debug and Release (DEVELOPMENT_TEAM)teamIdentifier in Core/Sources/CaffeineServiceProtocol/ServiceProtocol.swift |
| App identifier | Bundle Identifier of the Caffeine target, for Debug and ReleaseappIdentifier in ServiceProtocol.swift |
| Service identifier | Bundle Identifier of the CaffeineHelper target, for Debug and ReleasehelperIdentifier and machService in ServiceProtocol.swiftLabel and the key under MachServices in the launchd plist |
| launchd plist name | The file in Service/; rename it in the Xcode project navigator so the Embed LaunchDaemon build phase keeps itdaemonPlist in ServiceProtocol.swift |
| Recovery folder | productionDirectory in Core/Sources/CaffeineSystemPower/RecoveryJournal.swift |
Keep these rules:
- The service identifier must be the same string in all five places: the helper's bundle identifier,
helperIdentifier,machService, the plist'sLabeland itsMachServiceskey. The helper is signed with its bundle identifier as its code signing identifier (--identifierin the helper's Other Code Signing Flags); keep that flag. daemonPlistmust match the plist's file name.- Sign both targets with an Apple-issued certificate of your team: Apple Development for your own Mac, Developer ID Application for builds you give to others. Both sides check the requirement
anchor apple generic and identifier "…" and certificate leaf[subject.OU] = "<Team ID>", which an ad hoc signature ("Sign to Run Locally") can't meet. - Keep the product names
CaffeineandCaffeineHelper. The app sets up its service only when it runs from/Applications/Caffeine.app, and the plist startsContents/MacOS/CaffeineHelper(seeinstallationProbleminCaffeine/SystemServiceSetup.swift). - Signatures don't cover the recovery folder, but give your build its own, so it never shares recovery state with another copy of Caffeine.
Quit Caffeine if it's running, then build and install it:
xcodebuild -project Caffeine.xcodeproj -scheme Caffeine -configuration Debug -derivedDataPath build
rm -rf /Applications/Caffeine.app
ditto build/Build/Products/Debug/Caffeine.app /Applications/Caffeine.app
open /Applications/Caffeine.appA running service hands over only to a replacement signed like itself. If a copy of Caffeine signed by another team was installed before, restart the Mac after installing your build, so that the old service process ends.
The Caffeine UI Preview scheme runs a Debug build with --ui-preview: a simulated session that works from any folder, installs nothing and changes no power setting. Turn on the --preview-on-battery argument in the scheme to simulate battery power.
swift test --package-path Core- Every helper change you ship needs a higher
helperBuildinServiceProtocol.swiftand, by convention, a higher build number (Current Project Version) in both targets. The app compareshelperBuildwith the running service and offers to update an older one once it's idle. The running service also restarts itself when a correctly signed replacement is installed and no session is active. - A change to the messages (
ServiceRequest,ServiceReply,ServiceSnapshot) also needs a higherprotocolVersion.
| Path | Contents |
|---|---|
Caffeine/ |
The menu bar app (SwiftUI) |
CaffeineHelper/ |
The background service |
Core/ |
Swift package with the session logic, service protocol, sleep backend, logging and their tests |
Service/ |
The launchd property list embedded in the app |
- Choose Quit Caffeine (⌘Q) in its card. Quitting turns off Keep Awake and waits until the sleep setting is restored.
- Delete
Caffeine.appfrom Applications. macOS removes the service registration with it. If Caffeine is still listed in System Settings → General → Login Items & Extensions, turn it off there. - Restart the Mac, so that the service process ends if it's still running.
- Check that sleep is allowed:
pmset -g | grep SleepDisabledshould print0or nothing. If it prints1, runsudo pmset -a disablesleep 0. - Optionally, remove the logs, the recovery folder and the preferences:
sudo rm -rf /Library/Logs/Caffeine "/Library/Application Support/com.serhiital.Caffeine"
rm -rf ~/Library/Logs/Caffeine
defaults delete com.serhiital.Caffeine- "Install the signed Caffeine app in Applications…" Move
Caffeine.appto the Applications folder and open it from there. - "This build isn't signed for Caffeine's background service…" The app's signature doesn't match
teamIdentifierandappIdentifierinServiceProtocol.swift, or the app is signed ad hoc. See Building from source. - "…restart your Mac to finish the update." A service process from the replaced app is still running, and the new app can't verify it. A restart ends it.
- Setup waits for approval. Open System Settings → General → Login Items & Extensions and turn on Caffeine.
- The Mac no longer sleeps. Run
pmset -g | grep SleepDisabled. If it prints1while Keep Awake is off, open Caffeine and follow its message; the service restores the setting from its recovery record. If Caffeine is no longer installed, runsudo pmset -a disablesleep 0.pmset -g assertionsshows other apps that keep the Mac awake. - Reporting a bug. Choose Export Log… in the card and attach the file to an issue.
Caffeine is released under the MIT License. The icons are Tabler Icons, also under the MIT License; see THIRD_PARTY_NOTICES.md.

