Docker Explained: What Containers Are and Why Developers Use Them
“It works on my machine.”
If you’ve spent time around software developers, you’ve heard this phrase. Code that runs perfectly on the developer’s laptop mysteriously fails in production. The cause is almost always environmental differences — different OS versions, different library versions, different configurations, different anything.
Docker was built to eliminate this problem. It did, and in the process reshaped how software is built, shipped, and run.
The Problem: Environments Are Fragile
Software doesn’t run in isolation. Every application depends on a specific version of a runtime (Python 3.11, Node.js 18), specific libraries (and their specific versions), configuration files, environment variables, and the underlying operating system.
Getting all of these to match between your development laptop, your teammate’s machine, your test environment, and your production server is genuinely difficult. A library upgrade on one machine, a different OS version on another, a missing environment variable in production — any of these can break software that works fine elsewhere.
The traditional solution was virtual machines. But VMs are heavy: each one runs a full operating system, consuming gigabytes of disk space and significant CPU and memory just for the OS overhead.
Docker found a better answer: containers.
What a Container Is
A container is a lightweight, isolated environment that packages an application with everything it needs to run: code, runtime, libraries, and configuration. It runs on top of the host operating system’s kernel but is isolated from the host and from other containers.
Crucially, containers are:
Lightweight: Unlike VMs, containers share the host OS kernel. They don’t need to run a full OS. A container might be 100MB where an equivalent VM would be 10GB.
Fast: Containers start in seconds or less. VMs take minutes to boot.
Consistent: The same container image runs identically on a developer’s MacBook, a CI/CD server, and a production Linux server. The environment is defined in code and doesn’t vary.
Isolated: Containers are separated from each other and from the host. A problem in one container doesn’t affect others.
Docker: The Container Platform
Docker is the dominant tool for building and running containers. It consists of:
Docker Engine: The runtime that creates and runs containers on your system.
Docker images: Read-only templates that define what a container contains. An image might specify: start with Ubuntu, install Python 3.11, copy in my application code, install its dependencies. Every container is created from an image.
Dockerfile: A text file containing the instructions for building an image. This is the “recipe.”
Docker Hub: A registry of public images. Instead of building everything from scratch, you start from an existing image — python:3.11, node:20, postgres:15 — and add your application on top.
Docker Compose: A tool for defining and running multi-container applications. Your app might need a web server, a database, and a cache — Compose lets you define all three in one file and start them together with a single command.
A Simple Example
Say you’re building a Python web application. Without Docker, anyone who wants to run it needs to:
- Install the right version of Python
- Install the right versions of all dependencies
- Set up the right environment variables
- Hope their OS doesn’t cause any conflicts
With Docker, you write a Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"] This says: start from the official Python 3.11 image, set the working directory, copy the requirements file, install dependencies, copy the application code, and define how to run it.
Anyone with Docker installed can now run your application with:
docker build -t my-app .
docker run -p 8000:8000 my-app Same command. Same result. Any machine, any OS.
Docker Compose: Running Multiple Containers
Real applications usually have multiple components. A typical web app might need:
- A web server running your application
- A PostgreSQL database
- A Redis cache
docker-compose.yml defines all of them:
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: example
redis:
image: redis:7 docker compose up starts all three containers, properly networked together. docker compose down stops them. Every developer on the team runs identical infrastructure with a single command.
Images and Layers
Docker images are built in layers. Each instruction in a Dockerfile creates a new layer:
FROM python:3.11-slim # Layer 1: base image
RUN pip install flask # Layer 2: install Flask
COPY . . # Layer 3: your application code Layers are cached. If you rebuild the image after changing only your application code, Docker reuses the cached layers for the Python install and only rebuilds from the changed layer. Builds that would take minutes become seconds.
This layering also means images share common layers. If ten images all start from python:3.11-slim, that base layer is stored once on disk, not ten times.
The Relationship to Production
Docker’s real impact goes beyond developer laptops. The same container image that developers test locally gets:
Pushed to a registry: Docker Hub, AWS ECR, Google Container Registry. The image is stored and versioned.
Pulled by servers: Production servers pull and run the exact same image. No deployment scripts that recreate an environment — just run the image you already tested.
Orchestrated at scale: Tools like Kubernetes manage dozens or thousands of containers across multiple servers — handling load balancing, scaling, health checks, and rolling deployments. All of this is built on containers.
Docker vs. Virtual Machines
| Docker Containers | Virtual Machines | |
|---|---|---|
| Startup time | Seconds | Minutes |
| Disk size | MBs | GBs |
| OS overhead | Shared kernel | Full OS per VM |
| Isolation | Process-level | Full hardware virtualization |
| Best for | Application packaging | Full OS isolation |
VMs still have their place — running a different OS entirely, stronger security isolation, or legacy systems. But for packaging and deploying applications, containers have largely displaced VMs.
Getting Started with Docker
Install Docker Desktop from docker.com. It includes Docker Engine, Docker Compose, and a GUI dashboard. Available for Mac, Windows, and Linux.
Run your first container:
docker run hello-world This pulls the hello-world image from Docker Hub and runs it. A good first test.
Run an interactive container:
docker run -it ubuntu bash Drops you into a bash shell inside an Ubuntu container. Exit and the container stops. Nothing on your actual system is affected.
Try Docker Compose with a sample application. The Docker documentation has a getting-started tutorial that walks through a full web app with a database — the best hands-on introduction.
When You Should Care About Docker
If you’re developing software: Docker standardizes your development environment and makes onboarding new teammates straightforward. Instead of a 20-step setup guide, it’s docker compose up.
If you’re deploying applications: Containers make deployment predictable. What works in testing works in production.
If you’re learning: Understanding containers is now a baseline expectation for software engineers. Cloud platforms, CI/CD systems, and modern infrastructure all speak Docker.
Docker solved a real problem that frustrated developers for decades. The learning curve is modest, the payoff is immediate, and the concepts transfer directly to how modern infrastructure works at scale. If you’re building software and haven’t tried it yet, this week is a good time to start.
Written by Marcus Thorne
Software analysis and cybersecurity tips
A former software engineer, Marcus transitioned into tech journalism to explain complex digital concepts in simple terms.
You Might Also Like

Why Most Home Server Setups Disappoint (And What Actually Works for Real Control)
Discover why common home server setups often fail to deliver, and learn actionable strategies for building a reliable, powerful server that meets your needs.

Why Most USB-C Hubs Are a Headache (And What Actually Works for Reliable Connectivity)
Fed up with flaky USB-C hubs? Discover why many fail, common pitfalls, and what features truly ensure reliable, multi-device connectivity.

Why Most People Don't Understand Their Computer Specs (And What Actually Matters)
Stop comparing GHz and GB. Learn what computer specs truly impact performance, value, and your daily experience. Marcus Thorne explains.
