What Is Docker? Images, Containers, Volumes, and Compose Explained
Learn how Docker works through images, containers, Dockerfiles, volumes, networks, and Compose with a practical Node.js example.
Article by Phạm Minh Thiện
Front-end developer working directly with Next.js, NestJS, Docker, Nginx, and Ubuntu VPS deployments.
Reviewed on 7/21/2026.
Table of contents
Docker packages an application and its dependencies into a repeatable image. A container is a running instance of that image with an isolated process, filesystem view, and network namespace.
Docker does not replace application architecture or operations. It gives teams a consistent unit for building, testing, shipping, and running.
Image, container, registry, and daemon
- An image is an immutable template made of filesystem layers.
- A container is a runtime instance of an image.
- A registry stores and distributes images.
- The Docker Engine daemon creates networks, containers, volumes, and images.
Unlike a full virtual machine, a container shares the host kernel. It usually starts faster and uses fewer resources, but it still needs correct isolation, updates, and limits.
A simple Node.js Dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
FROM selects a base image, WORKDIR sets the working directory, COPY adds files, RUN executes build steps, and CMD defines the default process. Multi-stage builds leave build tools out of the runtime image.
Build and run it
docker build -t my-api:1.0 .
docker run --rm -p 3000:3000 --env-file .env my-api:1.0
The -p mapping publishes host port 3000 to container port 3000. Environment variables and secrets should come from the runtime environment, not be baked into the image.
Layers and .dockerignore
Docker caches layers. Copy lockfiles and install dependencies before copying frequently changing source files so builds stay fast. Use .dockerignore to exclude .git, node_modules, local logs, and secrets:
node_modules
.git
.env*
dist
*.log
Volumes preserve data
The writable container layer is disposable. Use a named volume for stateful data:
docker volume create postgres_data
docker run -v postgres_data:/var/lib/postgresql/data postgres:16
Back up important data separately. A volume is persistence across container replacement, not a backup.
Networks let services find one another
Compose services on the same network can use service names as hostnames. An API should connect to db:5432, not localhost:5432, when PostgreSQL is another service.
Docker Compose
Compose describes multiple services in YAML:
services:
api:
build: .
ports: ["3000:3000"]
depends_on: [db]
db:
image: postgres:16-alpine
volumes: ["postgres_data:/var/lib/postgresql/data"]
volumes:
postgres_data:
Useful commands include:
docker compose up -d
docker compose ps
docker compose logs -f api
docker compose exec api sh
docker compose down
Use docker compose down -v only for disposable data because it removes project volumes.
Development and production are different
Development may mount source code and use hot reload. Production should use a built, pinned image, protected secrets, health checks, restart policy, log limits, private networks, backups, and a rollback plan.
Do not expose databases or admin ports publicly by default. Scan and update base images, run as a non-root user when supported, and set CPU or memory limits for noisy workloads.
Common mistakes
- Using
latestand losing track of the deployed version. - Copying secrets into an image layer.
- Treating a volume as a backup.
- Connecting between containers through
localhost. - Publishing every internal port to the internet.
- Forgetting
.dockerignoreand sending a huge build context. - Assuming
depends_onalone means an application is ready.
Docker is most useful when the image is reproducible, the runtime is observable, and data recovery is planned before a container fails.
Continue reading
Related articles
Docker Compose Ignores .env and PostgreSQL Says Password Authentication Failed
Understand .env, env_file, and environment in Docker Compose, inspect variables inside a container, and fix PostgreSQL passwords when a volume already exists.
Read article →Docker Port Is Already Allocated: Find the Process Using a Port
Fix Docker port is already allocated, address already in use, and EADDRINUSE errors on Windows and Linux without stopping the wrong process.
Read article →Docker Compose in Production: Node.js, PostgreSQL, and Backups
Build a safer Docker Compose production stack for Node.js and PostgreSQL with health checks, private networks, volumes, secrets, log rotation, backups, and rollback.
Read article →