SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

Keep an Ollama model loaded in memory

Ollama unloads a model after 5 minutes idle, so the next request waits for a reload. Set OLLAMA_KEEP_ALIVE in a systemd override, then preload after reboot.

Why does Ollama unload the model after a few minutes?

Ollama keeps a model loaded in memory for five minutes after the last request, then frees it. The next request has to load the weights into RAM or VRAM again, so it stalls before the first token arrives. That is why a chat UI or a coding agent feels fast, goes quiet for a while, then feels slow again on the next message. Nothing is broken. The idle timer expired.

The timer is called keep_alive. It is per model, and it restarts every time a request finishes. A model that is currently answering a request is never unloaded, because the server only expires a model with no active request. As of October 2026 the default is five minutes, and it applies to every model this server loads.

There are two places to set the idle timeout: the keep_alive field on the individual request, or OLLAMA_KEEP_ALIVE as the server default. A systemd drop-in is what makes the server default survive a restart. This guide assumes Ollama is already running as a service. If it is not, start with installing Ollama on a VPS and come back.

Which models are resident right now, and when do they expire?

ollama ps
NAME        ID              SIZE      PROCESSOR    CONTEXT    UNTIL
qwen3:8b    500a1f067a9f    6.6 GB    100% GPU     4096       4 minutes from now

Empty output means nothing is loaded, so the next request has to load the model first. PROCESSOR tells you where the weights went. 100% GPU and 100% CPU are the clear cases. A split such as 25%/75% CPU/GPU means only part of the model is in VRAM, so the rest runs on the processor and generation is slower. Usually that is because the model did not fit, but a num_gpu setting that limits how many layers go to the GPU produces the same kind of split.

UNTIL is the countdown, and it prints a relative time such as 4 minutes from now. It prints Forever when the model was loaded with a negative keep_alive. It prints Stopping... after expiration, including while active requests delay unloading.

The column set has changed between releases, so read the header instead of counting fields in a script. Ollama v0.40.5, checked on 2026-10-30, also prints a RUNNER column between CONTEXT and UNTIL, naming the runner that serves the model, so your header may be one column wider than the example above. For anything automated, ask the API:

curl -s http://localhost:11434/api/ps

Each entry carries expires_at, an absolute timestamp such as 2026-08-09T14:38:31.83753Z, and size_vram, the part of that model sitting in GPU memory. A size_vram of 0 means the model is running on the CPU.

What the reload actually costs

Do not guess at it. Ollama reports the load time in every response, as load_duration, in nanoseconds.

sudo apt install -y jq
ollama stop qwen3:8b
curl -s http://localhost:11434/api/generate -d '{"model": "qwen3:8b", "prompt": "hi", "stream": false}' | jq '{load_duration, total_duration}'
curl -s http://localhost:11434/api/generate -d '{"model": "qwen3:8b", "prompt": "hi", "stream": false}' | jq '{load_duration, total_duration}'

Wait until ollama ps no longer lists the model after ollama stop before running the two API calls. The first call loads the model, so its load_duration is large. Divide by 1000000000 to read it as seconds. The second call runs while the model is resident and reports a much smaller number. The gap between those two figures is what every user pays once the timer has expired, and it is the whole reason to change keep_alive. After an unload the kernel's page cache often still holds the model file, so on a box with spare RAM a reload can be quicker than the first load after a boot. When that cache has been reclaimed, the weights come from disk, so if you have moved the model directory onto a second volume, the speed of that volume sets the floor on every cold load. For the generation speed either side of that pause, see how to measure tokens per second on your own box.

Keep an Ollama model loaded in memory on one request

Send keep_alive with the request. It applies to that model from the moment the request finishes.

curl -s http://localhost:11434/api/chat -d '{
  "model": "qwen3:8b",
  "messages": [{"role": "user", "content": "hello"}],
  "keep_alive": "30m"
}'

Four value forms are accepted:

  • a duration string: "30m", "24h", "90s"
  • a plain number, read as seconds: 3600
  • a negative value, -1 or "-1m", meaning no idle timeout at all
  • 0, meaning unload once the model has no active requests

