Dockerize a Project
A practical guide to packaging real-world applications into Docker containers — the hands-on bridge between Docker Fundamentals and Kubernetes orchestration.
What is Dockerization?
Dockerization is the process of packaging an application and all its dependencies into a Docker image so it can run consistently anywhere. It eliminates the “works on my machine” problem by ensuring the runtime environment is identical across development, staging, and production.
The Dockerfile Anatomy
A Dockerfile is a text document containing instructions to build a Docker image. Each instruction creates a cached layer.
# ─── Stage 1: Builder ───
FROM node:18-alpine AS builder
WORKDIR /app
# Copy dependency files first (for layer caching)
COPY package*.json ./
RUN npm ci --only=production
# Copy source and build
COPY . .
RUN npm run build
# ─── Stage 2: Runtime ───
FROM node:18-alpine
WORKDIR /app
# Create non-root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# Copy only built artifacts from builder stage
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
USER nextjs
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "dist/main.js"]Key Instructions
| Instruction | Purpose | Example |
|---|---|---|
FROM | Base image to start from | FROM node:18-alpine |
WORKDIR | Set working directory inside container | WORKDIR /app |
COPY | Copy files from host to container | COPY package*.json ./ |
RUN | Execute commands during build | RUN npm install |
EXPOSE | Document which port the container listens on | EXPOSE 3000 |
ENV | Set environment variables | ENV NODE_ENV=production |
USER | Run as non-root user | USER nextjs |
CMD | Default command when container starts | CMD ["node", "server.js"] |
ENTRYPOINT | Configure container as executable | ENTRYPOINT ["python", "app.py"] |
The Dockerize Workflow
- Create a
.dockerignore— excludenode_modules,.git, build artifacts, large data files. - Write the
Dockerfile— choose a slim base image, setWORKDIR, order instructions for cache efficiency. - Build the image —
docker build -t myapp:1.0 . - Test locally —
docker run -d -p 8080:3000 myapp:1.0 - Push to registry —
docker push registry.example.com/myapp:1.0
Layer Caching & Optimization
- Docker builds images in layers — each instruction creates a cached layer.
- If a layer changes, all subsequent layers must be rebuilt.
- Golden Rule: Copy dependency files (
package.json,requirements.txt,pom.xml) before the full source code so dependency installation is cached and only rebuilds when dependencies change.
# ✅ Good — dependencies cached unless package.json changes
COPY package*.json ./
RUN npm ci
COPY . .
# ❌ Bad — any source change invalidates the npm install cache
COPY . .
RUN npm installMulti-Stage Builds
Multi-stage builds allow you to use multiple FROM statements in a single Dockerfile, copying only what you need into the final image.
Benefits:
- Smaller final image size (no build tools in production image)
- Reduced attack surface
- Faster deployments
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# Runtime stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]Common Pitfalls
| Pitfall | Why It Matters | Fix |
|---|---|---|
No .dockerignore | Bloated build context, slow builds, leaked secrets | Add .dockerignore with node_modules, .env, .git |
Using latest tag | Non-reproducible builds, unexpected breakages | Pin to specific versions: node:18-alpine |
| Running as root | Security risk if container is compromised | Use USER instruction or run with --user flag |
| Hardcoded config | Limits portability across environments | Use ENV variables and config files mounted at runtime |
| Ignoring signals | Containers don’t shut down gracefully | Use exec in entrypoint scripts so PID 1 receives SIGTERM |
| Large images | Slower pulls, more storage, larger attack surface | Use Alpine or Distroless base images; multi-stage builds |
Essential Build & Run Commands
| Command | Purpose |
|---|---|
docker build -t myapp:1.0 . | Build image with tag from current directory |
docker run -d -p 8080:3000 myapp:1.0 | Run container in detached mode, map ports |
docker run -it --rm myapp:1.0 /bin/sh | Interactive shell access, auto-remove on exit |
docker logs <container_id> | View application logs |
docker exec -it <container_id> /bin/sh | Shell into a running container |
docker inspect <image_id> | Inspect image layers and metadata |
Sources
Related Pages
Tags: docker dockerfile containerization devops cka multi-stage-builds