What Are Containers?
Containers package your app with everything it needs to run—same everywhere. Like shipping containers for software: standardized, portable, isolated.
The problem containers solve
“Works on my machine”
You build an app on your laptop. It works perfectly. You deploy it to a server. It crashes.
Why? Different versions:
- Your laptop has Python 3.11, the server has 3.9
- You have library version 2.3.1, the server has 2.1.0
- Environment variables are different
- Operating system behaves differently
This happens constantly. Developers spend hours debugging environment differences instead of building features.
The old solutions
Virtual machines: Emulate an entire computer. Works, but heavy—each VM runs a full operating system. Starting a VM takes minutes. Running ten VMs needs significant resources.
Configuration management (Ansible, Chef): Automate server setup. Better, but configuration drifts over time. Servers become “snowflakes”—unique, fragile, hard to reproduce.
Documentation: “Make sure you have Python 3.11 and run pip install -r requirements.txt.” Doesn’t scale. People miss steps. Versions drift.
What a container actually is
A container is a lightweight, isolated environment that includes:
- Your application code
- The runtime (Python, Node, Java, etc.)
- All libraries and dependencies
- Configuration files
- Environment settings
It does NOT include:
- A full operating system
- A hypervisor
- Hardware emulation
Containers share the host’s operating system kernel but are isolated from each other and from the host.
┌─────────────────────────────────────────────────┐
│ Host Machine │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│
│ │ Container A │ │ Container B │ │ Container C ││
│ │ Python app │ │ Node.js app │ │ Go app ││
│ │ + deps │ │ + deps │ │ + deps ││
│ └─────────────┘ └─────────────┘ └─────────────┘│
│ ┌─────────────────────────────────────────────┐│
│ │ Container Runtime (Docker) ││
│ └─────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────┐│
│ │ Host Operating System ││
│ └─────────────────────────────────────────────┘│
└─────────────────────────────────────────────────┘Containers vs Virtual Machines
| Aspect | Container | Virtual Machine |
|---|---|---|
| Size | MBs (10-500 MB typical) | GBs (full OS image) |
| Startup | Seconds | Minutes |
| Isolation | Process-level | Hardware-level |
| Overhead | Minimal | Significant |
| Density | Run dozens per host | Run a few per host |
| OS | Shares host kernel | Full guest OS |
When to use VMs: When you need complete isolation (security requirements), different operating systems (Windows on Linux host), or legacy applications that don’t containerize well.
When to use containers: Most modern applications, microservices, CI/CD pipelines, development environments.
Docker: the container tool
Docker is the software that makes containers practical. Before Docker (2013), container technology existed in Linux but was complex. Docker made it accessible.
Key concepts
Image: A read-only template with everything needed to run your app. Like a recipe. You build it once.
Container: A running instance of an image. Like a dish made from the recipe. You can run many containers from one image.
Dockerfile: Instructions for building an image. A text file listing the steps.
Registry: Where images are stored. Docker Hub is the public default. Companies use private registries (Amazon ECR, GitHub Container Registry).
A simple Dockerfile
# Start from a base image with Python
FROM python:3.11-slim
# Set the working directory
WORKDIR /app
# Copy requirements and install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy your application code
COPY . .
# Tell Docker what command to run
CMD ["python", "app.py"]Building and running
# Build the image (creates a snapshot)
docker build -t my-app .
# Run a container from the image
docker run -p 8080:8080 my-app
# Your app is now running, accessible on port 8080Why containers matter
Consistency
The same container runs identically everywhere:
- Developer laptop (Mac, Windows, Linux)
- CI/CD pipeline
- Staging environment
- Production servers
- Different cloud providers
No more “works on my machine.”
Portability
Containers work on:
- Any cloud (AWS, GCP, Azure)
- Any operating system (with Docker installed)
- Any orchestration system (Kubernetes, ECS, etc.)
You’re not locked into one platform.
Isolation
Each container is isolated:
- Different apps can use different versions of dependencies
- One container crashing doesn’t affect others
- Security boundaries between applications
Efficiency
Containers are lightweight:
- Share the host kernel (no OS overhead)
- Start in seconds
- Run many containers on one machine
- Efficient resource usage
Reproducibility
A container image is immutable. Version 1.0.0 always contains exactly the same code and dependencies. You can:
- Roll back to any previous version
- Run the exact same code that ran six months ago
- Debug production issues with the exact production environment
How containers work (simplified)
Linux has features that enable containers:
Namespaces: Isolate what a container can see. Each container has its own view of processes, network, users, and filesystem.
Cgroups: Limit what a container can use. Set memory limits, CPU limits, I/O limits.
Union filesystems: Layer images efficiently. Changes are stored as layers; unchanged parts are shared.
Docker (and other container tools) combine these features into a usable interface. You don’t need to understand the Linux internals—Docker handles it.
Common container patterns
Single-process containers
One container runs one process. Your web server is one container. Your database is another. They communicate over a network.
┌─────────────┐ ┌─────────────┐
│ Web App │────▶│ Database │
│ Container │ │ Container │
└─────────────┘ └─────────────┘Sidecar pattern
A helper container runs alongside your main container, handling logging, proxying, or monitoring.
Init containers
Containers that run before your main container, setting up prerequisites.
When you don’t need to think about containers
Many modern platforms containerize for you:
- Vercel: Automatically builds and deploys your Next.js/React app
- Railway: Auto-detects your language and creates containers
- Render: Build from Git, containers handled
- Heroku: Buildpacks create containers automatically
These platforms use containers internally, but you don’t write Dockerfiles.
When you do need containers
- Custom runtimes: Need specific versions or configurations
- Multiple services: Running several interconnected applications
- Self-hosting: Running on your own servers or Kubernetes
- CI/CD: Building and testing in controlled environments
- Team alignment: Everyone needs the same development environment
- Production debugging: Reproducing exact production conditions
Getting started
Install Docker
- Mac: Docker Desktop
- Windows: Docker Desktop (requires WSL2)
- Linux: Docker Engine
Run your first container
# Run nginx web server
docker run -p 8080:80 nginx
# Visit http://localhost:8080 - you're running a web server in a containerKey commands
docker build -t name . # Build an image from Dockerfile
docker run image # Run a container from an image
docker ps # List running containers
docker stop container_id # Stop a container
docker images # List images
docker pull image # Download an image from registryFurther reading
- What is Kubernetes? : Orchestrating many containers
- What is cloud-native? : The ecosystem containers enable
- What is DevOps and CI/CD? : Automating container workflows
- What is a server? : Where containers run
- What is hosting? : Deployment options including containers
Frequently asked questions