Containers, Docker, and Kubernetes, how we package software now
Containers, Docker, and Kubernetes, how we package software now
Field note. Wings runs each game server in a Docker container. Containers exist because of forty years of slow movement toward this packaging model. Knowing the history demystifies the abstraction.
In 2013, a small company called dotCloud released a tool called Docker. Within two years, "container" had become one of the most-used words in software infrastructure. Within five years, Kubernetes had emerged as the dominant orchestration platform for containers.
The container revolution reshaped how software gets packaged, deployed, and operated. This article explains what containers are, why they exploded, and what they've enabled.
The packaging problem
The fundamental problem containers solve: getting an application to run consistently across different machines.
Before containers, a typical software deployment looked like:
- Developer writes code on their laptop. Has specific dependencies (Python 3.8, certain libraries, a database).
- Deploys to a test server. Different Python version. Some libraries different. Things break.
- Eventually fixes the test environment. Deploys to production. Production has different config. Things break again.
- Spends hours figuring out which dependency was wrong.
This was called "works on my machine" hell. The application worked in one environment but not another, because environments differed in subtle ways.
Solutions before containers:
- Detailed installation scripts. Bash scripts that set up environments. Brittle.
- Virtual machines. Full VMs with everything pre-installed. Heavy (gigabytes), slow to boot.
- Configuration management (Ansible, Puppet, Chef). Made setting up servers more reliable but didn't fully solve the consistency problem.
Containers proposed a different model: package the application AND its environment together, in a way that runs consistently on any container-capable host.
VMs vs containers
VIRTUAL MACHINES (heavy) CONTAINERS (light)
┌────────────────────────┐ ┌────────────────────────┐
│ App A │ │ App A App B App C│
├────────────────────────┤ ├────────────────────────┤
│ Guest OS (full) │ │ Container runtime │
├────────────────────────┤ ├────────────────────────┤
│ Hypervisor │ │ Host OS kernel │
├────────────────────────┤ ├────────────────────────┤
│ Host OS kernel │ │ Hardware │
├────────────────────────┤ └────────────────────────┘
│ Hardware │
└────────────────────────┘ Each container is a process
sharing the host kernel.
Each VM is a full OS, Megabytes, boots in ms.
gigabytes, boots in seconds. Hundreds per host common.
Tens per host typical.
What a container actually is
A container is a packaged process. It includes:
- The application binary.
- The libraries it needs.
- The configuration files.
- The smallest possible OS-like environment.
When you run a container, the host OS provides isolation: the container thinks it's running in its own environment. It can't see other processes; its filesystem is its own; its network is isolated.
Under the hood (on Linux), containers use:
- Namespaces for isolation (process, network, mount, user, etc.).
- Control groups (cgroups) for resource limits (CPU, memory).
- chroot-like filesystem isolation.
These features had existed in Linux for years before Docker (cgroups since 2008, namespaces longer). What was missing was a friendly user interface and a packaging format.
What Docker did
Docker (2013) provided three things that together changed everything:
A friendly packaging format. The Dockerfile: a simple text recipe for building a container image.
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app.py
CMD ["python3", "/app.py"]
This text says: start from Ubuntu 22.04, install Python 3, copy app.py in, and run it. Anyone with Docker can build this image, get the same result, and run the same container.
A registry for sharing. Docker Hub: a public site where images could be published and downloaded. Want to run PostgreSQL? docker pull postgres. The image is downloaded; you run it; PostgreSQL starts. No installation.
A simple command-line interface. docker build, docker run, docker push, docker pull. The commands were learnable in an afternoon.
The combination was magic. A developer could:
- Define their application in a Dockerfile.
- Build a container image.
- Push it to Docker Hub or a private registry.
- Pull it on any Docker-capable machine.
- Run it. It works.
The "works on my machine" problem mostly disappeared.
Why Docker exploded
Several factors:
Right timing. The cloud was big. Microservices were emerging. Both benefit from standardized packaging.
Developer-friendly. Docker felt good to use. Simple commands, clear concepts.
Network effect via Docker Hub. Once popular images were available (databases, languages, web servers), using Docker became easier than not using it.
Solves a real problem. "Works on my machine" had been a chronic pain for years. Docker actually solved it.
Linux had the primitives. cgroups, namespaces, overlay filesystems. Docker assembled them; Linux had built them.
Within two years (2013-2015), Docker was everywhere. New projects used it from day one. Old projects migrated.
The orchestration problem
Containers solved packaging. They created a new problem: how to manage many containers across many machines.
If you have 100 containers running on 20 servers, you need to think about:
- Which container goes on which server.
- What happens when a container crashes.
- How do containers find each other (service discovery).
- How do you roll out a new version.
- How do you scale up or down based on load.
Docker itself was good at "run a container on this machine." It wasn't designed to orchestrate fleets.
Several orchestration systems competed:
- Docker Swarm (Docker's own). Simple, never quite gained traction.
- Apache Mesos with Marathon. More complex, had some adoption.
- Kubernetes (Google open-source, 2014). Eventually won.
Kubernetes
In 2014, Google open-sourced Kubernetes (often abbreviated k8s). The name is Greek for "helmsman." It was based on Google's internal cluster-management system (Borg).
What Kubernetes provided:
- Declarative configuration. You describe the desired state (3 instances of this service running with 1GB RAM each); Kubernetes makes it happen.
- Self-healing. Containers that crash are restarted. Nodes that fail have their workloads moved.
- Service discovery. Containers find each other via DNS-like names within the cluster.
- Rolling updates. New versions deploy without downtime.
- Resource management. CPU and memory allocations enforced.
- Extensibility. Custom resources, operators, plugins.
Kubernetes was complex. The learning curve was steep. But it solved real problems and was backed by Google's experience.
By 2017, Kubernetes had won the container orchestration race. Mesos and Docker Swarm faded. By 2020, Kubernetes was the default infrastructure for most cloud-native applications.
The cloud-native era
A new term emerged: cloud-native. Applications designed from the start for cloud, containers, Kubernetes, microservices, declarative infrastructure.
Cloud-native patterns:
- Microservices. Many small services instead of one monolith.
- API-first. All communication via APIs.
- Containers. All deployment via containers.
- Orchestration. Managed by Kubernetes (or similar).
- Observability. Metrics (Prometheus), logging (centralized), tracing.
- CI/CD. Continuous integration and delivery.
- Infrastructure as code. Configurations in Git, applied via tools.
A whole ecosystem grew up. The Cloud Native Computing Foundation (CNCF), hosting Kubernetes and many other projects, became a major industry body.
What containers and Kubernetes enabled
Beyond the obvious:
Modern microservices. A single application becomes dozens of services, each in its own container. Each developed and deployed independently.
Faster deployments. Container builds and deployments are minutes, not hours. CI/CD pipelines are practical.
Cloud portability. A containerized application can run on any cloud (or on-prem, or your laptop). Lock-in reduced.
Better resource utilization. Containers are lightweight. Pack more services on the same hardware.
Easier developer onboarding. New developer? Pull the containers; run docker-compose. Within minutes you have a working dev environment.
Reproducible builds. Same Dockerfile, same image, anywhere.
What it complicated
The container era also added complexity:
Steep learning curve. Kubernetes especially. Hundreds of concepts (Pods, Deployments, Services, Ingress, ConfigMaps, Secrets, NetworkPolicies, etc.).
More infrastructure to manage. Container registries, orchestrators, observability stacks, networking layers. Each is a system.
New failure modes. Networking between containers fails in new ways. Persistent storage in containers is harder than in VMs.
Security. Containers share kernel with host. Misconfigurations can leak. Image supply chain is a security concern.
Cost. Running Kubernetes well requires expertise. Many small projects don't need this complexity.
The trade-off: containers solve real problems, but they're not free. Adopt thoughtfully.
What's running on containers in 2026
Most modern infrastructure:
- Most cloud-deployed applications. AWS ECS, EKS, Fargate; Azure AKS; GCP GKE; smaller cloud providers' equivalents.
- Most CI/CD pipelines. GitHub Actions, GitLab CI, Jenkins workers, etc.
- Many data systems. Spark, Flink, Airflow often deployed via containers.
- Many AI / ML workloads. Especially serving (inference). Training sometimes uses bare metal for performance.
- Many development environments. Devcontainers, GitHub Codespaces, gitpod.
Bare-metal or VM-based deployments still exist, especially for:
- Stateful workloads with extreme performance requirements (databases, sometimes).
- Specialized hardware needs.
- Legacy systems.
- Cost-sensitive workloads where Kubernetes overhead matters.
But for new applications, container-based is the default.
Containers in game hosting
For game hosting:
Game servers themselves are sometimes containerized, sometimes not. Modern game-hosting platforms like Pterodactyl (which this very documentation series is hosted under) use containers (Docker) to isolate each game server. The container provides resource isolation and consistency.
Backend services for games (auth, matchmaking, persistence) are typically containerized and orchestrated.
Patch delivery, asset distribution uses containerized services behind CDNs.
The pattern: game-server applications themselves don't always need containerization for performance reasons, but the hosting platform benefits enormously from container isolation.
The next thing
Containers and Kubernetes won. The interesting question is what comes after.
A few candidates:
Serverless. AWS Lambda, Google Cloud Functions, etc. Hide the orchestration entirely; run functions on demand. Smaller adoption than expected, but real for some workloads.
WebAssembly (Wasm). A portable bytecode format. Some see it as "containers, but smaller and faster." Adoption growing for specific use cases.
Simpler abstractions on top of Kubernetes. Kubernetes is powerful but complex. Many platforms (Heroku-style PaaS, modern serverless) try to simplify the user experience.
Edge computing. Running compute closer to users. Containers and Wasm both relevant.
The container era isn't ending. It's settling. New abstractions build on top.
Conclusion
Containers, popularized by Docker in 2013 and orchestrated by Kubernetes since 2014, transformed how software is packaged and deployed. They solved real problems (consistency, packaging, portability) and created new ones (complexity, security surface area).
For developers in 2026, "I'll containerize this" is a reflex action for new projects. For infrastructure engineers, Kubernetes is the default platform. For game hosting, container-based isolation underpins major platforms like Pterodactyl.
The container revolution didn't change everything (databases still mostly use traditional deployment; truly performance-critical workloads sometimes bypass containers). It did change most things. Modern infrastructure has containers under almost every hood.
Coming up
Final article in Series D: Git, the version-control system Linus wrote in two weeks. The tool that powers almost all software development in 2026.
Hosting your game server with AndroHost means we handle most of what's in this post for you automatically: tier sizing, SRV records, off-site backups, DDoS protection.
Keep reading
Today's centralization, when did the open internet become Cloudflare, AWS, and Google?
The early internet was a decentralized vision. Many small networks, many independent operators, no central authority. The end-to-end principle. Permissionless innovation.
IPv4 runs out, the 30-year scarcity timeline
The IPv4 address space has 4.3 billion addresses. By the 1990s, anyone paying attention could see they would run out. Anyone could predict roughly when. The transition to IPv6 was supposed to solve it. The transition started in 1996 and...
The cloud era, AWS, S3, and the death of the server room
In 2005, if you wanted to run a website at any scale, you needed servers. Physical machines, in a room you rented or owned, that you bought, configured, maintained, replaced. Setting up a startup's infrastructure took months and tens of...