TLS and network control
Set timeouts and safe retries
Bound connection and total time, then retry only operations whose repetition is acceptable.
10 minute lesson
By default, curl waits a long time for a server that never answers. In a terminal that costs you patience. In automation it costs you a hung script, a stuck cron job, or a CI pipeline that blocks for minutes on a dead host. Network operations can hang or fail temporarily, so automation needs time bounds, clear exit codes, and a retry policy connected to what the operation actually does.
Bound the time
Add a connection timeout, total timeout, and limited retry:
curl --connect-timeout 3 --max-time 10 --retry 2 https://example.org/
The two timeouts answer different questions. --connect-timeout 3 bounds how long curl waits to establish the connection: DNS plus TCP plus the handshake. --max-time 10 bounds the entire transfer, including the download. A server that accepts connections instantly but dribbles out one byte per second sails past the first limit and hits the second.
Test once against an unreachable lab address:
time curl --connect-timeout 3 --max-time 10 https://10.255.255.1/
# curl: (28) Failed to connect to 10.255.255.1 port 443 after 3002 ms: Timeout was reached
echo $? # 28
Record the elapsed time and final exit code instead of waiting indefinitely. Exit code 28 is curl’s timeout signal, and the roughly three-second duration proves the connection timeout did the work. Without these options the same command can hang for minutes.
Retry what deserves retrying
--retry 2 makes curl attempt the transfer up to two extra times, but only for failures curl considers transient: timeouts, plus HTTP responses like 408, 429, 500, 502, 503, and 504. A 404 does not get retried, and that is correct — the resource will still be missing on attempt three. curl also waits between attempts, backing off progressively, and you can cap the whole budget with --retry-max-time 30.
The safety rule
Retries can repeat side effects. Retrying a GET is harmless: reading twice changes nothing. Retrying a POST that creates an order can create two orders, because “the response timed out” does not mean “the server did nothing” — the request may have succeeded just as the connection dropped.
Use retries freely only for operations designed to be idempotent or protected by an idempotency key, where the server recognizes a repeated request and refuses to apply it twice. For everything else, a failure should surface to something that can decide, not silently fire the same mutation again.
Lesson completed