Is It Still Up?๐
Part of a deep dive: Day One Task Scripts
Consult the map
-
Day One Task Scripts โ step 1 of 5
โ (first step) ยท you are here ยท What Just Broke? โ
You're deploying. Traffic is cut over to the new pods, the old ones are terminating, and you need to know the moment your API is healthy again before you proceed. Your options: sit there hitting refresh, write a curl loop in bash, or do it properly.
This is where Python earns its place. Not because curl can't poll โ it can. Because you need to know why the check failed, not just that it did.
The Bash Version and Its Problem๐
| Bash health poller | |
|---|---|
This works for interactive use. In a deployment pipeline, it has problems:
- No timeout โ it'll run forever if the API never comes back
- No distinction between "connection refused" (server not started yet) and "HTTP 503" (server started, app not ready)
- Exit code is from
curl, not from your intent โ harder to integrate with pipeline logic - No useful output about how long it waited
Here's what the Python version does at each step:
flowchart TD
A([Start polling]) --> B[Send HTTP GET]
B --> C{Response?}
C -->|HTTP 200| D([โ Healthy โ continue deploy])
C -->|HTTP 503| E[Print status, wait 5s]
C -->|Connection refused| E
C -->|Request timeout| E
E --> F{Elapsed โฅ timeout?}
F -->|No| B
F -->|Yes| G([โ Did not recover โ exit 1])
style A fill:#1a202c,stroke:#cbd5e0,stroke-width:2px,color:#fff
style B fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style C fill:#4a5568,stroke:#cbd5e0,stroke-width:2px,color:#fff
style D fill:#2f855a,stroke:#cbd5e0,stroke-width:2px,color:#fff
style E fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style F fill:#4a5568,stroke:#cbd5e0,stroke-width:2px,color:#fff
style G fill:#c53030,stroke:#cbd5e0,stroke-width:2px,color:#fff
The Python Version๐
timeout=3is the per-request timeout โ how long to wait for a single HTTP response. Separate from the outertimeout=120loop timeout.- Connection refused means the server isn't listening yet. Different from an HTTP error โ the application hasn't started.
- Server accepted the connection but didn't respond in time โ usually means the app is starting but not ready.
sys.exit(1)signals failure to whatever called this script โ your CI/CD pipeline, a Makefile, a parent script.
Running It๐
| Running the health check | |
|---|---|

The output tells you exactly what the server was doing during the wait. In a CI log, that's useful information. "It spent 10 seconds failing to connect, then 10 seconds returning 503s before coming healthy" is a different story than "it came up immediately."
Checking a JSON Field, Not Just Status Code๐
Your /health endpoint might return 200 with a body that indicates partial readiness:
A status-code-only check would pass this. Look at the body itself:
| Checking the response body | |
|---|---|
This snippet replaces the if resp.status_code == 200: block inside the wait_for_health() loop.
This is where Python genuinely beats a curl loop โ parsing JSON inline without calling jq or juggling subshells.
Making It Reusable Across a Deploy Script๐
A health check that lives in a function can be called from a larger deployment script:
Bash functions exist, but sharing logic across files and integrating cleanly with exit codes gets awkward fast. The moment a health check needs to live inside a deploy pipeline โ not a terminal โ you've outgrown the curl loop.
Practice Exercises๐
Exercise 1: Add exponential backoff
The current poller waits exactly 5 seconds between each attempt. Modify it so the interval doubles after each failed attempt, up to a maximum of 30 seconds. (This reduces load on a recovering service while still catching a fast recovery.)
Exercise 2: Accept the URL as a command-line argument
Hardcoding the URL makes the script less reusable. Modify health_check.py so the URL is passed as the first argument: python health_check.py http://api.internal/health
Quick Recap๐
| Concept | What It Does |
|---|---|
requests.get(url, timeout=3) |
HTTP GET with per-request timeout |
ConnectionError |
Server isn't listening (process not started) |
Timeout |
Server accepted connection but didn't respond |
resp.status_code |
HTTP status (200, 503, etc.) |
resp.json() |
Parse response body as JSON |
sys.exit(1) |
Signal failure to calling process |
What's Next๐
- What Just Broke? โ When the API came back but something still isn't right and you need to read the logs
Further Reading๐
Official Documentation๐
requestslibrary โ The HTTP library used heretimemodule โtime.sleep(),time.time()sys.exit()โ Exit codes and pipeline integration
Exploring Computer Science๐
- What an API Actually Is โ The contract behind every
requests.get()here: it's a library API call that makes a web API call, and why that trips people up
Exploring Kubernetes๐
- kubectl Commands โ When health checking is part of a larger deploy:
kubectl rollout statusand related commands
Exploring Linux๐
- Bash Conditionals โ The
if [[ ]]test patterns that cover simple health checks before Python is needed