Ollama context deadline exceeded 超时怎么修复
Ollama 报错 `context deadline exceeded` 表示请求在模型响应前超时。本文按客户端、模型加载、keep_alive、提示词负载和 nginx 逐层定位。
“context deadline exceeded”实际表示什么
Ollama 报错 context deadline exceeded 表示请求超时。某段 Go 代码为请求设置了截止时间,但模型未能在该时间内完成,随后截止时间到期。没有发生崩溃,也没有文件损坏。截止时间到达时,任务仍在运行。
这个措辞来自 Go 的标准 context 包,本身就是一个有用的线索。基于 httpx 构建的 Python 客户端会改为抛出 httpx.ReadTimeout。浏览器则会显示普通的网络错误。如果您看到的正是这些文字,说明某个 Go 程序停止等待:可能是 Ollama 命令行工具、Ollama 服务器本身,或调用该 API(应用程序编程接口)的 Go 应用程序。
有五层组件可以设置这个截止时间。它们会在不同阶段失败,也需要不同的修复方法。因此,关键是确定究竟是哪一层触发了超时。
- HTTP 客户端为请求设置了固定的时间预算。
- Ollama 服务器的模型加载超时。首次从磁盘读取大型模型时,可能触发此超时。
keep_alive会在请求之间卸载模型,导致下一次调用再次承担加载成本。num_ctx足够大时,仅处理提示词就可能让纯 CPU 服务器运行数分钟。- nginx 或 Traefik 等反向代理可能在 Ollama 返回响应前切断连接。
请按顺序检查上述列表。下面的每一步都会排除其中一层,避免继续猜测。
直接调用 API,绕过代理复现问题
在服务器本机直接向 Ollama 发送请求,不经过任何代理。
time curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "Why is the sky blue?",
"stream": false
}' | head -c 400curl本身不设置总超时,只设置连接超时。因此,该命令会一直等待,直到 Ollama 返回结果。这样可以将问题分成两部分。如果返回 JSON,说明 Ollama 已响应,超时责任在前置组件。如果该调用本身挂起数分钟,说明延迟发生在 Ollama 内部,代理没有问题。
现在通过公网 URL 发送相同的请求,并记录耗时。
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' \
-X POST https://llm.example.com/api/generate \
-d '{"model": "llama3.1:8b", "prompt": "hi", "stream": false}'如果在一个异常规整的秒数后打印出 504,例如 60.0 或 30.0,这就是代理超时。代理通常使用整秒默认值。模型不会连续两次都恰好在 60.000 秒完成。如果直接调用立即被拒绝,而不是响应缓慢,则说明是监听器配置问题,而不是超时问题。Ollama 在 11434 端口绑定的地址介绍了这种情况。
观察请求运行期间的服务器日志
打开第二个会话并跟踪服务日志,然后再次发送请求。
journalctl -u ollama --no-pager --follow --pager-end正常的冷启动会依次记录加载模型、启动 runner,然后处理请求。加载失败时则会显示如下内容;其中的字符串用于标识服务器自身的加载超时:
Error: timed out waiting for llama runner to start - progress 0.00 -这表示模型进程未能在服务器分配的时间内完成启动。进度数值表示启动进行到的阶段。值为 0.00 表示截止时间前 runner 完全没有报告进度,通常意味着文件仍在读取,或机器正在使用交换空间。要在加载期间获取更多详细信息,请设置 OLLAMA_DEBUG=1 后重启服务,再重复上述操作。
判断延迟来自加载还是生成
Ollama 会报告自身的耗时,因此无需猜测这部分原因。
ollama run --verbose llama3.1:8b "Why is the sky blue?"答案输出后,它会打印 total duration、load duration、prompt eval count、prompt eval rate、eval count 和 eval rate。运行两次。第二次运行时,load duration 应降至接近 0,因为模型已经驻留在内存中。如果没有下降,说明模型在两次运行之间被卸载,这就是下文所述的 keep_alive 情况。
API 返回的最终 JSON 对象中也会包含相同的数值,分别是 load_duration、prompt_eval_duration 和 eval_duration。文档说明所有时长均以纳秒返回,因此除以 10^9 后即可得到秒数。
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "Why is the sky blue?",
"stream": false
}' | python3 -c 'import json,sys; d=json.load(sys.stdin); print({k: round(v/1e9, 2) for k, v in d.items() if k.endswith("_duration")})'查看数值最大的项目。如果 load_duration 占主导,说明模型加载存在问题,请转到接下来的两个章节。如果 prompt_eval_duration 占主导,说明成本来自提示词处理,请转到 num_ctx 章节。如果 eval_duration 占主导,说明模型在当前硬件上的生成速度本来就较慢,任何超时设置都无法改变这一点。使用 num_predict 缩短输出,或改用更小的模型。
检查版本后提高 OLLAMA_LOAD_TIMEOUT
控制服务器等待模型启动时长的变量是 OLLAMA_LOAD_TIMEOUT。该变量的默认值在不同版本之间可能发生变化,因此应针对当前构建版本读取默认值,不要直接采用任何文章中的值,包括本文中的值。先输出版本。
ollama --version然后打开与该版本完全对应的标签 https://github.com/ollama/ollama/blob/<your version>/envconfig/config.go 的源代码,并搜索 OLLAMA_LOAD_TIMEOUT。该文件中的值就是二进制文件编译时内置的默认值。通过 systemd drop-in 设置自定义值。
sudo systemctl edit ollama.service在 [Service] section 下添加变量。这是 Ollama 官方文档针对 Linux 提供的方法:
[Service]
Environment="OLLAMA_LOAD_TIMEOUT=15m"
Environment="OLLAMA_KEEP_ALIVE=-1"sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl show ollama --property=Environment最后一条命令会输出服务实际接收到的环境变量。若结果为空,说明 drop-in 保存到了编辑器标记之外,或保存到了错误的 section 名称下,因此设置的变量没有生效。需要明确这一设置的作用:延长加载超时可以避免服务器过早放弃,但不会让加载速度变快。如果模型无法装入内存,机器会使用 swap,加载过程会非常缓慢;增大超时值只会让失败发生得更晚。
暂停后第一个请求为什么会很慢
Ollama 会卸载处于空闲状态的模型,以释放内存。keep_alive 设置决定模型何时被卸载。Ollama 文档在 2026 年 9 月记录的默认值为 5 分钟。因此,每小时使用一次的聊天应用会在每条消息到来时重新加载模型,每条消息都要经历完整的冷启动。发生超时的请求正是安静一段时间后的第一个请求,这完全符合人们所描述的“随机”现象。
检查当前常驻内存的模型:
ollama ps
curl -s http://127.0.0.1:11434/api/ps如果列表为空,或模型将在几分钟后过期,就可以确认这一点。keep_alive 接受 "10m" 或 "24h" 这样的时长字符串、表示立即卸载的 0,以及表示无限期将模型保留在内存中的负数。可以按请求设置,也可以在服务上设置 OLLAMA_KEEP_ALIVE,使其应用于所有请求。
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"keep_alive": -1
}'指定模型但不提供提示词的请求会加载模型,然后返回结果。这是重启后预热服务器的文档化方式,应将其放入一个简短的 systemd 单元中,避免用户等待冷启动。代价也很明确:固定常驻的模型会永久占用其内存,因此在小型服务器上只能固定一个模型,而不是四个。让模型在请求之间保持常驻详细介绍内存计算和预热单元。
大 num_ctx 为什么会在生成首个 token 前超时
模型开始输出前,必须先读取完整提示词。这一阶段称为 prefill,由 prompt eval 测量。num_ctx 设置上下文长度,同时执行两项操作:限制模型可处理的 token 数量,并确定服务器预先分配的 KV cache(key value cache)大小。这两项都会增加计算量。
仅使用 CPU 的服务器执行 prefill 时速度较慢,而且耗时与提示词 token 数量成正比。将长文档粘贴到聊天中后,prefill 可能持续数分钟,而客户端完全看不到输出,因为流式传输尚未开始。客户端达到截止时间后报告 context deadline exceeded,但服务器在整个过程中都在处理请求。使用上一节的数值即可验证这一点:使用 "options": {"num_ctx": 2048} 运行相同提示词,再使用 32768 运行,并比较 prompt_eval_duration。
服务器默认值来自 OLLAMA_CONTEXT_LENGTH;在 options 对象中为单个请求设置 num_ctx 后,会覆盖该默认值。因为模型存在 advertised maximum,就将其提高到该最大值,这是最常见的错误。KV cache 的分配可能耗尽 RAM,使模型开始使用交换空间,从而让原本正常的配置变得无法正常工作。根据实际内存选择 num_ctx中包含大小规划的详细说明。
nginx 返回 504 Gateway Time-out 的原因
nginx 将 proxy_read_timeout 的默认值设为 60s,其错误日志会明确记录该失败:
upstream timed out (110: Connection timed out) while reading response header from upstreamnginx 文档中的重要说明是:该超时“只发生在两次连续读取操作之间,而不是针对整个响应的传输过程”。流式响应会在每个数据块到达时重置计时器,因此流式聊天不会超时。使用 "stream": false 的请求在答案生成完成前不会发送任何内容,因此整个生成过程必须在这一个超时窗口内完成。这就是同一个模型在聊天窗口中可以正常工作,但从脚本调用时会超时的原因。
location / {
proxy_pass http://127.0.0.1:11434;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_buffering off;
}sudo nginx -t && sudo systemctl reload nginxproxy_buffering off 会影响流式传输。启用缓冲后,nginx 可能会先收集响应,最后一次性转发,因此令牌不会逐个出现,正常工作的流式响应会看起来像卡住了一样。
Traefik 在路由器使用的 ServersTransport 上提供相同的控制项。
http:
serversTransports:
ollama:
forwardingTimeouts:
dialTimeout: "30s"
responseHeaderTimeout: "0s"
idleConnTimeout: "60s"responseHeaderTimeout 控制请求写入后等待响应头的时间,设置为 0 表示不超时。服务必须通过 serversTransport: ollama 按名称引用该传输配置,否则你修改的配置块不会被实际使用。
较小的量化版本加载更快,因为需要读取的数据更少
量化表示权重的存储精度。精度越低,文件越小;而加载模型主要就是将该文件从磁盘读入内存。
The data behind this chart
[
{
"label": "q4_K_M",
"download_size_gb": 4.9
},
{
"label": "q8_0",
"download_size_gb": 8.5
},
{
"label": "fp16",
"download_size_gb": 16
}
]这些是模型页面公布的大小,不是从测试服务器测得的数据。默认的 8B 构建版本大小为 4.9 GB。同一模型的全精度构建版本为 16 GB,需要读取的字节数超过 3 倍,占用的内存也超过 3 倍。对于使用共享存储的租用服务器,这一差异可能决定加载能否完成,还是会因超时而失败。在下载任何大型模型前,应先检查确定哪个模型适合您的 RAM。
租用服务器上需要修改的配置
按照测量结果指出的问题依次修改,每次只改一项,并在每次修改后重新运行计时命令。
- 使用
OLLAMA_KEEP_ALIVE=-1固定模型,或在启动时预热模型,避免用户请求承担加载开销。 - 将
num_ctx降低到提示词实际需要的值。这样可以缩短预填充时间,并释放 KV cache 占用的内存。 - 使用更小的量化版本。这样加载时读取的字节更少,也能为缓存留出空间。
- 提高 nginx 中的
proxy_read_timeout或 Traefik 中的responseHeaderTimeout,并关闭缓冲,让流式 token 直接发送到客户端。 - 提高客户端自身的超时时间。Go 或 Python 程序如果只允许 30 秒,模型思考时间更长时就会失败。
所有这些现象背后还可能有一个共同原因。Ollama 同时处理的请求数量有限,其余请求会进入队列。因此,第二个调用方可能在队列中等待,直到自身的截止时间到期,即使模型本身并不慢。服务器日志会显示请求延迟处理,而不是请求失败。多个用户共用一个 Ollama 服务器时会发生什么介绍并行处理设置,VPS 上的基础安装介绍这些覆盖配置所依赖的服务设置。
FAQ
Ollama 中的“context deadline exceeded”是什么意思?
这表示请求的截止时间在模型返回答案前已到期。该短语来自 Go 的 context 包,因此是某个 Go 程序输出的:Ollama 命令行工具、Ollama 服务器,或调用 API 的 Go 应用。这是超时,不表示系统损坏或数据损坏。下一步是确定由哪一层设置了截止时间,因为客户端、模型加载过程、keep_alive、num_ctx 和反向代理都会设置各自的截止时间。
应该延长客户端超时时间,还是 Ollama 的超时时间?
先进行测量。在服务器本机上使用 curl 发送请求,直接访问 http://127.0.0.1:11434,因为 curl 不设置整体时间限制。如果该调用返回 JSON 正文,说明 Ollama 正在响应,截止时间由客户端或代理设置,因此应在对应位置延长超时时间。如果该调用也一直等待,说明延迟发生在 Ollama 内部;响应中的 load_duration 和 prompt_eval_duration 字段会告诉您模型是在加载,还是在读取提示词。
为什么第一个请求超时,后续请求却能正常工作?
Ollama 会根据 keep_alive 设置的计划卸载空闲模型,以释放内存。经检查,2026 年 9 月记录的默认值为 5 分钟。空闲一段时间后的第一个请求会从磁盘重新加载模型,需要完整经历冷启动;紧接着发送的请求则会发现模型仍驻留在内存中,因此快速返回。运行 ollama ps 查看当前已加载的模型及其到期时间。设置 OLLAMA_KEEP_ALIVE=-1 可让模型保留在内存中,但内存也会持续被占用。
为什么只有通过 nginx 访问时才失败?
nginx 文档将 proxy_read_timeout 的默认值设为 60s,该超时时间适用于两次连续读取之间的间隔,而不是整个响应。流式响应每发送一个数据块都会重置该计时器;使用 "stream": false 发送的请求则必须在单个时间窗口内完成。因此聊天窗口可以正常工作,而脚本会失败。在 nginx 错误日志中查找 upstream timed out (110: Connection timed out) while reading response header from upstream,然后延长 proxy_read_timeout 并设置 proxy_buffering off。
延长 OLLAMA_LOAD_TIMEOUT 会让加载更快吗?
不会。它只会改变服务器等待后放弃并记录 timed out waiting for llama runner to start 的时长。如果模型无法装入内存,机器会使用交换空间,加载速度会大幅下降;延长超时只会让失败更晚发生,不能解决问题。运行 ollama --version 并读取该版本中的 envconfig/config.go,以确认您当前构建的默认值。如果加载需要数分钟,应考虑改用更小的量化模型。