
Ollama nạp lại model 214 lần/ngày: thủ phạm là keep_alive
Mặc định 5 phút idle của Ollama làm first token đội từ 0,9s lên 11,4s. Cách đếm số lần load, hai cái bẫy keep_alive, và cấu hình server-side đã sửa được.
Bạn test app chat local, first token về dưới một giây, ship luôn. Đi ăn trưa về hỏi đúng một câu, con trỏ nhấp nháy mười một giây — cùng máy, cùng model, cùng prompt.
Đó không phải model chậm. Đó là Ollama đã unload model khỏi VRAM và đang đọc lại vài GB từ disk. Dưới đây là cách đo, xác nhận, và sửa đúng chỗ.
Mặc định 5 phút mới là thủ phạm
Một dev đã gắn timer vào từng request trong 24 giờ và ghi được 1.180 request, 214 lần load model. Nguyên nhân: Ollama unload model sau 5 phút idle theo mặc định, và request kế tiếp phải trả giá một lần cold load đầy đủ từ disk.
Trên máy của tác giả — một box 16GB VRAM, model nằm trên NVMe, chạy llama3.1:8b (~4,9GB), qwen2.5-coder:14b (~9GB) và nomic-embed-text (~274MB) — cold load tốn 11,4 giây tới first token so với 0,9 giây khi warm. 18,1% request rơi vào trạng thái cold, kéo p50 toàn hệ lên 3,1 giây.
Chi tiết đáng giá với ai chạy cron: job summarize chạy mỗi 10 phút, đối đầu timeout 5 phút, nên cold 100% số lần. Chu kỳ job dài hơn keep_alive thì job đó chưa từng gặp model warm.
Đo trước, đừng sửa mò
Hai lệnh là đủ. Thứ nhất, ollama ps — theo tác giả, cột UNTIL trong output mới là keep_alive thật, không phải giá trị bạn ghi trong config. Thứ hai, đếm số lần load trong log server:
# Linux (systemd)
journalctl -u ollama --since "24 hours ago" | grep -ci "llama runner started"
# macOS
grep -ci "llama runner started" ~/.ollama/logs/server.log
Câu chữ trong log đổi theo version, nên grep tay một lần để chọn dòng xuất hiện đúng một lần mỗi lần load. Nếu histogram time-to-first-token tách thành hai cụm chứ không phải hình chuông, đó là bài toán trạng thái nhị phân, không phải model yếu.
Hai cái bẫy đã ăn mất một buổi tối
Bẫy 1: keep_alive trong request body bị bỏ qua ở endpoint OpenAI-compatible. Tác giả báo cáo rằng truyền keep_alive vào /v1/chat/completions không có tác dụng gì trên version của họ; endpoint native /api/chat thì honor. Đây là chi tiết theo version — cách kiểm chứng là gửi một request rồi đọc cột UNTIL, thay vì tin docs.
Bẫy 2: keep_alive: -1 trên hai model không cùng vừa VRAM. Pin cả model 8B lẫn 14B làm số lần load giảm, nhưng model lớn mất phần cấp phát GPU trọn vẹn và tràn layer xuống CPU: generation rơi từ 42 tok/s xuống 6 tok/s, một câu trả lời 400 token đi từ 10 giây lên hơn một phút. Kết luận của tác giả đáng nhớ: cold start là chi phí cố định trả một lần, còn partial offload là thuế trả trên từng token, mãi mãi, và không hề hiện lên bộ đếm load.
Cấu hình đã hiệu quả
Bốn thay đổi, theo thứ tự tác động: đặt OLLAMA_KEEP_ALIVE=24h ở phía server (systemd override, hoặc launchctl setenv trên macOS — để trong shell profile thì vô ích vì server không chạy trong shell của bạn); giữ đúng một model resident mỗi GPU; tách embeddings sang một instance Ollama CPU-only (CUDA_VISIBLE_DEVICES="" OLLAMA_HOST=127.0.0.1:11435 ollama serve); và xoá cron warmup.
| Chỉ số (24h) | Trước | Sau |
|---|---|---|
| Request ghi nhận | 1.180 | 1.206 |
| Lần load model | 214 | 9 |
| Tỷ lệ request cold | 18,1% | 0,8% |
| p50 time-to-first-token | 3,1s | 1,0s |
| p95 time-to-first-token | 12,6s | 1,9s |
| Cron summarizer, tỷ lệ cold | 100% | 0% |
| Tốc độ generation | 42 tok/s | 42 tok/s |
Dòng cuối là dòng quan trọng nhất: tok/s không đổi. Model chưa bao giờ chậm.
Làm gì ngay bây giờ
Chạy ollama ps và đọc cột UNTIL. Nếu nó báo 5 phút nữa, keep_alive của bạn đang bị bỏ qua bất kể config ghi gì. Đếm số dòng runner-start trong log một ngày, so với số request, rồi mới quyết định. Lưu ý phạm vi: đây là số liệu của một máy, một GPU, một version Ollama — riêng hành vi /v1 phụ thuộc version. Phương pháp chuyển được; con số thì chưa chắc.
Không spam, hủy đăng ký bất kỳ lúc nào.


