Serverless Functions#

Deploy code without owning a server, pay only while it runs, and scale to zero when nobody’s looking โ€” provided you design for a process that can vanish at any moment.

โฑ ~9 min read ยท ~20 min hands-on ๐Ÿ”— needs: Deployment Platforms ยท Advanced Docker

Serverless is the natural home for the workloads this course produces: a scheduled scraper, a webhook receiver, an inference endpoint used a few hundred times a day. You ship a function or container; the platform handles machines, scaling, and idle cost.

Try it in 5 minutes โ€” deploy a container that scales to zero#

Google Cloud Run takes any container that listens on $PORT:

# /// script
# requires-python = ">=3.12"
# dependencies = ["fastapi>=0.110", "uvicorn>=0.27"]
# ///
"""A serverless-ready API: binds $PORT, stateless, has a health check.

Run locally: uv run app.py
"""

import os

import uvicorn
from fastapi import FastAPI

app = FastAPI()


@app.get("/health")
def health():
    return {"ok": True}


@app.get("/")
def root():
    return {"message": "Hello from serverless"}


if __name__ == "__main__":
    # The platform chooses the port and injects it. Never hard-code 8000.
    uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
gcloud run deploy my-api --source . --region asia-south1 --allow-unauthenticated

โœ… A public HTTPS URL, autoscaling, and zero cost while idle. Two details make it work: bind 0.0.0.0 (not 127.0.0.1) and read $PORT from the environment.

The rules serverless imposes#

RuleWhyConsequence
StatelessAny request may hit a fresh instanceNever store session state in memory or on local disk
Ephemeral diskThe filesystem vanishesWrite to object storage or a database
Bounded runtimeRequests time outLong jobs โ†’ queue + worker, or a VM
Cold startsScale-to-zero means a first-request delaySlim images; min-instances if latency matters
ConcurrencyOne instance may serve many requestsCode must be thread/async-safe
flowchart LR
    R["Request"] --> S{"Warm instance<br/>available?"}
    S -->|Yes| H["Handle โ€” fast"]
    S -->|"No (scaled to zero)"| C["Cold start:<br/>pull image, boot"]
    C --> H
    H --> I{"Idle a while?"}
    I -->|Yes| Z["Scale to zero โ€” โ‚น0"]

Picking a platform#

PlatformBest for
Cloud RunAny container, generous limits, scale-to-zero โ€” the flexible default
AWS LambdaDeep AWS integration, event sources; size and runtime limits
Cloudflare WorkersEdge latency, tiny/fast JS-first workloads; not general Python
Vercel / NetlifyFrontends with API routes
HF Spaces / ModalML inference, GPUs on demand

Cloud Run is the best fit for this course: it takes the Docker image from Advanced Docker unchanged.

Cold starts and cost#

Cold start โ‰ˆ image pull + process boot + your imports. Shrink all three: slim multi-stage images, lazy-import heavy libraries, and avoid loading a model at module scope unless you also set a minimum instance count.

The flip side of scale-to-zero is scale-to-many: a traffic spike (or a bug, or a scraper hitting you) can launch hundreds of instances and a real bill. Always set a max-instance cap and an alert โ†’ Cost Alerting.

gcloud run deploy my-api --max-instances 10 --min-instances 0 --memory 512Mi

When it fails#

SymptomCauseFix
Container fails to startBound 127.0.0.1 or a fixed portBind 0.0.0.0, read $PORT
Data disappears between requestsLocal disk is ephemeralObject storage or a database
First request slow, rest fastCold startSlimmer image, lazy imports, min-instances
Long job times outExceeds the request limitQueue + worker โ†’ Pub/Sub
Surprise billUnbounded autoscaling--max-instances + budget alerts
Works locally, 403 deployedMissing IAM/auth flagCheck invoker permissions

Your turn (โ‰ˆ20 min)#

  1. Deploy the app above to Cloud Run (or Lambda); hit /health over HTTPS.
  2. Call it after several idle minutes and time the cold start, then again immediately โ€” compare.
  3. Set --max-instances 3 and explain in one line what that protects you from.
  4. Try writing a file in one request and reading it in the next; observe the failure and fix it with object storage.
  5. Point your scheduled scraper at it via a cloud scheduler instead of GitHub Actions.

Checklist#

  • My service binds 0.0.0.0 on $PORT.
  • It’s stateless and writes nothing important to local disk.
  • I know what a cold start is and how to reduce it.
  • I always cap max instances and set a budget alert.
  • I move long-running work to a queue + worker.

Go deeper#