The first useful thing Docker gave me was not “cloud-native architecture.” It was a boring promise: the application should behave the same way on another machine.
Before containers, a setup could depend on the exact Node version, system packages, environment variables, or a database installed on one laptop. Docker makes those assumptions explicit and packages the application process with its runtime dependencies.
Image, container, and volume
These three words explain most day-to-day Docker work:
- An image is an immutable template built from a Dockerfile.
- A container is a running instance of that image.
- A volume stores data that should survive when a container is replaced.
A container is not a tiny virtual machine. Containers share the host kernel, which is one reason they usually start faster and use fewer resources than full VMs.
A small Node example
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
CMD ["npm", "start"]
Build and run it:
docker build -t notes-api .
docker run --rm -p 3000:3000 --env-file .env notes-api
The port mapping exposes container port 3000 on the host. The environment file supplies configuration at runtime instead of baking secrets into the image.
Where Docker helps
Docker is useful when I need:
- reproducible local environments;
- isolated services such as Postgres or Redis;
- the same artifact in CI and deployment;
- an explicit record of system dependencies.
It does not automatically make an application scalable or secure. A large image is still large. A process running as root is still risky. A stateful service still needs backups.
A few habits that prevent pain
Use a .dockerignore file, pin important base-image versions, run as a non-root user, keep secrets outside the image, and rebuild images instead of manually changing running containers.
For local multi-service work, Compose is often enough:
services:
app:
build: .
ports: ["3000:3000"]
env_file: .env
db:
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: local-only
The mental model I keep
Docker packages a process and its dependencies behind a repeatable boundary. That boundary is valuable, but it is not magic. You still need to understand networking, storage, configuration, and the application itself.
Top comments (0)