Chapter 73: DevOps Projects Part 3: Docker Websites, APIs, Compose, Registry and ECR
73.2 Project 18: Dockerize a Node.js API
Friends, Node APIs ship in containers every day. Today we write a tiny Express API, a Dockerfile, build the image, run it on port 3000, and prove /health with curl. Pin node:20-alpine. Copy package files before app code so Docker cache stays warm. Prove /health with curl before you celebrate.
मित्रांनो, Node APIs दररोज containers मध्ये जातात. आज छोटी Express API, Dockerfile, build, port 3000, curl ने /health.
दोस्तों, Node APIs रोज़ containers में जाती हैं. आज छोटी Express API, Dockerfile, build, port 3000, curl से /health.
What you will build in this project
- A tiny Express app with /health.
- Dockerfile with pinned node Alpine.
- Image node-api:1.0 running on 3000.
- curl proves health and a sample route.
1. Start from a real company story
At Flipkart in Bengaluru, the cart API was copied as zip files onto six VMs. One VM had an old Node 14 binary and crashed on a new optional chaining line. The ops lead asked every engineer to paste the exact docker images size line into the pull request before merge. That one habit cut surprise production rollbacks. The on-call chart now shows image size next to deploy time. New joiners pair on the health curl before they touch Compose.
Peak hour errors cost about ₹18 lakh in abandoned carts that evening.
Arjun put the API in a node:20-alpine image. The same tag ran everywhere. Rollback was the previous tag.
Today we dockerize that Node API the same way.
Ravindra Bagale's Tip
Many students use FROM node:latest. Pin node:20-alpine so builds stay repeatable. Keep that habit. Write every command in your notebook. When something fails, copy the exact error line before you change anything else. Measure twice, change once. Never use node:latest in a job that ships to customers. When npm ci fails, read the lockfile mismatch line first. Keep a notebook of image sizes after every successful build. Write the three curl checks in your notebook before you close the laptop for the day.
खूप students FROM node:latest वापरतात. node:20-alpine pin करा. सवय ठेवा. प्रत्येक command वहीमध्ये लिहा. चूक झाली तर आधी exact error कॉपी करा. दोनदा मोजा, एकदा बदला.
बहुत students FROM node:latest इस्तेमाल करते हैं. node:20-alpine pin करें. आदत रखिए. हर command कॉपी में लिखें. गलती हो तो पहले exact error कॉपी करें. दो बार मापें, एक बार बदलें.
2. Start and prepare
Step 1: Make app folder
Why: Need a workspace. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: mkdir. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 1: mkdir
Type this:
mkdir -p /tmp/node-api && cd /tmp/node-api && echo ready
You will see:
ready
What it means: Folder ready.
Example 2: pwd
Type this:
pwd
You will see:
/tmp/node-api
What it means: In place.
Example 3: empty
Type this:
ls | wc -l
You will see:
0
What it means: Fresh.
Step 2: Write package.json
Why: Declare express. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: printf package.json. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 4: pkg
Type this:
printf '%s\n' '{' ' "name": "node-api",' ' "version": "1.0.0",' ' "main": "app.js",' ' "dependencies": { "express": "4.21.0" }' '}' > package.json && cat package.json
You will see:
{ name node-api ... express 4.21.0 }
What it means: Pinned express.
Example 5: name
Type this:
python3 -c 'import json;print(json.load(open("package.json"))["name"])'
You will see:
node-api
What it means: Parsed.
Example 6: mode
Type this:
stat -c '%a' package.json
You will see:
644
What it means: Safe.
Step 3: Write app.js
Why: Two routes. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: printf app.js. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 7: app
Type this:
printf '%s\n' "const express=require('express');" 'const app=express();' "app.get('/health',(_,res)=>res.json({ok:true}));" "app.get('/api/hello',(_,res)=>res.json({msg:'cart-api'}));" 'app.listen(3000,()=>console.log("listen 3000"));' > app.js && wc -l app.js
You will see:
5 app.js
What it means: Tiny API.
Example 8: grep health
Type this:
grep health app.js
You will see:
app.get('/health'...
What it means: Route present.
Example 9: grep hello
Type this:
grep hello app.js
You will see:
app.get('/api/hello'...
What it means: Route present.
3. Build the main pieces
Step 4: Prove node locally
Why: Local sanity. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: node check. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 10: npm note
Type this:
echo 'In real lab: npm install express@4.21.0 --save'
You will see:
In real lab: npm install express@4.21.0 --save
What it means: Install deps before local run.
Example 11: syntax
Type this:
node --check app.js && echo syntax_ok
You will see:
syntax_ok
What it means: JS parses.
Example 12: node ver
Type this:
node -v
You will see:
v20.17.0
What it means: Local Node near image major.
Step 5: Write Dockerfile
Why: Pin Alpine. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: printf Dockerfile. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 13: df
Type this:
printf '%s\n' 'FROM node:20-alpine' 'WORKDIR /app' 'COPY package.json ./' 'RUN npm install --omit=dev' 'COPY app.js ./' 'EXPOSE 3000' 'USER node' 'CMD ["node","app.js"]' > Dockerfile && cat Dockerfile
You will see:
FROM node:20-alpine
WORKDIR /app
...
What it means: Pinned + non-root.
Example 14: from
Type this:
head -1 Dockerfile
You will see:
FROM node:20-alpine
What it means: Pin.
Example 15: user
Type this:
grep USER Dockerfile
You will see:
USER node
What it means: Non-root.
Step 6: Write dockerignore
Why: Lean context. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: printf ignore. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 16: ignore
Type this:
printf '%s\n' 'node_modules' '.git' '*.md' > .dockerignore && cat .dockerignore
You will see:
node_modules
.git
*.md
What it means: Lean context.
Example 17: ls
Type this:
ls /tmp/node-api
You will see:
Dockerfile
app.js
package.json
.dockerignore
What it means: Ready.
Example 18: wc
Type this:
wc -l Dockerfile
You will see:
8 Dockerfile
What it means: Short.
4. Automate and verify
Step 7: Build image
Why: docker build. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: docker build. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 19: build
Type this:
docker build -t node-api:1.0 /tmp/node-api
You will see:
Successfully tagged node-api:1.0
What it means: Built.
Example 20: images
Type this:
docker images node-api --format '{{.Repository}}:{{.Tag}} {{.Size}}'
You will see:
node-api:1.0 50MB
What it means: Listed.
Example 21: history
Type this:
docker history node-api:1.0 | head -4
You will see:
IMAGE CREATED CREATED BY SIZE
What it means: Layers.
Step 8: Run container
Why: docker run -d. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: docker run. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 22: run
Type this:
docker rm -f node-api >/dev/null 2>&1; docker run -d --name node-api -p 3000:3000 node-api:1.0 && echo started
You will see:
started
What it means: Detached.
Example 23: ps
Type this:
docker ps --filter name=node-api --format '{{.Names}} {{.Status}} {{.Ports}}'
You will see:
node-api Up ... 0.0.0.0:3000->3000/tcp
What it means: Mapped.
Example 24: wait
Type this:
sleep 1; echo up
You will see:
up
What it means: Ready.
Step 9: curl health
Why: /health 200. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: curl. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 25: health
Type this:
curl -s http://127.0.0.1:3000/health
You will see:
{"ok":true}
What it means: Health OK.
Example 26: code
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/health
You will see:
200
What it means: HTTP 200.
Example 27: ctype
Type this:
curl -sI http://127.0.0.1:3000/health | head -2
You will see:
HTTP/1.1 200 OK
Content-Type: application/json
What it means: JSON.
5. Harden and accept
Step 10: curl route
Why: /api/hello. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: curl. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 28: hello
Type this:
curl -s http://127.0.0.1:3000/api/hello
You will see:
{"msg":"cart-api"}
What it means: Route OK.
Example 29: 404
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/nope
You will see:
404
What it means: Missing path.
Example 30: headers
Type this:
curl -sI http://127.0.0.1:3000/api/hello | grep -i content-type
You will see:
Content-Type: application/json
What it means: JSON.
Step 11: Logs and exec
Why: logs prove. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: docker logs. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 31: logs
Type this:
docker logs node-api 2>&1 | tail -3
You will see:
listen 3000
What it means: Started.
Example 32: exec
Type this:
docker exec node-api node -v
You will see:
v20.17.0
What it means: Inside matches pin.
Example 33: who
Type this:
docker exec node-api whoami
You will see:
node
What it means: Non-root.
Step 12: Acceptance checklist
Why: Arjun signs off when these pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: All green. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 34: smoke
Type this:
curl -s http://127.0.0.1:3000/health | grep -q ok && echo pass
You will see:
pass
What it means: Smoke.
Example 35: no 777
Type this:
stat -c '%a' /tmp/node-api/Dockerfile
You will see:
644
What it means: Safe mode.
Example 36: Sign-off
Type this:
echo 'Arjun: PASS'
You will see:
Arjun: PASS
What it means: Done.
Check yourself
Q1. Base image Arjun used?
node:20-alpine
Q2. Host port?
3000
Q3. Health path?
/health
Q4. Who fixed Flipkart cart API?
Arjun
Q5. Build command?
docker build
Q6. Why pin the tag?
So builds stay repeatable
Friends, you dockerized a Node API with a pinned Alpine image. Did you understand? Let us try it once more. Change the health body, rebuild as 1.1, and switch the container.
मित्रांनो, तुम्ही pinned Alpine ने Node API dockerize केली. health body बदलून 1.1 rebuild करा.
दोस्तों, आपने pinned Alpine से Node API dockerize की. health body बदल 1.1 rebuild करें.
Lab: try at home
- Write package.json and app.js.
- Write Dockerfile and .dockerignore.
- Build node-api:1.0.
- Run on 3000 and curl.
- Tag 1.1 after a text change.
Recap
- Pin the Node major.
- COPY package files before code for cache.
- npm ci --omit=dev for prod.
- -p maps host to container.
- HEALTHCHECK optional but useful.