Short answer
By default, Ollama unloads a model from memory after 5 minutes of inactivity. To keep a model loaded indefinitely on macOS, you need to set the OLLAMA_KEEP_ALIVE environment variable using launchctl — not a .zshrc export, which Ollama Desktop (a GUI app) never sees. Because launchctl setenv resets on every reboot, the reliable fix is a LaunchAgent that sets the variable and launches Ollama Desktop in a controlled sequence at every login.
Why export OLLAMA_KEEP_ALIVE=-1 in .zshrc doesn’t work
This is the most common mistake when trying to configure Ollama keep-alive on Mac. Here’s why it fails:
- Ollama Desktop is a GUI application. macOS GUI apps do not inherit environment variables from shell config files (
.zshrc,.bash_profile,.zprofile). - Only variables set through
launchctl setenvare visible to GUI apps launched from Finder, Spotlight, or the menu bar. launchctl setenvvalues are session-scoped — they disappear on every reboot, so the fix needs to re-run automatically at every login.- Even when the variable is set correctly, if Ollama Desktop starts before the variable is in place (a startup race condition), it will silently fall back to the default 5-minute timeout.
Step-by-step: Set OLLAMA_KEEP_ALIVE permanently with a LaunchAgent
The most reliable method is a single LaunchAgent that runs the entire startup sequence in the correct order: set the variable → launch Ollama → wait for the server → preload the model.
1. Disable Ollama Desktop’s auto-launch at login
The current version of Ollama Desktop has no “launch at login” toggle in its own settings. Disable it at the macOS system level instead: System Settings → General → Login Items & Extensions, then turn off Ollama in the login items list. This prevents a race condition between Ollama’s own auto-start and the LaunchAgent.
2. Create the LaunchAgent file
Save this as ~/Library/LaunchAgents/com.user.ollama-keepalive.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.ollama-keepalive</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>launchctl setenv OLLAMA_KEEP_ALIVE -1 && open -a Ollama && for i in $(seq 1 30); do curl -s http://localhost:11434/api/tags >/dev/null 2>&1 && break; sleep 1; done && /usr/local/bin/ollama run qwen2.5:14b "" >/dev/null 2>&1</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/ollama-keepalive.out</string>
<key>StandardErrorPath</key>
<string>/tmp/ollama-keepalive.err</string>
</dict>
</plist>
Before using this, customize:
- Replace
qwen2.5:14bwith your model name (list yours withollama list). - Confirm the
ollamabinary path withwhich ollama— a LaunchAgent’sPATHdiffers from an interactive shell’s. - Set
OLLAMA_KEEP_ALIVEto-1(forever) or a specific duration like24h.
Common syntax error:
&&must be written as&&inside a.plistfile. An unescaped&&will break the XML silently.
3. Validate and load the LaunchAgent
plutil -lint ~/Library/LaunchAgents/com.user.ollama-keepalive.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.ollama-keepalive.plist
4. Verify the model stays loaded
ollama ps
You should see UNTIL: Forever (or your chosen duration) next to your model — even after several minutes of inactivity.
Managing the LaunchAgent: command reference
| Task | Command |
|---|---|
| Load the LaunchAgent | launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.ollama-keepalive.plist |
| Unload the LaunchAgent | launchctl bootout gui/$(id -u)/com.user.ollama-keepalive |
| Force it to run immediately | launchctl kickstart -k gui/$(id -u)/com.user.ollama-keepalive |
| Inspect status (run count, exit code, env) | launchctl print gui/$(id -u)/com.user.ollama-keepalive |
| Check the current variable value | launchctl getenv OLLAMA_KEEP_ALIVE |
Any time you edit the .plist, you must bootout then bootstrap again — reloading an already-registered job does not pick up file changes automatically.
How to troubleshoot OLLAMA_KEEP_ALIVE not working
If your model still unloads after 5 minutes despite this setup, check these in order:
Did the LaunchAgent actually run?
launchctl print gui/$(id -u)/com.user.ollama-keepalive | grep -E "runs|last exit"runs ≥ 1andlast exit code = 0confirm it executed successfully.Check the script’s own logs:
cat /tmp/ollama-keepalive.out cat /tmp/ollama-keepalive.errConfirm the Ollama server received the setting:
grep -i keep_alive ~/.ollama/logs/server.log | tail -3It should read
-1s(or your chosen value), not the default5m0s.Run the real-world test: reboot the Mac fully, log in, and check
ollama pswithout doing anything manually. This is the only test that fully rules out login-sequence timing issues.
FAQ
Does OLLAMA_KEEP_ALIVE=-1 keep the model loaded forever?
Yes, -1 tells the Ollama server to never unload the model due to inactivity. It will still unload if you run ollama stop <model> or restart the server.
Why doesn’t launchctl setenv alone survive a reboot?
launchctl setenv writes to the current login session’s environment only. It’s not persisted to disk, so it has to be re-applied on every login — which is exactly what the LaunchAgent’s RunAtLoad key does.
Does an API call’s keep_alive parameter override OLLAMA_KEEP_ALIVE?
Yes. Any keep_alive value sent in an /api/generate or /api/chat request takes priority over the server-wide OLLAMA_KEEP_ALIVE setting for that specific call. If a client tool always sends its own value, your global setting will be ignored for its requests.
Can I preload a model automatically at startup?
Yes — the LaunchAgent above does this by running ollama run <model> "" (an empty prompt) once the server is confirmed to be responding, which forces the model into memory without waiting for a real user request.