Docker
Docker packages your application and everything it needs into an image that runs the same way on your laptop, a CI server and the cloud.
On this page · 5 min · Intermediate
On this page
- Learning objectives
- Explanation
- Visual explanation
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Intermediate
- Updated
- Oct 1, 2026
Related tutorials
Learning objectives#
- Distinguish images from containers
- Write a Dockerfile
- Use multi-stage builds to keep images small
- Run a container with ports and environment variables
Explanation#
An image is a read-only template: an operating system layer, a runtime, your code and its dependencies. A container is a running instance of an image.
A Dockerfile lists the steps to build an image. Each instruction creates a cached layer, so order instructions from least to most frequently changing.
Multi-stage builds use one stage to build and another, smaller stage to run. Build tools never reach the final image.
Visual explanation#
Code example: Multi-stage Dockerfile for a Node.js API#
# ---- build stage ----
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build --if-present
# ---- runtime stage ----
FROM node:22-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY /app ./
USER node
EXPOSE 3000
CMD ["node", "server.js"]
# Build and run:
# docker build -t techie-api .
# docker run --rm -p 3000:3000 -e JWT_SECRET=change-me techie-api
$ docker run --rm -p 3000:3000 techie-api API running on http://localhost:3000
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The build stage installs all dependencies with npm ci (reproducible, uses the lockfile) and builds the app.
- package*.json is copied before the rest of the code so the dependency layer is cached when only code changes.
- The runtime stage installs only production dependencies and copies the built app from the build stage.
- USER node runs the process as a non-root user.
- -p 3000:3000 maps the host port to the container port; -e passes environment variables.
Real-world use case#
Consistent local development, CI pipelines that build once and deploy the same image everywhere, and hosting on Azure Container Apps, AWS ECS or Kubernetes.
Common mistakes#
Add a .dockerignore with node_modules, .env, .git and build output.
If the app is compromised, root inside the container makes escape and damage easier. Use a non-root user.
latest changes over time. Pin base images and tag your images with versions or commit SHAs.
Best practices#
- Use multi-stage builds and small base images
- Add a .dockerignore
- Pin base image versions
- Pass configuration through environment variables
- Scan images for vulnerabilities in CI
Remember this#
Image = recipe output; container = running copy.
Same image runs the same everywhere.
Dockerfile → docker build → docker run.
Practice exercise#
Your task: Write a .dockerignore for the Node.js project above.
Show solution
node_modules
npm-debug.log
.env
.git
dist
coverage
Mini quiz#
What is the relationship between an image and a container?
Want more? Practise all DevOps MCQs · DevOps interview questions
My notes#
Saved in this browser.