Docker Practical Guide: Images, Containers, and Compose
Understand Docker from first principles and learn production-ready Dockerfiles, persistent storage, networking, security, and deployment habits.
Images, containers, and registries
A Docker image is an immutable package containing an application, runtime, libraries, and filesystem layers. A container is a running instance of that image with a writable layer and isolated process, network, and filesystem views.
A registry stores versioned images. Teams build an image once, scan it, push it to a registry, and promote the same digest through testing and production. This avoids rebuilding different artifacts for each environment.
Writing a production-friendly Dockerfile
Order instructions from least frequently changed to most frequently changed so Docker can reuse cached layers. Copy dependency manifests and install packages before copying application source.
Multi-stage builds keep compilers and development dependencies out of the final image. The runtime stage should use a small trusted base image, run as a non-root user, and contain only required files.
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]Volumes, networks, and Compose
- Named volumes persist database or uploaded data beyond a container's lifetime.
- Bind mounts map a host path and are useful for local source-code development.
- User-defined networks provide DNS resolution by service name.
- Docker Compose describes a multi-container development stack in one YAML file.
- Health checks report readiness; they do not replace application monitoring.
Security and reliability rules
- Pin important base images by version or digest and rebuild regularly for patches.
- Never bake passwords, API keys, or private certificates into image layers.
- Use .dockerignore to exclude source control, local dependencies, logs, and secrets.
- Set CPU and memory limits in the deployment environment.
- Log to standard output and keep durable state outside the container.
Key Takeaways
- An image is the packaged template; a container is its running process.
- Multi-stage builds reduce image size and attack surface.
- Persist state in volumes or managed services, not in the container layer.
- Build once and promote the same immutable image between environments.
Frequently Asked Questions
- What is the difference between EXPOSE and publishing a port?
- EXPOSE documents the port an image expects to use. The -p or --publish option actually maps a host port to a container port.
- Should a database run in Docker?
- It is convenient for local development and testing. In production, managed databases are often preferable because backups, failover, upgrades, and storage require careful operations.
- Why does Docker layer order matter?
- Changing one instruction invalidates that layer and all layers after it. Stable dependency steps near the top maximize cache reuse and shorten builds.