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 setenv are visible to GUI apps launched from Finder, Spotlight, or the menu bar.
  • launchctl setenv values 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 &amp;&amp; open -a Ollama &amp;&amp; for i in $(seq 1 30); do curl -s http://localhost:11434/api/tags &gt;/dev/null 2&gt;&amp;1 &amp;&amp; break; sleep 1; done &amp;&amp; /usr/local/bin/ollama run qwen2.5:14b "" &gt;/dev/null 2&gt;&amp;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:14b with your model name (list yours with ollama list).
  • Confirm the ollama binary path with which ollama — a LaunchAgent’s PATH differs from an interactive shell’s.
  • Set OLLAMA_KEEP_ALIVE to -1 (forever) or a specific duration like 24h.

Common syntax error: && must be written as &amp;&amp; inside a .plist file. 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

TaskCommand
Load the LaunchAgentlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.ollama-keepalive.plist
Unload the LaunchAgentlaunchctl bootout gui/$(id -u)/com.user.ollama-keepalive
Force it to run immediatelylaunchctl 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 valuelaunchctl 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:

  1. Did the LaunchAgent actually run?

    launchctl print gui/$(id -u)/com.user.ollama-keepalive | grep -E "runs|last exit"
    

    runs ≥ 1 and last exit code = 0 confirm it executed successfully.

  2. Check the script’s own logs:

    cat /tmp/ollama-keepalive.out
    cat /tmp/ollama-keepalive.err
    
  3. Confirm the Ollama server received the setting:

    grep -i keep_alive ~/.ollama/logs/server.log | tail -3
    

    It should read -1s (or your chosen value), not the default 5m0s.

  4. Run the real-world test: reboot the Mac fully, log in, and check ollama ps without 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.