After looking into it, this seems like it may be more of an Ollama (or rather Go) quirk
:
The important distinction here seems to be who owns the timeout versus why the TLS handshake took too long.
From the error you posted:
pull model manifest: Get "https://hf.co/v2/.../manifests/...": net/http: TLS handshake timeout
I do not think this points to a Hugging Face server-side TLS timeout being set to 10 seconds.
Go’s standard net/http transport has this default:
TLSHandshakeTimeout: 10 * time.Second
and TLSHandshakeTimeout is specifically the maximum time the client waits for a TLS handshake. The net/http: TLS handshake timeout error is generated on that client side when this timer expires.
Ollama’s current manifest-fetch path appears to use an ordinary Go http.Client without replacing that transport in the normal case, so it inherits this behavior. The relevant path is visible in server/images.go.
So I would separate the two questions like this:
-
Where does the 10-second cutoff appear to come from?
The Ollama/Go client side.
-
Why did the handshake fail to finish within those 10 seconds?
That is still unknown from this log alone. It could be a transient route problem, packet loss, VPN/proxy/TLS inspection, an IPv4/IPv6 path difference, a regional/CDN edge issue, or something specific to the Ollama process environment.
That said, I would not throw away the motivation behind your “120 seconds” suggestion. It maps quite naturally onto a different design question:
Should the initial manifest request be more tolerant of a transient connection/TLS failure, either through a longer bounded timeout or a small retry/backoff policy?
There is already an open Ollama feature request asking almost exactly that for the initial model-pull phase: #5884 — Extend initial timeout or support retry model pull.
So I think there are really two separable issues here:
- diagnosis: why this particular connection spent >10 seconds in TLS;
- resilience policy: whether one such transient failure should abort the whole initial manifest pull.
The second can still be worth improving even if the first eventually turns out to be an ISP/router/proxy/CDN-path problem.
The smallest useful test
Before changing timeout values, I would first measure the connection directly. One fairly high-information test is:
curl -sS -o /dev/null \
--connect-timeout 15 \
--max-time 20 \
-w 'code=%{http_code} remote=%{remote_ip} dns=%{time_namelookup} tcp=%{time_connect} tls_done=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n' \
https://huggingface.co/v2/
A 401, 404, etc. is not a failure for this test. The interesting part is whether DNS/TCP/TLS complete promptly.
time_appconnect is the elapsed time from the beginning of the transfer until the TLS connection is established; for an approximate TLS-handshake-only duration, compare it with time_connect. These timing fields are documented in the curl manual.
A useful first-pass decision tree is:
-
If curl itself takes many seconds in TLS or times out:
look first at the network path: VPN, proxy, TLS inspection, firewall/security software, ISP/routing, IPv4/IPv6, or a localized HF edge/path problem.
-
If curl is consistently fast but Ollama still gets TLS handshake timeout:
look at the Ollama process itself: Ollama version, whether it is running as a system service/container/app, which proxy variables and certificate store it actually sees, and finally Go-specific TLS behavior.
-
If it is intermittent:
that makes a small retry/backoff around the initial manifest request especially interesting as a resilience improvement, regardless of which network component caused the transient stall.
I would start there rather than immediately making the timeout 120 seconds, because it tells you which layer you are actually fixing.
Why this appears to be client-side, and why the manifest/model contents are probably not involved yet
There are two useful implementation details here.
First, Go’s DefaultTransport currently includes:
TLSHandshakeTimeout: 10 * time.Second
See the net/http transport source.
The same source also defines TLSHandshakeTimeout as the maximum amount of time to wait for a TLS handshake.
Second, Ollama’s current request path creates an http.Client with redirect behavior but, in the normal path, does not install a replacement transport:
c := &http.Client{
CheckRedirect: regOpts.CheckRedirect,
}
See Ollama server/images.go.
That means the normal Go default transport is relevant here.
There is another subtle point in the same file: makeRequestWithRetry() has a two-pass structure, but a generic request/network error is returned immediately. The retry is principally used for the HTTP 401 authentication challenge path.
In other words, an initial TLS/network failure is not treated like:
TLS failure
→ wait
→ retry manifest
→ perhaps succeed
but more like:
TLS failure
→ return error
→ pull aborts
That makes an otherwise transient path problem more visible to the user.
Also, the failure stage matters.
The rough sequence is:
DNS
↓
TCP connection
↓
TLS handshake <-- your log appears to fail here
↓
HTTP registry request
↓
manifest response
↓
manifest parsing
↓
blob/GGUF download
↓
digest verification
↓
local model loading/runtime
TLS has to be established before the ordinary HTTPS application request/response exchange. The TLS 1.3 protocol structure is described in RFC 8446.
So for this particular error, I would not initially investigate:
- GGUF corruption;
- quantization choice;
- chat templates;
- GPU support;
- VRAM;
- runtime/model architecture support;
- manifest JSON contents.
Those are downstream layers. They may produce their own errors later, but they do not naturally explain an initial TLS handshake timeout.
A slightly more detailed low-cost diagnostic flow
If the first curl test is abnormal, I would keep the next tests small rather than collecting a huge diagnostic bundle immediately.
1. Compare IPv4 and IPv6
curl -4 -sS -o /dev/null \
--connect-timeout 15 \
--max-time 20 \
-w 'code=%{http_code} remote=%{remote_ip} dns=%{time_namelookup} tcp=%{time_connect} tls_done=%{time_appconnect} total=%{time_total}\n' \
https://huggingface.co/v2/
and, if the machine actually has usable IPv6:
curl -6 -sS -o /dev/null \
--connect-timeout 15 \
--max-time 20 \
-w 'code=%{http_code} remote=%{remote_ip} dns=%{time_namelookup} tcp=%{time_connect} tls_done=%{time_appconnect} total=%{time_total}\n' \
https://huggingface.co/v2/
Interpretation:
IPv4 fast, IPv6 slow/failing
→ investigate the IPv6 path
IPv6 fast, IPv4 slow/failing
→ investigate the IPv4 path
both slow/failing
→ broader route/proxy/firewall/edge problem is more plausible
both fast
→ Ollama-process-specific branch becomes more interesting
An immediate curl -6 “couldn’t connect” is not by itself evidence of an HF IPv6 problem; it can simply mean the local machine/network does not have a usable IPv6 route.
2. Compare the two supported HF hostnames separately
The Hugging Face Ollama documentation explicitly says that both hf.co and huggingface.co can be used.
So these are useful as an A/B test:
curl -sS -o /dev/null \
--connect-timeout 15 \
--max-time 20 \
-w 'code=%{http_code} remote=%{remote_ip} tcp=%{time_connect} tls_done=%{time_appconnect} total=%{time_total}\n' \
https://hf.co/v2/
curl -sS -o /dev/null \
--connect-timeout 15 \
--max-time 20 \
-w 'code=%{http_code} remote=%{remote_ip} tcp=%{time_connect} tls_done=%{time_appconnect} total=%{time_total}\n' \
https://huggingface.co/v2/
I would not use -L for the first comparison, because following redirects combines multiple connections and makes it harder to see which host’s TLS handshake you are timing.
If one hostname behaves differently, that is useful information.
You can also try the fully qualified model name with Ollama:
ollama pull huggingface.co/<owner>/<repository>:<tag>
but I would treat this as an A/B diagnostic, not a guaranteed fix.
There is currently a separate Ollama issue, #15661, concerning an hf.co → huggingface.co authentication-realm host mismatch.
So if changing the hostname causes the error to change from:
TLS handshake timeout
to something like:
realm host ... does not match original host ...
or another 401/authentication error, that is actually useful: it means you got past the original TLS failure and reached a different layer. I would not merge those two errors into one diagnosis.
3. Try another network, if convenient
A phone hotspot is surprisingly high-information here.
same machine + same Ollama + different network succeeds
→ ISP/router/VPN/corporate-path hypothesis gets much stronger
same failure on unrelated networks
→ client/process/version-specific hypothesis gets stronger
This is often more informative than changing many Ollama settings at once.
If curl is fast but Ollama still fails
This is the branch where I would look closely at how Ollama itself is running.
Go’s DefaultTransport uses proxy settings from:
HTTP_PROXY
HTTPS_PROXY
NO_PROXY
(and their lowercase equivalents).
Meanwhile, Ollama may be running in an environment that is not the same environment as the shell where you ran curl.
Examples:
- Linux systemd service;
- macOS application launched outside your shell;
- Docker container;
- Windows app/service environment.
The Ollama FAQ documents this distinction. In particular, for model pulls Ollama recommends HTTPS_PROXY, and if the proxy re-signs TLS traffic, its CA certificate needs to be trusted by the relevant system/container.
For Linux/systemd, Ollama’s own documentation shows configuring service environment variables through:
systemctl edit ollama.service
systemctl daemon-reload
systemctl restart ollama
For Docker, the proxy has to reach the container, for example via the container environment, and a custom TLS-inspection CA may also have to be installed in the container image.
This creates an important diagnostic possibility:
curl in interactive shell: succeeds
Ollama daemon:
has different HTTPS_PROXY / NO_PROXY / CA trust
↓
follows a different effective TLS path
↓
fails
So “curl works” does not necessarily prove that Ollama is using an identical network/TLS environment.
I would avoid solving this by disabling certificate verification. If a corporate proxy performs TLS inspection, installing the appropriate CA in the environment where Ollama actually runs is much safer and much more diagnostic.
Why I think your timeout/retry idea still points at a real design boundary
I think this is the part of your suggestion that is worth preserving.
The registry protocol and the client resilience policy are different concerns.
The registry side exposes operations such as the manifest endpoint:
/v2/<name>/manifests/<reference>
That general protocol shape comes from the OCI Distribution Specification.
But the registry protocol does not inherently require:
client TLS timeout = 10 seconds
or:
retry count = 0
Those are client behavior/resilience choices.
So rather than phrasing the change as:
Hugging Face should raise its TLS timeout to 120 seconds
I would frame the engineering question more like:
Should Ollama make the initial manifest request more resilient to transient connect/TLS failures?
Possible policies include:
A. keep the 10 s handshake timeout, but retry transient network failures
B. use a somewhat longer bounded handshake timeout
C. combine a bounded timeout with a small retry count + backoff/jitter
D. expose an advanced configurable timeout/retry setting
Each has trade-offs.
For example, simply changing:
10 seconds → 120 seconds
can help a genuinely slow handshake, but if packets are black-holed or a middlebox is stuck, it may only turn a 10-second failure into a 120-second failure.
A small retry can sometimes be more useful for transient failures:
attempt 1: unlucky route / transient packet loss
attempt 2: succeeds immediately
but retries should still be bounded, and HTTP/auth errors should not all be retried blindly.
There is already an open Ollama feature request with essentially this concern:
#5884 — Extend initial timeout or support retry model pull
That report is not the same root cause—it shows an initial network i/o timeout rather than your exact TLS error—but the design boundary is very similar: initial model-pull connectivity can fail transiently, and the user asks whether timeout/retry behavior should be configurable or more tolerant.
This also seems distinct from later blob download resilience.
For example, PR #16386 proposes configurable stall handling/resilient chunking for model downloads. That work is in the blob-download phase; it should not be confused with an initial manifest TLS-handshake failure.
So I would keep these as separate failure domains:
manifest/connect/TLS resilience
≠
large-blob download/stall resilience
≠
GGUF/runtime/model-loading errors
That separation should make both bug reports and fixes easier to reason about.
A small external control I tried
As a sanity check, I also tried the HTTP/TLS layer from a separate Google Colab runtime—not as a reproduction of your machine/network, but as an independent public-network control.
I compared:
curl
Go 1.26.0
Go 1.26.7
hf.co
huggingface.co
default Go TLS settings
PQ-hybrid compatibility toggles
On that Colab path:
- direct curl over the usable IPv4 path completed TLS quickly;
- direct
huggingface.co with the Go probes succeeded in all 32/32 runs across the tested Go versions/settings;
- successful Go TLS handshakes were on the order of tens of milliseconds, nowhere near 10 seconds;
- changing the tested post-quantum TLS compatibility settings did not produce a stable success/failure difference.
There was one isolated connection reset on a redirected hf.co path, but it was not a TLS handshake timeout and did not reproduce.
The Colab runtime did not have a usable IPv6 route, so that test says nothing useful about HF’s IPv6 path.
I would interpret this very narrowly:
I could not reproduce a general “HF + Go 1.26 always hits the TLS timeout” problem from that unrelated network.
It does not rule out anything specific to your:
- ISP;
- router;
- VPN;
- proxy;
- TLS-inspection device;
- local firewall/security software;
- IPv4/IPv6 path;
- CDN edge;
- Ollama service environment.
So I would use this mainly to keep a universal HF/Go incompatibility lower on the list, not to conclude that the problem must be local.
A more exotic Go TLS branch — only if curl is consistently fine
There is one more technically plausible branch, but I would put it after the ordinary network/proxy checks rather than starting here.
Recent Go versions enable post-quantum hybrid TLS key exchanges by default.
The Go 1.24 release notes explicitly note that the larger TLS records involved can expose compatibility bugs in some TLS systems and can result in handshake timeouts. Go provides a compatibility control:
GODEBUG=tlsmlkem=0
Go 1.26 added additional hybrid groups; its release notes document another diagnostic compatibility switch:
GODEBUG=tlssecpmlkem=0
This is interesting if the pattern is specifically:
curl / browser: always fine
Ollama / Go client: repeatedly fails
same network
same destination
same time window
because different TLS implementations can produce different ClientHello layouts/key shares and therefore interact differently with a buggy firewall/proxy/middlebox.
But I would not currently make this the primary explanation:
- TLS handshake timeout reports in Ollama predate these Go changes;
- the separate Colab control above did not show a stable PQ-on/PQ-off difference;
- ordinary routing/proxy/environment explanations are simpler and more common.
If this branch ever becomes worth testing, I would use those GODEBUG switches only as temporary A/B diagnostics on the actual Ollama process—not as a permanent “fix”. They change TLS key-exchange behavior; they do not disable certificate verification.
If this needs to become a reproducible Ollama issue
If the problem persists and you want to turn it into a useful upstream report, I think a very small evidence bundle would be enough.
Something like:
Ollama version:
OS:
native / Docker / WSL / other:
exact pull command:
timestamp + timezone:
proxy/VPN: yes/no
Ollama error:
...
timed curl to https://huggingface.co/v2/:
...
curl -4 result:
...
curl -6 result, if IPv6 is actually available:
...
same command on a different network:
success / same failure / not tested
If curl is fast while Ollama fails, also note whether Ollama is running as:
interactive shell process
systemd service
macOS app
Windows app
Docker container
because that determines which proxy/environment/CA configuration is relevant.
No tokens, proxy passwords, private URLs, or other credentials need to be posted.
That should be enough for someone upstream to distinguish:
network-path problem
vs
service environment problem
vs
Go/TLS-specific problem
vs
retry/resilience design issue
without needing a large diagnostic dump.
So my current read would be:
The 10-second timeout itself looks client-side (Go/Ollama), not like an HF server-side TLS setting. But the log does not tell us why the handshake exceeded 10 seconds. Your underlying resilience concern still seems reasonable; I would just move it to the client-side initial-manifest timeout/retry boundary.
And for the immediate failure, the single timed curl above is probably the cheapest next observation with the highest information gain.