A value on the request overrides the server default, in both directions. That matters more than it sounds: a client sending its own keep_alive wins over anything you configured on the server.

You can also load a model without generating anything. Send only the model name. The server loads it and returns an empty response with "done": true.

curl -s http://localhost:11434/api/generate -d '{"model": "qwen3:8b", "keep_alive": "30m"}'

That is the command to run after a reboot, or after pulling a new model, so the first real user request does not pay for the load. The CLI does the same job with a flag:

ollama run --keepalive 30m qwen3:8b "hello"

Keep it loaded by default with OLLAMA_KEEP_ALIVE

The server reads OLLAMA_KEEP_ALIVE at startup and uses it for every model that does not carry its own value. It takes the same forms as the request field, so 30m, 3600 and -1 all work.

The catch is whose environment it has to be in. Running export OLLAMA_KEEP_ALIVE=30m in your SSH session does nothing, because the packaged install runs the server as a systemd service under its own user with its own environment. Your login shell and that service never meet. This is the most common reason the setting looks ignored.

Make it survive a restart with a systemd drop-in

sudo systemctl edit ollama.service

The editor opens with two comment markers. Type between them: systemd discards anything you write below the second marker.

[Service]
Environment="OLLAMA_KEEP_ALIVE=30m"

Saving writes /etc/systemd/system/ollama.service.d/override.conf. That is a drop-in rather than an edit of the shipped unit, so an Ollama package upgrade that replaces ollama.service leaves your setting alone. If drop-ins and unit files are new to you, the systemd service and timer guide covers the mechanics.

sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl show ollama --property=Environment

The last command prints the environment the service will really run with. If OLLAMA_KEEP_ALIVE=30m is missing from that line, the drop-in did not take, and the cause is almost always a missing [Service] header or lines typed below the marker. The restart itself drops every loaded model, so the next request is a cold load. Warm it up with the preload call above. The drop-in keeps the setting across a reboot, not the model: the service starts ollama serve with nothing loaded, so run the same preload call after every boot.

What keeping a model resident costs you

The SIZE column in ollama ps is memory held for the whole idle window, not only during a request. An 8B model at 4-bit quantisation sits around 5 to 6 GB. A 27B model is a different conversation, and the memory arithmetic for running one on a CPU-only VPS is worth working through before you decide to hold one resident. Set keep_alive to -1 and you disable idle expiration; later requests for the same model can replace its timeout or force a reload when runner options change. On a small VPS that is a direct trade against your database, your web app and your build jobs.

Watch the real numbers rather than trusting an estimate. Run this while a model is loaded, then again after ollama stop:

free -h

The available column is the memory the kernel could still hand to a new process. On an NVIDIA GPU box, nvidia-smi shows the same story in VRAM. If the box runs out, the kernel kills a process to recover:

sudo dmesg -T | grep -i "out of memory"

A line naming ollama means the model server was the victim. A line naming your database means the model won and something you cared about lost. Both outcomes come from the same decision: a long keep-alive window on a box with no headroom.

Two costs here are easy to miss. A longer context length reserves a larger KV cache (key value cache, the per token attention state the model keeps while generating), and that cache is part of the resident size. How big it gets follows from num_ctx, so raising the context window raises the memory a resident model holds for the whole idle period, not only while it is answering. OLLAMA_NUM_PARALLEL above 1 reserves that cache once per parallel slot when the model supports that parallelism. Ollama forces parallelism to 1 for embedding models and several architectures, including mllama and qwen3next. If you plan to serve several people from one model, size the memory for the slots, not for the weights alone.

A reasonable default: one model on a box with headroom can use -1. A shared box should use a window that covers the gaps between your requests, such as 30m, so the memory comes back when you stop working.

Unload a model immediately

ollama stop qwen3:8b

