Skip to main content

Workflows — The Daily Kitchen Routine

The five friends have learned the commands and written their recipes. Now they need a routine — a shared way of working that keeps every developer's kitchen consistent and every deployment predictable.

The Core Loop​

The loop has three rules:

  1. Develop inside containers — no "works on my machine" ever again
  2. Build once, run anywhere — the image is the same on every machine
  3. One container per service — web server, database, and cache each get their own
Remember

If you ever hear "it works on my machine," the answer is always: "then let's ship your machine."

Local Development — Your Personal Kitchen​

The Problem​

Without Docker, each developer installs a different version of Node, Postgres, Redis, and a dozen other tools directly on their laptop. When something breaks, nobody knows why.

The Solution​

docker-compose.yml — the full development kitchen
services:
web:
build: .
ports:
- "3000:3000"
volumes:
- .:/app # Live-reload: your code changes appear instantly
- /app/node_modules # Don't overwrite node_modules with local copy
environment:
- NODE_ENV=development
depends_on:
- db
- redis

db:
image: postgres:16
environment:
POSTGRES_DB: campus_library
POSTGRES_USER: riya
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/data

redis:
image: redis:7-alpine
ports:
- "6379:6379"

volumes:
db-data:
Start the whole development environment
docker compose up -d
See what's running
docker compose ps
NAME IMAGE STATUS PORTS
web campus-lib Up 2 minutes 0.0.0.0:3000->3000/tcp
db postgres:16 Up 2 minutes 0.0.0.0:5432->5432/tcp
redis redis:7 Up 2 minutes 0.0.0.0:6379->6379/tcp
Remember

The volumes mount (.:/app) means your local code changes are reflected inside the container instantly — no rebuild needed for code changes.

Rebuilding — When the Recipe Changes​

If you change package.json (add a dependency), you need to rebuild:

Rebuild after dependency changes
docker compose build
docker compose up -d

Or force a full rebuild:

No-cache rebuild
docker compose build --no-cache
docker compose up -d
Common mistake

Forgetting docker compose build after changing dependencies means the container still uses the old node_modules. If a new package isn't found, rebuild.

Production Images — The Catering Order​

When the friends are ready to ship, they build a production image — a minimal, optimized version:

Production Dockerfile (multi-stage)
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
Build the production image
docker build -t campus-library:1.0.0 .
Remember

Tag your production images with a version (1.0.0, not just latest). This makes rollbacks trivial — just point back to the previous tag.

Pushing to a Registry — Sharing the Recipe​

A container registry (Docker Hub, AWS ECR, GitHub Container Registry) is a shared shelf where everyone can store and pull images.

Tag the image for your registry
docker tag campus-library:1.0.0 riya/campus-library:1.0.0
Push to Docker Hub
docker push riya/campus-library:1.0.0
Anyone can now pull it
docker pull riya/campus-library:1.0.0
docker run -d -p 3000:3000 riya/campus-library:1.0.0
Remember

docker push uploads your image to the registry. docker pull downloads it. This is how images travel from your laptop to a production server.

Environment Management — Different Kitchens for Different Meals​

Using .env files​

.env
APP_PORT=3000
DB_PASSWORD=secret
NODE_ENV=development
docker-compose.yml
services:
web:
ports:
- "${APP_PORT}:3000"
env_file:
- .env

Override files​

Production override
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Remember

Use .env files for local development secrets. Never commit .env to Git — add it to .gitignore.

Health Checks — Testing the Lunch Box​

Docker can automatically check if a container is healthy:

Dockerfile with health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:3000/health || exit 1
docker-compose.yml with health check
services:
web:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
Check container health
docker inspect --format='{{.State.Health.Status}}' my-web
healthy
Remember

Health checks are the quality inspector. They catch problems before users do.

Cleanup — Washing the Dishes​

Stop and remove all containers
docker compose down
Remove volumes too (data loss!)
docker compose down -v
Prune everything unused
docker system prune -a --volumes
Common mistake

docker compose down -v deletes all data in named volumes. Only use it when you truly want a fresh start.