Ollama context deadline exceeded 오류 해결 방법
Ollama에서 발생하는 context deadline exceeded 오류의 원인을 분석합니다. 클라이언트 타임아웃, 모델 로드 지연, nginx 프록시 설정 등 계층별로 원인을 진단하고 해결하는 구체적인 단계를 안내합니다.
"context deadline exceeded"의 실제 의미
Ollama의 "context deadline exceeded" 오류는 타임아웃 보고입니다. Go 코드의 일부가 요청에 기한을 설정했으나, 모델이 그 시간 내에 작업을 마치지 못해 기한이 만료된 것입니다. 프로그램이 충돌하거나 파일이 손상된 것은 아닙니다. 제한 시간이 지났을 때 작업은 여전히 실행 중이었습니다.
이 문구는 Go의 표준 context 패키지에서 유래했으며, 그 자체로 유용한 단서가 됩니다. httpx 기반으로 작성된 Python 클라이언트는 대신 httpx.ReadTimeout를 발생시킵니다. 브라우저는 일반적인 네트워크 오류를 표시합니다. 이 정확한 문구를 보고 있다면, Ollama 명령줄 도구, Ollama 서버 자체, 또는 API(애플리케이션 프로그래밍 인터페이스)를 호출하는 Go 애플리케이션 중 하나가 대기를 포기한 것입니다.
여기에는 5개의 계층이 관여합니다. 그중 3개는 자체적인 기한을 설정합니다. 바로 클라이언트, 서버의 모델 로드 타임아웃, 그리고 리버스 프록시입니다. 나머지 두 계층인 keep_alive와 num_ctx은 기한을 설정하지는 않지만, 한 번의 작업에 얼마나 많은 부하가 포함될지를 결정합니다. 각 계층은 서로 다른 지점에서 실패하며 해결 방법도 다르므로, 전체 작업의 핵심은 어떤 계층이 오류의 원인인지 파악하는 것입니다.
- 요청에 고정된 시간 예산을 할당한 HTTP 클라이언트.
- 대용량 모델을 디스크에서 처음 읽어올 때 발생하는 Ollama 서버의 모델 로드 타임아웃.
- 요청 사이에 모델을 언로드하여 다음 호출 시 로드 비용을 다시 발생시키는
keep_alive. - CPU 전용 장비에서 프롬프트 처리만으로 수 분이 소요될 만큼 큰
num_ctx. - Ollama가 응답하기 전에 연결을 끊어버리는 nginx나 Traefik 같은 리버스 프록시.
이 목록을 순서대로 확인하십시오. 아래의 각 단계는 문제의 계층을 하나씩 제거하므로 추측을 멈출 수 있습니다.
프록시를 제외하고 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}'60.0초나 30.0초와 같이 의심스러울 정도로 딱 떨어지는 시간에 504 상태가 출력된다면 이는 프록시의 시간 제한 때문입니다. 프록시는 기본값으로 정수 단위를 사용합니다. 모델이 두 번 연속으로 정확히 60.000초 만에 작업을 마칠 수는 없습니다. 직접 호출했을 때 느린 것이 아니라 즉시 거부된다면 이는 제한 시간 문제가 아니라 리스너 문제입니다. 이 경우 Ollama가 11434 포트에서 바인딩하는 주소 항목을 확인하십시오.
요청이 실행되는 동안 서버 로그 모니터링
두 번째 세션을 열어 서비스 로그를 팔로우한 다음, 요청을 다시 보냅니다.
journalctl -u ollama --no-pager --follow --pager-end정상적인 콜드 스타트라면 모델이 로드되고, 러너가 시작된 뒤, 요청이 처리되는 과정이 로그에 남습니다. 로드에 실패하면 대신 다음과 같은 메시지가 나타나며, 이 문자열을 통해 서버 자체의 로드 타임아웃 여부를 식별할 수 있습니다.
Error: timed out waiting for llama runner to start - progress 0.00 -해당 메시지는 서버에 할당된 시간 내에 모델 프로세스가 시작을 완료하지 못했음을 의미합니다. 진행률 수치는 프로세스가 어디까지 진행되었는지를 나타냅니다. 0.00라는 값은 기한 내에 러너로부터 아무런 응답을 받지 못했음을 의미하며, 이는 보통 파일을 읽는 중이거나 시스템에서 스왑이 발생하고 있음을 뜻합니다. 로드 과정에 대한 자세한 정보를 확인하려면 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.serviceOllama 공식 문서에서 Linux 환경을 위해 권장하는 방식에 따라 [Service] 섹션 아래에 변수를 추가하십시오.
[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 파일이 편집기 마커 밖에 저장되었거나 잘못된 섹션 이름 아래에 작성된 것이므로, 설정한 값이 적용되지 않은 상태입니다. 이 설정의 목적을 명확히 이해해야 합니다. 로드 타임아웃을 늘리는 것은 서버가 작업을 포기하지 않게 할 뿐, 처리 속도를 높여주지는 않습니다. 모델이 메모리에 적재되지 않으면 시스템은 스왑을 사용하게 되고 로드 속도는 매우 느려지며, 값을 크게 설정하는 것은 단지 실패 시점을 뒤로 미룰 뿐입니다.
휴식 후 첫 번째 요청이 느린 이유
Ollama는 메모리를 확보하기 위해 유휴 상태인 모델을 메모리에서 해제합니다. keep_alive 설정이 그 시점을 결정합니다. 2026년 10월 확인된 Ollama 문서에 따르면 기본값은 5분입니다. 따라서 한 시간에 한 번 사용하는 채팅 앱은 메시지를 보낼 때마다 모델을 다시 불러오며, 모든 메시지마다 전체 콜드 스타트 비용을 지불하게 됩니다. 타임아웃이 발생하는 요청은 조용한 기간 이후의 첫 번째 요청이며, 이는 사용자들이 무작위라고 묘사하는 패턴과 정확히 일치합니다.
현재 상주 중인 모델을 확인하십시오:
ollama ps
curl -s http://127.0.0.1:11434/api/ps목록이 비어 있거나 만료 시간이 몇 분 남지 않았다면 이를 확인할 수 있습니다. keep_alive은 "10m" 또는 "24h"과 같은 지속 시간 문자열, 단순 초 단위 숫자, 즉시 해제를 위한 0, 그리고 모델을 메모리에 무기한 유지하기 위한 음수를 허용합니다. 요청별로 설정하거나 서비스 전체에 적용되도록 OLLAMA_KEEP_ALIVE를 설정할 수 있습니다. 요청 시 설정한 값이 우선합니다. Ollama 문서에 따르면 keep_alive API 매개변수가 OLLAMA_KEEP_ALIVE 설정을 덮어쓰므로, 자체 keep_alive을 보내는 클라이언트는 자신의 일정에 따라 모델을 해제하게 됩니다.
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "llama3.1:8b",
"keep_alive": -1
}'모델 이름만 있고 프롬프트가 없는 요청은 모델을 로드한 뒤 즉시 반환합니다. 이는 재부팅 후 서버를 예열하는 문서화된 방법이며, 재부팅 후 첫 번째 콜드 스타트를 기다리지 않도록 작은 systemd 유닛에 포함하는 것이 좋습니다. 비용은 명확합니다. 고정된 모델은 메모리를 영구적으로 점유하므로, 작은 서버에서는 4개가 아닌 1개의 모델만 고정할 수 있습니다. 요청 간 모델 상주 유지에서 메모리 계산 방식과 예열 유닛에 대해 자세히 다룹니다.
num_ctx 값이 클 때 첫 토큰 생성 전 타임아웃이 발생하는 이유
모델이 응답을 작성하기 전에는 전체 프롬프트를 읽어야 합니다. 이 단계를 프리필(prefill)이라고 하며, prompt eval가 이를 측정합니다. num_ctx은 컨텍스트 길이를 설정하며, 이는 두 가지 작업을 동시에 수행합니다. 모델이 고려할 수 있는 토큰의 수를 제한하고, 서버가 미리 할당하는 KV 캐시(key value cache)의 크기를 결정합니다. 두 작업 모두 처리량을 증가시킵니다.
CPU 전용 서버에서 프리필은 느리며, 프롬프트 토큰 수에 비례하여 선형적으로 증가합니다. 채팅창에 긴 문서를 붙여넣으면 스트리밍이 시작되지 않았기 때문에 클라이언트는 아무것도 보지 못한 채 프리필 단계에서 수 분을 대기할 수 있습니다. 클라이언트는 제한 시간을 초과하여 context deadline exceeded 오류를 보고하지만, 서버는 그 시간 동안 계속 작업을 수행하고 있는 상태입니다. 이전 섹션의 수치로 이를 증명할 수 있습니다. 긴 프롬프트를 전송한 뒤 Ollama가 캐시되지 않은 프롬프트 토큰을 평가하는 데 소요된 시간으로 정의하는 prompt_eval_count과 prompt_eval_duration를 확인하십시오. 작은 eval_duration 값과 함께 수 분이 소요된다면 이는 프리필 단계입니다. 캐시되지 않은 토큰만 계산에 포함되므로, 모델이 여전히 로드된 상태에서 동일한 프롬프트를 다시 실행하면 훨씬 적은 시간이 보고될 수 있습니다. 따라서 첫 번째 실행 결과를 비교해야 합니다. 더 큰 캐시가 미치는 영향을 확인하려면 "options": {"num_ctx": 2048}와 32768로 프롬프트를 각각 실행하고, 모델 로드 시간이 prompt_eval_duration이 아닌 이곳에 보고되므로 load_duration도 함께 확인하십시오.
서버 기본값은 OLLAMA_CONTEXT_LENGTH에서 결정되며, options 객체의 요청별 num_ctx 설정이 이를 재정의합니다. 단순히 모델의 최대 지원 길이라는 이유만으로 이 값을 최대로 높이는 것은 흔한 실수입니다. KV 캐시 할당으로 인해 모델이 RAM에서 밀려나 정상 작동하던 설정이 스왑(swapping)을 유발하는 상태로 바뀔 수 있기 때문입니다. 실제 메모리에 맞춘 num_ctx 선택하기에서 상세한 크기 산정 방법을 확인할 수 있습니다.
Nginx가 504 Gateway Time-out을 반환하는 이유
Nginx 문서는 proxy_read_timeout을 기본값 60s으로 정의하며, 에러 로그는 해당 실패 원인을 명확하게 기록합니다.
upstream timed out (110: Connection timed out) while reading response header from upstream중요한 세부 사항은 Nginx 문서에 명시되어 있습니다. 이 타임아웃은 "전체 응답 전송 시간이 아니라, 연속된 두 읽기 작업 사이의 시간"으로 설정됩니다. 스트리밍 응답은 데이터 조각(chunk)이 전송될 때마다 타이머를 초기화하므로 스트리밍 채팅은 타임아웃 없이 유지됩니다. 반면 "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를 사용하여 해당 트랜스포트를 이름으로 참조해야 합니다. 그렇지 않으면 아무것도 사용하지 않는 블록을 편집한 셈이 됩니다.
양자화 크기가 작을수록 읽어야 할 데이터가 적어 로딩 속도가 빠릅니다
양자화는 가중치를 저장하는 정밀도를 의미합니다. 정밀도가 낮을수록 파일 크기가 작아지며, 모델 로딩은 대부분 디스크에서 메모리로 파일을 읽어오는 과정입니다.
이 차트의 데이터
[
{
"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을 사용하여 모델을 고정하고 부팅 시점에 미리 로드하십시오. 이렇게 하면 사용자 요청 시 로드 비용이 발생하지 않습니다. 또한 클라이언트가 자체적인keep_alive를 보내지 않는지 확인하십시오. 요청에 포함된 값은 서버 설정을 덮어쓰기 때문입니다.num_ctx을 실제 프롬프트에 필요한 수준으로 낮추십시오. 프리필(prefill) 시간이 단축되고 KV 캐시가 점유하던 메모리가 확보됩니다.- 더 작은 양자화 모델을 사용하십시오. 로드 시 읽는 바이트 수가 줄어들며 모델이 캐시를 위한 공간을 확보할 수 있습니다.
- Nginx의
proxy_read_timeout또는 Traefik의responseHeaderTimeout를 높이고, 버퍼링을 비활성화하여 스트리밍되는 토큰이 클라이언트에 즉시 도달하도록 하십시오. - 클라이언트 프로그램의 타임아웃 설정을 높이십시오. 30초 제한을 가진 Go나 Python 프로그램은 그보다 오래 생각하는 모델을 만나면 실패하게 됩니다.
이 모든 문제 뒤에는 또 다른 원인이 숨어 있습니다. Ollama는 한 번에 제한된 수의 요청만 처리하고 나머지는 대기열에 넣습니다. 따라서 두 번째 호출자는 모델이 느리지 않더라도 자신의 데드라인이 만료될 때까지 대기열에서 기다릴 수 있습니다. 서버 로그에는 요청이 실패한 것이 아니라 늦게 처리된 것으로 나타납니다. 여러 사용자가 하나의 Ollama 서버를 공유할 때 발생하는 현상에서 병렬 처리 설정을 다루며, VPS 기본 설치에서 이러한 설정 변경을 전제로 한 서비스 구성을 설명합니다.
FAQ
Ollama에서 "context deadline exceeded"는 무엇을 의미합니까?
모델이 응답하기 전에 요청에 설정된 기한이 만료되었다는 뜻입니다. 이 문구는 Go의 context 패키지에서 유래했으므로, Ollama 명령줄 도구, Ollama 서버, 또는 API를 호출하는 Go 애플리케이션 중 하나가 이 메시지를 출력한 것입니다. 이는 타임아웃 오류일 뿐이며, 데이터가 손상되거나 시스템이 고장 난 것은 아닙니다. 클라이언트, 모델 로드 과정, 리버스 프록시가 각각 고유한 기한을 설정하며, keep_alive 및 num_ctx이 그 안에서 처리할 작업량을 결정하므로 어느 계층에서 기한을 설정했는지 확인해야 합니다.
클라이언트 타임아웃을 높여야 합니까, 아니면 Ollama의 타임아웃을 높여야 합니까?
먼저 측정하십시오. curl은 전체 시간 제한을 두지 않으므로, 서버 자체에서 curl를 사용하여 http://127.0.0.1:11434으로 직접 요청을 보내십시오. 해당 호출이 JSON 본문을 반환한다면 Ollama는 정상적으로 응답하고 있는 것이며, 기한은 클라이언트나 프록시에서 설정된 것이므로 해당 위치의 값을 높여야 합니다. 만약 그 호출조차 응답이 없다면 지연은 Ollama 내부에서 발생하는 것이며, 응답 내 load_duration 및 prompt_eval_duration 필드를 통해 모델이 로딩 중인지 아니면 프롬프트를 읽는 중인지 확인할 수 있습니다.
왜 첫 번째 요청은 타임아웃이 발생하고 다음 요청은 정상적으로 작동합니까?
Ollama는 keep_alive에 설정된 일정에 따라 유휴 상태인 모델을 메모리에서 해제합니다. 2026년 10월 기준으로 문서화된 기본값은 5분입니다. 유휴 기간 이후의 첫 번째 요청은 디스크에서 모델을 다시 불러오는 콜드 스타트 과정을 거치지만, 바로 이어서 보내는 요청은 메모리에 상주하는 모델을 사용하므로 빠르게 응답합니다. ollama ps를 실행하여 현재 로드된 모델과 만료 시점을 확인하십시오. 모델을 메모리에 유지하려면 OLLAMA_KEEP_ALIVE=-1을 설정하되, 메모리가 계속 점유된다는 점을 감수해야 합니다. 클라이언트가 자체적인 keep_alive을 보내면 해당 설정이 우선하므로 클라이언트 설정도 함께 확인하십시오.
왜 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을 읽어 빌드 기본값을 확인하십시오. 로딩에 수 분이 걸린다면 더 작은 양자화 모델을 사용하는 것을 고려해야 합니다.