It returns with no output after requesting expiration; unloading waits for active requests to finish, so the command can return before memory is freed. It also returns with no output for a model you have pulled but that is not loaded right now. The command never checks what is resident: it sends the unload request, and the server answers with success whether or not anything was in memory. The error couldn't find model "qwen3:8b" to stop only appears when no model by that name is installed on this server, which usually means a typo in the name or tag. So a silent ollama stop is not proof that anything was freed. ollama ps is. The API form is a request with no prompt and keep_alive set to 0:

curl -s http://localhost:11434/api/chat -d '{"model": "qwen3:8b", "messages": [], "keep_alive": 0}'

The reply carries "done_reason": "unload". Use this instead of restarting the service. systemctl restart ollama frees the memory too, but it drops every other loaded model and kills any request that was running.

Running more than one model on one server

OLLAMA_MAX_LOADED_MODELS caps how many models stay loaded at once, and as of October 2026 the default is three per GPU, or three on a CPU-only box. The cap counts models, while memory is the real limit, so a second large model can be refused room long before you reach three.

When a new model is requested and there is not enough memory for it, or the cap has been reached, the scheduler unloads one of the resident models to make space. It prefers one with no active request, and it will evict a model whose timer has not expired, including a model loaded with -1, though it tries models with shorter windows first. So a negative keep_alive means no idle timeout. It does not pin the weights against another model's request.

Those decisions show up in the service log, and the cap case is only logged at debug level. Add a second Environment="OLLAMA_DEBUG=1" line to the same drop-in, restart, and watch:

sudo journalctl -u ollama -f

Read the reason given next to the request that triggered the unload. max runners achieved, unloading one to make room means the OLLAMA_MAX_LOADED_MODELS cap was reached, which can happen even when both models would fit, so raise the cap if free -h shows the headroom. A line saying the new model is predicted to exceed available memory, followed by an eviction, means the scheduler's fit check rejected loading them together with its memory headroom. The fix there is fewer models on this box, or a long window for the one that must answer fast and 0 for the one you call rarely.

Guidance that outlives the next release

Ollama ships often and its defaults move, so check the build in front of you rather than memorising numbers:

ollama --version
ollama serve --help

ollama serve --help lists the environment variables that build actually reads, OLLAMA_KEEP_ALIVE among them. Two rules have held across releases and are safe to build on. A value on the request beats the server default. And ollama ps is the truth about what is loaded, whatever a config file says it should be.

If an editor or an agent drives your server, check what that client sends before you blame the server. Pointing a coding agent at your own Ollama server covers where those request settings live.

FAQ

Why does Ollama unload my model after 5 minutes?

Five minutes is the default keep_alive, the idle timer Ollama starts when a request finishes. When it expires the server frees the weights, so the next request has to load them again, and that reload is the pause you feel. Raise it for one request by sending "keep_alive": "30m" in the JSON body, or for the whole server with the OLLAMA_KEEP_ALIVE environment variable.

How do I keep an Ollama model loaded in memory permanently?

Use a negative value: "keep_alive": -1 on the request, or OLLAMA_KEEP_ALIVE=-1 for the server. ollama ps then shows Forever in the UNTIL column. This removes the idle timer and nothing else. If another model is requested and memory is short, the scheduler still unloads this one to make room. Later requests for the same model can also replace its timeout or trigger a reload when runner options change. A restart or reboot also clears it, so preload the model again afterwards.

Why is OLLAMA_KEEP_ALIVE being ignored?

Check where you set it. Run systemctl show ollama --property=Environment, and if the variable is not in that output the server never saw it, because a variable exported in your shell does not reach a systemd service. Set it with sudo systemctl edit ollama.service, then run sudo systemctl daemon-reload and sudo systemctl restart ollama. The other cause is a client that sends its own keep_alive on the request, which overrides the server default.

How do I free the memory without restarting Ollama?

ollama stop qwen3:8b requests expiration of that one model and leaves the server and every other loaded model running. Active requests must finish before it unloads. Over the API, send a request with no prompt and "keep_alive": 0, and the reply comes back with "done_reason": "unload". Confirm with ollama ps, which should no longer list the model once unloading finishes.

Was this article useful?