What is Docker
Docker is a tool that packages an application and its dependencies into a lightweight, portable container. Containers run the same way on any machine with Docker installed, so you get reproducible builds, isolated processes, and faster iterations compared to installing dependencies directly on each host.
Why use Docker:
- Reproducible development environments
- Easier deployment of services and microservices
- Isolation between projects (no global dependency conflicts)
Concept explained (short)
- Image: a read-only snapshot built from a Dockerfile. Think of it as the app + runtime files.
- Container: a running instance of an image. It has its own filesystem, processes, network stack (namespaced).
- Dockerfile: a text file with steps to build an image (install deps, copy files, set command).
- Layers: images are built in layers – caching speeds up rebuilds.
- Volumes: for persistent data that survives container restarts.
- Networking: containers can expose ports and join networks; typical pattern maps container port -> host port.
Image ≠ Container. Build an image once, then run multiple containers from it.
Step-by-step example
A minimal Node app and Dockerfile. Create three files:
-
package.json
{
"name": "simple-app",
"version": "1.0.0",
"scripts": { "start": "node app.js" },
"dependencies": {}
} -
app.js
const http = require("http");
const port = process.env.PORT || 3000;
http.createServer((req, res) => res.end("Hello from Docker!")).listen(port); -
Dockerfile
FROM node:18-slim
WORKDIR /app
COPY package.json ./
COPY app.js ./
EXPOSE 3000
CMD ["npm", "start"]
Commands:
# build image
docker build -t my-simple-app .
# run container mapped to host port 3000
docker run -d --name myapp -p 3000:3000 my-simple-app
# check running containers & logs
docker ps
docker logs myapp
Stop & remove:
docker stop myapp && docker rm myapp
Use a .dockerignore to avoid copying node_modules, local logs, or build artifacts into the image.
Variations & gotchas
- Docker Compose: use when your app needs multiple services (app + db + redis).
- Build context: copying unnecessary files slows Docker builds. Use .dockerignore to exclude large or irrelevant directories.
- File permissions: files created by the container may be owned by root on your host. Consider USER in Dockerfile or chown with volumes.
- Ephemeral filesystem: anything written inside a container without a volume will be lost when the container is removed.
Avoid baking secrets (API keys, passwords) into images. Use environment variables, Docker secrets, or the platform's secret store.
Common mistakes
- Forgetting .dockerignore → huge images and slow builds.
- Running heavy processes in a single container without resource limits.
- Using latest tags in production – leads to unpredictable deployments.
- Storing DB data inside container filesystem instead of a volume.
- Exposing internal ports to the public host unintentionally.
Best practices
- Use small base images (alpine or slim variants) when appropriate.
- Multi-stage builds to keep final images small:
# build stage
FROM node:18 AS build
WORKDIR /app
COPY . .
RUN npm install && npm run build
# runtime stage
FROM node:18-slim
WORKDIR /app
COPY --from=build /app/dist ./dist
CMD ["node", "dist/index.js"]
- Add HEALTHCHECK for production services.
- Run processes as a non-root user in containers (USER).
- Pin dependency and base image versions.
- Use volumes for persistent data (databases, uploaded files).
When to use / when not to use
When to use Docker:
- You want reproducible dev environments and easier CI/CD.
- You need to run multiple services together (app + db + cache).
- You want to move from local dev to a PaaS without rewriting the app.
When not to use Docker:
- If your app requires custom kernel modules or special hardware access (GPU/FPGA), a VM or bare-metal may be required.
- For trivial single-process scripts where overhead and learning cost outweigh benefits.
Key takeaways
- Docker packages apps into portable images; containers are running instances of those images.
- Use Dockerfiles and .dockerignore to create small, reproducible images.
- Prefer volumes for persistent data and avoid baking secrets into images.
- Docker Compose handles multi-service setups locally; use a PaaS for production deployments.