Containers
How modern deployments got reliable. The "works on my machine" problem, solved.
The problem they solve
Before containers, you wrote your app on your laptop. You deployed it to a server. The server had a slightly different version of Python, a missing library, a different OS. The app crashed.
"It works on my machine." The eternal lament.
Every server was a snowflake. Each one was slightly customized, set up by hand or by half-finished scripts. Drift built up over time. Reproducing one machine on another took hours.
A container packages your app together with everything it depends on. The runtime (Python 3.10, Node 18). Libraries. OS-level files. Configuration. Ship the container. Anywhere that container runs, your app runs the same way.
How containers work, briefly
A container is just a process running on a host operating system, isolated by Linux kernel features called cgroups and namespaces. It cannot see other processes or files. It has its own filesystem, its own network, and its own process tree. All inside one host.
A virtual machine boots a full guest operating system, which takes minutes and gigabytes. A container starts in milliseconds and adds almost no overhead. Containers share the host kernel. The isolation is just a wrapper.
The container image is a layered filesystem snapshot. Your app plus all its dependencies plus a small Linux userspace. Images are immutable. You build once and run anywhere.
Docker made this idea easy to use in 2013. The standard format is now called OCI. Kubernetes runs containers. AWS ECS, Cloud Run, and Fargate all run containers underneath.
What containers change for deployment
Container deployments look like this.
1. Build. Developers write a Dockerfile that describes the image. CI builds the image with docker build -t myapp:v123 .
2. Push. The image is uploaded to a registry (Docker Hub, ECR, or GCR).
3. Deploy. The production servers pull the new image and start containers from it.
What you get out of it.
The same Dockerfile gives you the same image, bit for bit, every time.
Atomic rollouts. You switch from v122 to v123 by pulling and restarting.
Fast rollbacks. You switch back to v122 in seconds.
Horizontal scale. You spin up more containers from the same image.
Without containers, every deployment was a fragile script that hoped the target server matched what it expected. With containers, deployments are predictable and you can roll them back.
Kubernetes. Orchestrating containers
Once you have many containers across many hosts, you need an orchestrator. Something that decides which container runs on which host, restarts crashed ones, scales them up and down, and connects them on the network.
Kubernetes (often called K8s) is the most common orchestrator. It schedules containers across a cluster of hosts. It replaces failed ones automatically. It handles rolling deploys. It exposes services to traffic.
Kubernetes added a lot of complexity. Many teams realized they did not need all of it. They use simpler platforms like AWS Fargate, Cloud Run, Fly.io, Railway, or Render. Those run your container without exposing the orchestration layer.
For most teams, start with the simpler platform. Move to Kubernetes only when you have a real reason. Things like multi-cloud, custom routing, very high scale, or a team that already knows it well.
But the basic idea, that your app is a container, is universal now. That part is not optional anymore.