Zero to Deployed on a €0 Bill: Teaching Cloud to TU Delft Students

July 2026 · 8 min read

The brief I set myself was simple to state and annoying to satisfy. Take university students who can write Python, and get every one of them to a personal API running in the cloud, deploying automatically on every commit. Without a single credit card entering the story.

That last constraint is the whole design. Students don't have corporate accounts. A course that begins with "first, set up billing" has already lost the room, and one that might surprise a student with a €40 bill is irresponsible. So the course I built for TU Delft students treats free tiers not as a nice-to-have but as a hard requirement that shapes every technical choice.

Nine exercises, one continuous artifact

The course is nine progressive exercises, and the crucial property is that they build one thing. Not nine toy examples. A single FastAPI service that each exercise makes more real.

The arc deserves spelling out, because the ordering is doing pedagogical work. Exercise one is the API itself. Three endpoints (root, health, version), Pydantic response models, type hints everywhere, and configuration through pydantic-settings reading environment variables. That last one is planted early on purpose, so that by the time containers arrive, "config comes from the environment" is already muscle memory rather than a new idea arriving at the same time as Dockerfiles. Exercise two is testing with pytest, async support and coverage included. Three introduces uv for dependency management. Four containerizes the app, and students complete a partial Dockerfile rather than copy a finished one. Five wires GitHub Actions to run quality checks on every push. Then the cloud half: six pushes the image to Artifact Registry, seven deploys it to Cloud Run by hand, eight automates the whole path so a push deploys itself, and nine hardens what now exists. Security, observability, the topics that only make sense once there's something real to protect.

By the end, a student's repository is a small but genuine production setup, and the final state has a property I care about:

flowchart LR
    PUSH["git push"] --> QC["GitHub Actions<br/>ruff lint · ruff format<br/>mypy · pytest"]
    QC -->|all green| BUILD["docker build"]
    BUILD --> AR["Artifact Registry"]
    AR --> CR["Cloud Run<br/>(scale to zero)"]
    CR --> HC["deploy health check<br/>GET /health"]
    QC -.->|red| STOP["pipeline stops:<br/>broken code never ships"]
    WIF["Workload Identity Federation<br/>no service-account keys, anywhere"] -.authenticates.-> AR
  

Every commit they push triggers quality checks, builds a container, ships it, and then curls its own /health endpoint to confirm the thing that deployed actually runs. That last small step separates deployment from hope. The moment a student pushes a change and watches their live URL update on its own is the moment the course actually lands.

You can lecture about CI/CD for an hour, or you can let someone feel it once.

Teach the tools professionals actually use

An early decision: no teaching-only technology. Everything in the stack is what they'd meet at a decent engineering shop in 2026. FastAPI, pytest, uv (watching pip users meet uv's install speed is reliable entertainment), ruff and mypy in CI, Docker, GitHub Actions, Cloud Run. The argument for this is respect. Students can smell a sandbox.

The lint configuration is itself a tiny syllabus. Ruff runs with rule families beyond the default errors. Bugbear for the mistakes that pass tests and fail at 2am, comprehension and upgrade rules, simplification hints. Mypy runs strict. Students meet "the linter is arguing with me" early, in exercise five, on code they wrote themselves. And the resolution process (read the rule, understand why, fix or consciously silence it) is rehearsed as a professional skill rather than an annoyance. The same philosophy runs through the troubleshooting doc, which is a first-class part of the course. Real tools come with real sharp edges, and "read the error, check the docs, fix the config" is the actual curriculum hiding inside every exercise.

And exercise two ships with one test that fails on purpose. Students' first encounter with pytest is red, not green. Because the skill isn't running tests, it's reading a failure and reasoning backward from the assertion to the cause. A wall of green teaches nothing except which button runs the suite. The failing test gets fixed in the first ten minutes and pays for itself all course long. It's a trick I've now smuggled into every course I write.

Free tiers as an architecture constraint

The budget engineering turned out to be some of the most interesting design work. The pillars:

GitHub Codespaces as the standard dev environment. 120 free core-hours per month, which on a 2-core machine is 60 hours, several times the course. It also deletes the entire "works on my machine" support queue. Everyone gets an identical Linux box with the toolchain pre-installed, and laptop diversity stops being my problem. In a room of thirty students, environment triage is the difference between teaching and tech support.

Cloud Run as the deployment target, and not just for the generous free tier. 180,000 vCPU-seconds a month is far more than a student API sipping requests will ever use, sure. But scale-to-zero is also the pedagogically honest choice. An idle student service costs actual zero, and thirty forgotten course deployments in December cost the same. Any always-on option would slowly bill someone for a tutorial they finished months ago. One shared GCP project hosts all the cloud exercises, so the setup ceremony happens once, in exercise six, instead of once per service.

Workload Identity Federation for the CI-to-GCP handshake, which I insisted on despite it being the fiddliest setup step in the whole course. The alternative, exporting a service-account key and pasting it into GitHub secrets, is easier to teach and exactly the habit I refuse to install in someone's first cloud experience. Long-lived credentials in CI is how universities end up mining someone else's cryptocurrency. Students configure OIDC trust between GitHub and GCP once, keys never exist, and their very first pipeline is built the way security teams wish all pipelines were.

What I'd keep

The through-line artifact, without question. One growing project generates a momentum that isolated exercises never find, the same lesson as the narrative structure in my agent course. The free-tier constraint too, and not only for the students' sake. Designing under "this must cost nothing" forced choices (serverless, scale-to-zero, keyless auth, ephemeral dev environments) that are simply good architecture, budget or not. Constraints that sound like austerity keep turning out to be taste.

There's a decent chance some of these students will one day inherit a real production pipeline. My quiet hope is that when they meet a service-account key pasted into a CI secret, something in the back of their mind, installed in exercise eight on a free tier years earlier, says: wait, we don't do it like that.

References