Project — Cloud Deployment
Deploy your Adult / Census Income scorer publicly, built to survive contact with the
real world. A stranger on another machine reaches your /predict over https:// on a
real domain and gets a prediction back, backed by dev-to-prod digest promotion, an
auto-renewing TLS cert, one-step rollback, and a stated cost ceiling. This is real cloud
work on your own account (docker push, a host, DNS, a TLS cert, a rollback), not a
browser exercise.
| Dataset | Adult / Census Income — the same dataset and repo you carry through every module’s project (the lessons harden a Lending Club scorer; here you take your service to the open internet) |
| Start from | Fork dutchengineer-org/phase2-starter — ships train.py and the frozen package (run python train.py to produce the model), so the model is never the blocker. Already forked it in an earlier ship module? Keep building in that fork |
| Aim for | The package matches the ml-pipeline-starter reference — you push the image your M5/M6 work built, and you do not change the package. No version bump: shipping the same code to a new place is a deployment event, not a code change — the library is byte-identical, so there is nothing to version |
| Done when | It passes every line of the rubric below, then you push to GitHub |
Deploy your Adult / Census Income scorer publicly, built to survive contact with the
real world. A stranger on another machine reaches your /predict over https:// on a
real domain and gets a prediction back, backed by dev-to-prod digest promotion, an
auto-renewing TLS cert, one-step rollback, and a stated cost ceiling. This is real cloud
work on your own account (docker push, a host, DNS, a TLS cert, a rollback), not a
browser exercise.
| Dataset | Adult / Census Income — the same dataset and repo you carry through every module’s project (the lessons harden a Lending Club scorer; here you take your service to the open internet) |
| Start from | Fork dutchengineer-org/phase2-starter — ships train.py and the frozen package (run python train.py to produce the model), so the model is never the blocker. Already forked it in an earlier ship module? Keep building in that fork |
| Aim for | The package matches the ml-pipeline-starter reference — you push the image your M5/M6 work built, and you do not change the package. No version bump: shipping the same code to a new place is a deployment event, not a code change — the library is byte-identical, so there is nothing to version |
| Done when | It passes every line of the rubric below, then you push to GitHub |
Start here
- Continue in your Phase 2 fork →
dutchengineer-org/phase2-starter(or keep building in the one you already forked); bring a cloud account and a domain you control - Work the tasks below in order — each maps to a lesson you just finished, and each runs against your own cloud account and the container your Phase 2 fork builds
- Check yourself against the rubric, then push and submit
The proof is not a screenshot. It is a live URL a grader can curl, a deploy config in
your repo, and a rollback you can demonstrate in one command.
The tasks
Do these in order; each maps to a lesson you just finished. Each runs against your own cloud account and the container your Phase 2 fork builds, not in a browser.
1. Get the service live on the internet (from Lesson 1)
- Tag and
docker pushthe Phase 2 starter’s/predictimage to a registry, then deploy it to a container host (Cloud Run / Fly / Render-style). curl https://<your-service-url>/healthfrom a second device returns{"status":"ok"}, andcurl -X POST https://<your-service-url>/predictwith one Adult/Census record returns a prediction, proving a stranger off your machine can reach and use it.
2. Promote, do not just push (from Lesson 2)
- Stand up a dev deployment with its own URL alongside prod, and deploy changes to dev first.
- Promote the exact image digest that passed dev to prod: no per-environment rebuild.
Confirm
devandprodreport the same digest, not just the samelatesttag.
3. Put it on a real domain over HTTPS (from Lesson 3)
- Point a subdomain you control (e.g.
predict.yourdomain.com) at the live service via a DNS record, and enable a managed TLS certificate. curl -v https://predict.yourdomain.com/healthshows a valid cert chain. Read the cert’s expiry from the output and confirm auto-renewal is configured so it does not lapse weeks later.
4. Build a one-step rollback (from Lesson 4)
- Configure your release so a bad version is survivable: deploy a deliberately broken build to prod, then roll back to the previous known-good digest in a single step.
- Confirm
/predictis healthy again and note how long the rollback took. The previous artifact must already exist (Task 2’s promoted digests): rollback is repointing, not rebuilding.
5. Bound the cost (from Lesson 5)
- Set a max-instances cap and a budget alert on the deployment, and list every attached resource (load balancer, static IP, egress) that bills independently of the main instance.
- State the expected monthly cost and the capped worst case in one sentence each, written into your repo’s README.
Hints
- Set a low DNS TTL before you create or change the record, not after: it shrinks the window where one network resolves the name and another still serves a cached miss. DNS must resolve before the TLS cert can issue; a cert error on a fresh domain is usually “not provisioned yet,” not “misconfigured”; wait the clock out before re-pushing.
- Promote digests, not
latest. Record which digest is live (in your README or a deploy log) so rollback is a known target. If you ship fromlatestor rebuild per environment, you will not have a one-step rollback when you need it. - The headline instance price is not the bill. Find the attached resources before the invoice does: a reserved static IP and a load balancer bill 24/7 even at zero traffic.
Rubric — your project is done when
This is the standard the module holds you to (each bar maps to the lesson that taught it):
- A stranger can use it at a URL —
/predictreturns a prediction over a public URL from a machine that is not yours. (Lesson 1) - Promoted, not rebuilt — prod runs the same image digest validated in dev; no per-environment rebuild. (Lesson 2)
- TLS on, on a real domain — the service serves over
https://on a domain you control with a valid, auto-renewing certificate. (Lesson 3) - One-step rollback — you can repoint prod at the previous known-good digest in a single step and restore correct behavior fast. (Lesson 4)
- Cost bounded and stated — a max-instances cap and a budget alert are configured, and you can state the expected and worst-case monthly figure. (Lesson 5)
Run the self-audit from the Module Summary against your own deploy before you submit: for each rubric line, confirm it holds against the live service, and if one fails, name which lesson’s failure mode (a public broken build, a per-environment rebuild, an unprovisioned cert, a rebuild-from-source rollback, an uncapped autoscaler) you reintroduced.
Submit
Use the branch workflow from the M1 git lesson, not commits straight to main. Branch
off main (git checkout -b m8-cloud-deploy), build this module’s piece there, and open a
pull request to merge it back once it meets the rubric. main stays the last-good version
of the product you carry forward, so a half-finished module never breaks what later
modules build on.
When your deploy meets every rubric line, merge your branch to main, push the deploy
config to GitHub, and submit the repository URL and the live https:// URL a grader
can hit here. (Submission coming soon.)
Coming soon
This lesson is not published yet. Join the waitlist to hear when it ships.
Coming soon