Deploying to the cloud
Lesson 3 of 3
3Deploying to the cloud
The big pictureDeploying is not uploading files once. It is a process repeated dozens of times: build an image, run it with production settings, make sure it is healthy, then switch visitors to it, with a way back if something goes wrong.
What the picture shows: A zero-downtime deploy: the new version starts and is checked before receiving any visitor, and the old one stays until it succeeds.
Map of this part
The concepts in order, click any one to jump to it:
Two ways into the same idea
Like opening a new café branch: you prepare the place, test the machines, make sure everything works, then open the door. If a machine breaks in the first hour, you send customers to the old branch while you fix it.
Three tiers: PaaS (Render, Railway, Fly.io, Heroku): push code or an image and the platform handles the rest, the fastest start. Managed containers (Cloud Run, ECS, Azure Container Apps): more control and autoscaling. A virtual server (VPS) with Docker Compose and Caddy or Nginx: cheaper, more control and more responsibility. In all of them: a /health endpoint that checks dependencies, HTTPS with a Let's Encrypt certificate, migrations as a separate backward-compatible step (add a column before you depend on it), rolling or blue/green deploys, and an image tagged with its version (not latest) so rolling back is redeploying the previous tag.
Before You Start
🐳Docker
An image that runs locally.
⚙️Configuration
The previous lesson: environment and secrets.
🌍DNS
The DNS lesson in the internet track.
The Core Idea
1The health check
The platform asks /health every few seconds. If it fails, it sends no visitors, or restarts. A good check confirms the key dependencies, not just that the server answers:
def check_database():
return True
def check_cache():
raise TimeoutError("redis did not answer in 1s")
CHECKS = {"database": check_database, "cache": check_cache}
def health():
results = {}
for name, check in CHECKS.items():
try:
results[name] = "ok" if check() else "fail"
except Exception as e:
results[name] = f"fail: {e}"
healthy = all(v == "ok" for v in results.values())
return (200 if healthy else 503), results
status, body = health()
print(status, body)2Deploying to a managed platform
Most platforms read your Dockerfile and ask for the secrets in their dashboard and a health-check path. A general example with CLI steps:
# build and tag with the version, never "latest" docker build -t registry.example.com/cafe-api:1.4.0 . docker push registry.example.com/cafe-api:1.4.0 # run migrations as a one-off job before switching traffic <platform> run --image cafe-api:1.4.0 -- alembic upgrade head # deploy; the platform waits for /health before sending traffic <platform> deploy --image cafe-api:1.4.0 --health-path /health # something wrong? redeploy the previous tag <platform> deploy --image cafe-api:1.3.2
3A virtual server with Caddy
On a VPS, Caddy in front of the app obtains and renews an HTTPS certificate automatically. A three-line config:
# Caddyfile
cafe.example.com {
reverse_proxy api:8000
}Worked example
Problem: a new release drops the phone column and renames it mobile. During the deploy the old version is still running and asks for phone, so it crashes. Fix in three stages: (1) add mobile and copy the data, with the code writing both. (2) deploy code that reads only mobile. (3) drop phone in a later release. Each stage is compatible with the version before it.
Myth vs Reality
| Myth | Reality |
|---|---|
| The cloud means Kubernetes. | Most projects are served by a managed platform or one server for years. |
| A successful deploy = the page opens. | Check health, logs and key flows; the home page may work while payment is broken. |
Where you meet it at work
🧪Staging
A replica tested before production.
🔁Frequent deploys
Small daily releases are less risky than a huge monthly one.
Test your understanding
Why tag the image with its version, not latest?
latest moves, so you do not know what is running or how to go back.
When do database migrations run?
Both versions run together for a while, so both must understand the schema.
The health check returns 503. What does the platform do?
It keeps the healthy version running until the new one is healthy.
Quick review cards
Check Your Understanding
What is the simplest hosting for your first project?
A managed platform that reads your Dockerfile and gives you HTTPS and a domain.
Why backward-compatible migrations?
Because the old version keeps running during the deploy.
What does /health check?
The key dependencies like the database, with a short timeout.
Resources for this lesson
Grouped by type. Start with the official docs for precision, or an article or video for the explanation.
Official docs1
Done with this lesson?
Sign in to save your progress.