caffeinate but it’s written in Rust and has more options. Keeps your Mac wide awake.
Almost ready for 1.0.0.
Download the latest release binary from the releases page.
This won’t be available until version 1.0.0.
CLI only (default):
cargo install caffeinate2
Menu bar + privileged helper:
cargo install caffeinate2 --features full
From a clone:
cargo build --release --features full
The pieces are separate features when you want only one: --features tray
builds the menu bar (caffeinate2-tray) and --features helper-bin builds
the entirely-mode helper daemon (caffeinate2-helper); full is both.
Either cargo install also installs sleepdetect (a clone build produces it
in target/release) — a small diagnostic that logs when the system actually
slept, by watching for jumps in a monotonic clock. It takes no arguments; stop
it with Ctrl+C.
Usage: caffeinate2 [OPTIONS] [COMMAND]...
Arguments:
[COMMAND]... Trailing command to run (takes priority over `--timeout` and `--waitfor`). End
options with `--` when the command could be parsed as flags or arguments, e.g.
`caffeinate2 -t 3600 -- hour`
Options:
--install-helper Install the privileged helper for entirely mode (prompts for
administrator authorization; or run with sudo). Cannot be combined with
other options
--uninstall-helper Remove the privileged helper (requires root). Cannot be combined with
other options
--status Show entirely-mode helper status (holders and sleep state). Cannot be
combined with other options
-v, --verbose Verbose mode
--dry-run Dry run. Don't actually prevent sleep. Useful for testing
--drop-root Drop root privileges in command. You need root to disable sleep
entirely, but some programs don't want to run as root. Also sets HOME,
USER, LOGNAME, and SHELL to the target user's
--shell Run COMMAND through /bin/sh -c instead of executing it directly
-d, --display Disable display sleep
-m, --disk Disable disk idle sleep
-i, --system Disable idle system sleep. [DEFAULT]
-s, --system-on-ac Disable system sleep while not on battery
-e, --entirely Disable system sleep entirely (ignores lid closing)
-u, --user-active Declare the user is active. If the display is off, this option turns it
on and prevents it from going into idle sleep
-t, --timeout <DURATION> Duration to wait before timing out (`-t` / `--timeout`). Takes a single
value: bare numbers are seconds; quote human-readable durations (e.g.
`"1 hour and 30 minutes"`). Use `--` before a trailing command when it
could be confused with flags (e.g. `caffeinate2 -t 3600 -- hour`)
-w, --waitfor <PID> Wait for program with PID X to complete and pass its exit code (another
user's process can be waited on, but its exit code is unreadable;
caffeinate2 then exits 0)
-h, --help Print help
-V, --version Print version
Sleep is disabled until the command completes. Timeout and PID are ignored when a trailing command is present.
By default the command is executed directly, so arguments keep their boundaries (a filename with spaces stays one argument). Use --shell when you want shell features such as pipes, globbing, or &&.
Put -- before the command when it could be parsed as another option or argument:
caffeinate2 sleep 5
caffeinate2 --shell 'sleep 5 && echo done'
caffeinate2 -t 3600 -- ./script.sh --verbose
caffeinate2 -t 3600 -- hour
Sleep is disabled for a certain amount of time, or until the program with the specified PID completes. If both are specified, it waits until one of them completes. The -t countdown follows the wall clock: sleep prevention ends at the promised resume time even if the Mac slept in between (for example the lid was closed, which the default modes don’t prevent) — a Mac still asleep at that time ends the session within a minute of waking. Combined with -w, the timeout instead counts awake time toward the PID wait.
-t / --timeout takes one argument:
-t 3600)-t 10m, -t 1.5h)-t "1 hour and 30 minutes")The value must be a positive duration — -t 0 (or anything that doesn’t parse) is rejected in every mode, including alongside -w or a trailing command.
Supported units include seconds, minutes, hours, days, weeks, months, and years, plus short forms like s, m, h, and d. Anything after the timeout value is treated as a trailing command, not part of the duration — there is no multi-word duration inference, so quote the duration instead:
# Correct: 90 minutes
caffeinate2 -t "1 hour and 30 minutes"
# Wrong: rejected — the trailing words look like a misquoted duration
caffeinate2 -t 1 hour and 30 minutes
For PIDs, caffeinate2 waits until the specified program exits, then exits with the same exit code. If the program doesn’t exist, it exits immediately with an error. Another user’s process can be waited on, but macOS won’t reveal its exit code — caffeinate2 says so and exits 0.
caffeinate2 -t 600
caffeinate2 -t 10m
caffeinate2 -w 1234
caffeinate2 -t 600 -w 1234
Sleep will be disabled indefinitely until you press Ctrl+C.
caffeinate2
Run caffeinate2-tray after installing with --features full. To run it in the background instead of occupying the terminal, use -d / --detach:
caffeinate2-tray -d
This returns control to the shell immediately and keeps the menu bar icon running after the terminal closes. Its stderr is captured, best-effort, to ~/Library/Logs/caffeinate2-tray.log (appended, never rotated), and is discarded only if that file cannot be opened; run it in the foreground to watch the logs live. Stop it with the tray’s Quit item or pkill -x caffeinate2-tray.
.app.tray.toml and reappears when the watcher is re-enabled).When a time limit is set, left-clicking to start sleep prevention turns it off again after that duration (like caffeinate2 -t). Changing the selection while sleep prevention is running re-acquires every selected mode, keeping the old holds until the new ones are in place; if any of them fails, the whole change is rolled back and the previous holds stay. Wait for apps keeps prevention on until every instance of all the selected apps has exited (it stops once at least one selection has been seen running and then none remain); any selected app that isn’t running yet is waited on to launch. With both set, whichever comes first wins. While a time limit is counting down, the minutes remaining (rounded up, e.g. 29m or 1h 29m) are shown next to the menu bar icon; the tooltip shows remaining time and/or app status while active. Time limits follow the wall clock: a 1-hour limit ends an hour after it started even if the Mac slept mid-session (for example the lid was closed, which every mode except Entirely allows) — asleep time counts.
Upgrade other apps’ sleep prevention keeps the Mac awake on behalf of tools that can’t. Tools like Claude Code, Codex, and caffeinate -i use a low-level assertion that still allows sleep when the lid closes — so a long-running agent dies the moment you shut the lid. With this on, caffeinate2 watches for those assertions and temporarily upgrades to Entirely mode while one is active.
runningboardd, audio and media daemons, Apple’s apps); they release them on their own, so caffeinate2 ignores every one of them and never mentions them in the menu.The upgrade dialog. Left-clicking the icon while an upgrade is active asks what to do with it instead of just switching it off (with several apps upgraded at once, a pop-up in the dialog picks which one the ignore choices apply to):
Settings are stored in ~/Library/Application Support/caffeinate2/tray.toml. The selected modes live there as modes = ["user_active", "entirely"]; a file written by an older version, which stored a single mode, is read and migrated on the next save.
Start at login writes ~/Library/LaunchAgents/com.randomblock1.caffeinate2-tray.plist and takes effect at the next login (the tray does not start a second instance immediately). Use -d for a one-off background launch right now; use the LaunchAgent to start the tray at every login. The menu toggle and these CLI flags use the same LaunchAgent:
caffeinate2-tray --install-launch-agent
caffeinate2-tray --uninstall-launch-agent
caffeinate2-tray --launch-agent-status
Unsigned binaries may require running from Terminal once (right-click → Open) or allowing in Privacy & Security.
Codex.app stays matched across restarts).coreaudiod), any Apple .app, anything launched from /System, /usr/libexec, /usr/sbin, /sbin or /Library/Apple — macOS’s daemons and per-user agents — and any process whose executable can’t be read. These are silently dropped: never upgraded, never listed. Everything else you run is upgradeable, including Homebrew binaries and Apple’s own command-line tools: caffeinate -i from /usr/bin is you asking to stay awake, not the OS doing housekeeping.ignored_apps in tray.toml (a list of process names as IOKit reports them, matched case-insensitively) and editable by hand as well as from the menu. Ignoring an app takes effect immediately: if nothing else is worth upgrading, the hold is released right away rather than after the usual 5-second delay.Entirely mode (-e / tray Entirely) disables system sleep even when the lid is closed. It uses a small privileged helper daemon, so once installed it works without repeated password prompts.
One-time setup (either method):
sudo caffeinate2 --install-helperHelper install requires caffeinate2-helper next to caffeinate2: use cargo install caffeinate2 --features full, build with --features full, or extract the GitHub release bundle (all three binaries together).
After that, caffeinate2 -e and tray Entirely use the helper without further passwords.
Who can use it: root and administrator accounts, plus members of the caffeinate2 group (created by --install-helper). The helper denies everyone else, since disabling sleep entirely is otherwise a root-only setting. To allow a standard account:
sudo dseditgroup -o edit -a USERNAME -t user caffeinate2
The grant takes effect on the user’s next attempt (no logout needed). Denied requests show the exact grant command; caffeinate2 --status works for every account.
Other helper commands:
sudo caffeinate2 --uninstall-helper (refuses while entirely-mode sessions are still active)caffeinate2 --statusAfter upgrading caffeinate2, reinstall the helper: the installed daemon is a copied snapshot, so it keeps running the old version until you run sudo caffeinate2 --install-helper again. caffeinate2 --status shows when the installed helper’s version differs from the binary (the tray also logs a warning at startup).
The helper logs to the unified system log (subsystem com.randomblock1.caffeinate2.helper); view it with Console.app or log show --predicate 'subsystem == "com.randomblock1.caffeinate2.helper"'.
This project is licensed under the MIT License.