Learn / Prove it — practice ladder
Dockerize and automate an existing project
Lesson 36 of 37 · 8 min read ·
The brief
Take an existing project that currently runs with npm run dev and a database you installed by hand, and get it to: build as a container, run locally with one command, test automatically on every pull request, and deploy on merge to main.
The bar is specific — a new developer clones the repository and has it running in under five minutes without asking you anything.
Step 1: Dockerfile
- Multi-stage: build with the full toolchain, ship only the artefact. See containers.
- Dependency manifests copied and installed before source, so a source change does not reinstall dependencies.
-
.dockerignorewithnode_modules,.git,.env, build output. Check what you are actually sending:docker buildprints the context size. - Pinned base image tag. Not
latest. -
USERset to a non-root user. - Handles
SIGTERM— drains connections and exits, rather than being killed. - Config entirely from environment variables. No baked-in secrets, no build-time API keys.
Target: rebuild after a source-only change in under 30 seconds, and an image measured in tens of megabytes rather than gigabytes.
Step 2: Compose for local development
-
docker compose upstarts the app, the database and anything else it needs. - Database data in a named volume so it survives a restart.
- Healthcheck on the database, and
depends_on: condition: service_healthyso the app does not start against a database that is not ready. - Source mounted for hot reload in development.
-
.env.examplecommitted with every variable and dummy values. - Migrations run automatically on start, or via one documented command.
Step 3: CI on every pull request
Ordered cheapest-first so failures come back fast:
- Lint and type check.
- Unit tests.
- Build the image.
- Integration tests against real services in containers.
- Dependency audit and secret scan.
- Dependency cache between runs.
- Branch protection: no merging to main without green checks.
Target: under ten minutes from push to green. Beyond that people start working around it, and a pipeline people work around is worse than none. See CI/CD.
Step 4: Deploy on merge
- Build the image once, tag it with the commit SHA, push to a registry.
- Deploy that exact tag — never rebuild per environment.
- Migrations run before the new version takes traffic, and are backward compatible so the old version survives until it is replaced.
- Smoke test after deploy: hit
/healthand one real endpoint; fail the pipeline if either fails. - A documented rollback, which is redeploying the previous SHA. Do it once for real so you know it works and how long it takes.
- Deployment secrets in the CI provider's secret store, never in the workflow file.
The test
Wipe your local environment — docker compose down -v, delete node_modules. Then follow your own README exactly as written, without using anything you know that is not on the page.
Every point where you have to improvise is a missing line in the README. Fix it and repeat until the run is clean. Better still, hand it to someone else and watch without helping. It is uncomfortable and it is the fastest way to find every hidden assumption.
Prove you know it
Time three numbers: clone to running locally, push to green CI, merge to live in production. Then roll back a deploy and time that too. Those four numbers are the honest measure of the pipeline — and they are the numbers a team lead will ask you about.