CD10: Capstone: three DevOps projects for RouteRani
Project 1: an environment from code
🎯You'll be able to
Bring the starter's hand-made-looking environment up to a reproducible, safe standard that passes all 10 infrastructure tests.
🏠The everyday picture
An inspector checks a new flat against a list: wiring certified, gas pipes sealed, the meter cupboard locked, the drawings matching what was built. The flat gets its certificate only when every item is ticked, and anyone can check the same list again later.
📘What it really is
Project 1 asks you to fix these in the starter:
-
the image (CD1):
FROM node:22,COPY . ., root user, no health check,npm start. Fix with a pinned slim base, two stages,.dockerignore,USER node, aHEALTHCHECKon/readyandCMD ["node", "server.js"]; -
versions and secrets (CD3 Lesson 5): an unpinned Docker provider and the password
routerani123written twice. Pin both providers, generate the password withrandom_password, use it for the database and the connection string; -
the database (CD2): published on port 15432 of your computer, no volume, no health check. Remove the port, add a named volume, a
pg_isreadyhealth check andwait = true; -
the API copies (CD2 Lesson 4, CD3 Lesson 4): one copy named
routerani-<env>-appusingrouterani:latest. Addapp_versionandapp_countvariables with validation,count, a name that includes the version and a random suffix,create_before_destroy, memory limits andwait = true, and nodepends_onthe database; -
outputs: add
app_containersandapp_version. The tests then check formatting, the image, the running environment, no drift, drift repair, data surviving a database replacement, and a clean destroy and rebuild.
📝A worked example
Input: after your changes, run npm run test:infra in tests/.
Expected output, in part:
ok 1 - 1. the Terraform files are formatted and valid
ok 2 - 2. no password is written in the Terraform files, provider versions are pinned, and no image uses "latest"
ok 3 - 3. the image runs as a non-root user, has a health check and leaves out node_modules and tests from the build
...
ok 10 - 10. terraform destroy removes everything, and the environment can be rebuilt from the files
# pass 10
# fail 0
On the 4-core test machine on 1 November 2026, the reference solution passed all 10 in about 2 minutes, plus the image build.
🌍Where it's used
- Taking over environments built by hand, one resource at a time.
- Audits that ask for proof an environment can be rebuilt.
- Disaster recovery drills: destroy a test environment and rebuild it from code.
📝Case scenario
A learner's test 7 ("a second plan shows no changes") keeps failing: each plan wants to update the API copies' memory_swap. Docker sets swap
to double the memory limit when it isn't given, and the provider reads that back as a change. Setting memory_swap = 256 alongside
memory = 256 makes the plan clean. Reading the plan's exact attribute, not just "1 to change", found the cause in minutes.
🖱️Try it
Fix the image first and run only test 3 (node --test --test-name-pattern="non-root" infra.test.js), then move on to the Terraform. Success:
each fix turns at least one more test green, and you know which change did it.
⚠️Common mistakes
- Fixed names with
create_before_destroy. Sign: "name already in use" during deploys or rollbacks. Fix: include the version and a random suffix. depends_onthe database from the copies. Sign: replacing the database fails or starts two databases on one volume. Fix: nodepends_on; the API retries.- Skipping
wait = true. Sign: Terraform finishes before copies are healthy, and tests see errors. Fix: wait for health checks.
🛠️For your project
Save the full npm run test:infra output and the image sizes before and after on the Project 1 page.
✅Quick check
Why does each API copy's name include the version and a random suffix?
Show the answer
So new copies can start alongside old ones, including when rolling back to an earlier version.
What does test 9 prove?
Show the answer
Bookings survive replacing the database container, because the data is on a volume.
