BM: Baca dalam Bahasa Melayu
World D4: Set Meals
Capstone mission 1: Pack it
You will learn: to pack Kedai Kancil so one command starts it on any machine.
Checked with Docker Engine 29 on 31 Oct 2026
The problem
Mission 1: pack it! Kedai Kancil gets its own repo, kedai-kancil. First, put each file in its folder.
Drag a card into a box. Or tap the card, then tap the box.
Watch
<h1>Kedai Kancil</h1>FROM nginx:1.30-alpine
COPY index.html /usr/share/nginx/html/Your laptop~/kedai-kancil
- No open hatches
Lunchboxes (containers)
- No containers yet
Frozen packs (images)
- nginx:1.30-alpine8 layers · 93.5MB
- node:24-alpine4 layers · 244MB
- postgres:18-alpine9 layers · 433MB
- redis:8-alpine7 layers · 162MB
0 containers (0 running), 4 images, 0 lockers.
Top: web/index.html. Below: web/Dockerfile. Two lines: start from nginx, copy your page in.
Watch
The API code is below: api/server.js, then api/package.json. Its recipe will have no npm ci step. Why is that fine?
Pick one answer.
const http = require('node:http');
const port = process.env.PORT || 3000;
http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ ok: true, shop: 'Kedai Kancil' }));
}).listen(port, () => console.log(`API listening on port ${port}`));{
"name": "kedai-api",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node server.js",
"test": "node --test"
}
}Watch
FROM node:24-alpine
WORKDIR /app
COPY package.json server.js ./
USER node
EXPOSE 3000
CMD ["node", "server.js"]Your laptop~/kedai-kancil
- No open hatches
Lunchboxes (containers)
- No containers yet
Frozen packs (images)
- nginx:1.30-alpine8 layers · 93.5MB
- node:24-alpine4 layers · 244MB
- postgres:18-alpine9 layers · 433MB
- redis:8-alpine7 layers · 162MB
0 containers (0 running), 4 images, 0 lockers.
This is api/Dockerfile. USER node runs the API as a normal user, not root.
Try it
Now the set meal. Add web, api and db. Give db the locker db-data. Make api wait until db is healthy. The cache is optional.
Build a set meal: add the services, then join their phone lines and lockers.
Menu (tap or drag)
Your set meal
Empty. Tap a dish on the menu, or drag it here.
compose.yaml 1 lines
services: {}
Hint
Three dishes on the table. db gets db-data. api waits for db: healthy.Try it
Now do it for real, step by step. Your ticks are saved.
Real mission
mkdir -p kedai-kancil/web kedai-kancil/api && cd kedai-kancil && git initdocker compose config --quietdocker compose up -d --builddocker compose pscurl localhost:8080git remote add origin https://github.com/YOUR-NAME/kedai-kancil.gitgit add . && git commit -m "Pack the shop" && git branch -M main && git push -u origin maindocker compose down -vgit clone https://github.com/YOUR-NAME/kedai-kancil.git fresh && cd fresh && docker compose up -d
0 of 11 steps done · Your ticks are saved.
Check
GitHub can confirm part of it. Type your username and press Check now.
Check it on GitHub
- Your kedai-kancil repo is publicNot checked yet
- It has compose.yamlNot checked yet
- It has web/DockerfileNot checked yet
- It has api/DockerfileNot checked yet
Check
The rest only you can see. Tick what is true.
Self-check
0 of 5 steps done · Your ticks are saved.
You did it!
You can now pack a whole app so one command starts it anywhere.
Go deeper (optional)
Where the story breaks. A real set meal comes on one tray. Compose dishes are separate boxes. They share only phone lines and lockers. A meal is eaten once; a box can stop and serve again. Compose also cooks in one kitchen only. Many machines need the food court: Part 3.
Mission 2 (World C2) adds a green line.
Bahasa Melayu
Misi 1: bungkus! Kedai Kancil dapat repo sendiri, kedai-kancil. Mula-mula, letak setiap fail dalam foldernya.
<h1>Kedai Kancil</h1>FROM nginx:1.30-alpine
COPY index.html /usr/share/nginx/html/Atas: web/index.html. Bawah: web/Dockerfile. Dua baris: mula dari nginx, salin laman anda masuk.
Kod API ada di bawah: api/server.js, kemudian api/package.json. Resepinya nanti tiada langkah npm ci. Kenapa tak apa?
const http = require('node:http');
const port = process.env.PORT || 3000;
http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ ok: true, shop: 'Kedai Kancil' }));
}).listen(port, () => console.log(`API listening on port ${port}`));{
"name": "kedai-api",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node server.js",
"test": "node --test"
}
}
FROM node:24-alpine
WORKDIR /app
COPY package.json server.js ./
USER node
EXPOSE 3000
CMD ["node", "server.js"]Ini api/Dockerfile. USER node jalankan API sebagai pengguna biasa, bukan root.
Sekarang set hidangan. Tambah web, api dan db. Beri db loker db-data. Buat api tunggu sampai db sihat. Cache pilihan sahaja.
Sekarang buat betul-betul, langkah demi langkah. Tanda anda disimpan.
GitHub boleh sahkan sebahagiannya. Taip nama pengguna anda dan tekan Semak sekarang.
Selebihnya hanya anda boleh lihat. Tanda yang betul.
Kini anda boleh bungkus seluruh aplikasi supaya satu arahan hidupkannya di mana-mana.
You finished this before. Practice is always welcome!
Official docs: Compose quickstart · Dockerfile reference · Compose file reference
You've learned for 25 minutes. A short break helps your brain.
Sprint done! Great focus. Stretch for a minute?