Docker bundles your application with its runtime, libraries, and config into an image — a snapshot you can run anywhere Docker is installed. Run that image and you get a container: an isolated process that behaves the same on your Mac, a Linux server, or a cloud VM.
The Dockerfile is a recipe. Start from a base image (`node:20`, `python:3.12`), copy your code, install dependencies, set the start command. `docker build` creates the image; `docker run` starts a container. Share images through Docker Hub or a private registry your company controls.
Why teams love it: onboarding is faster (new devs run one command instead of a three-page setup doc), environments match production more closely, and you can run multiple services — web app, database, Redis — with Docker Compose in one file.
Containers are not tiny VMs. They share the host kernel and start much faster. They are also not magic — stateless by default. Data in a container disappears when it stops unless you mount a volume or use an external database.
Common pitfalls: huge images because you copied everything including `.git`, running as root when you do not need to, and forgetting to pin dependency versions. Multi-stage builds slim images by compiling in one stage and copying only the output to the final image.
You do not need Kubernetes on day one. Docker alone fixes local dev and small deploys. Learn images, containers, Compose, and volumes first. Orchestration comes when one server is not enough.