How to Migrate Docker Compose to Kubernetes: A Step-by-Step Guide

You are currently viewing How to Migrate Docker Compose to Kubernetes: A Step-by-Step Guide

How to Migrate Docker Compose to Kubernetes: A Step-by-Step Guide

Image by: Victor Moragriega

Imagine you are running a successful application on a single, robust server using Docker Compose. Everything is working perfectly: your web server, your database, and your cache are all running in neat containers, linked by a simple bridge network. But then, traffic spikes. Your CPU hits 95%, and your single server’s RAM is exhausted. You reach for the “scale” button, only to realize that Docker Compose is fundamentally bound to that single host. This is the moment many system administrators face: the realization that they have outgrown single-host orchestration and must transition to Kubernetes scalability to survive. This tutorial is designed specifically for professionals making that leap, providing a practical roadmap for translating configurations, managing stateful data, and exposing services to the world through ingress.

The leap from Docker Compose to Kubernetes scalability

For years, Docker Compose has been the gold standard for local development and small-scale deployments. It is intuitive, uses simple YAML syntax, and allows you to stand up an entire stack with a single command. However, as your infrastructure needs grow, the limitations of a single-host approach become glaringly obvious. In a Docker Compose environment, if the host machine fails, your entire application goes offline. There is no native self-healing, no automated rescheduling of containers to healthy nodes, and no seamless way to scale horizontally across multiple machines.

Kubernetes (K8s) solves these problems by introducing an orchestration layer that abstracts the underlying hardware. Instead of thinking about “servers,” you begin thinking about “clusters.” In a Kubernetes cluster, your application is spread across a pool of resources. If a node dies, Kubernetes detects the failure and restarts your containers on a different, healthy node. This transition represents a paradigm shift in how system administrators manage uptime and reliability. While Docker Compose manages containers on a machine, Kubernetes manages the desired state of your entire distributed system.

To understand this transition, it is helpful to compare the fundamental philosophies of both tools. Docker Compose is “declarative for a single instance,” whereas Kubernetes is “declarative for a distributed cluster.” When you move to Kubernetes, you aren’t just moving your containers; you are moving into a world of controllers, schedulers, and control planes. This requires a new mental model where the system is constantly working to reconcile the “current state” with your “desired state.”

Translating Docker Compose to Kubernetes manifests

The most immediate task for a sysadmin transitioning to Kubernetes is the translation of existing Docker Compose files into Kubernetes manifests. In Compose, you define services, networks, and volumes. In Kubernetes, these concepts are split into several distinct object types: Pods, Deployments, Services, and ConfigMaps/Secrets.

Let’s look at how a basic web service translates. In Compose, you might have a service definition that specifies an image, ports, and environment variables. In Kubernetes, the Deployment object handles the lifecycle of your pods, ensuring the specified number of replicas are always running. The Service object provides a stable IP address and DNS name to access those pods, acting as an internal load balancer.

Here is a comparative data table to help you visualize the mapping between these two technologies:

Docker Compose Concept Kubernetes Equivalent Primary Purpose
Service (e.g., web) Deployment / ReplicaSet Manages the lifecycle and scaling of pods.
Port Mapping (e.g., 80:80) Service (Type: ClusterIP/NodePort) Provides network access to a group of pods.
Environment Variables ConfigMap / Secret Decouples configuration and sensitive data from the image.
Volumes (Local) PersistentVolume / PVC Manages lifecycle and mounting of persistent storage.
Networks NetworkPolicy / Service Controls communication between workloads.

A critical takeaway for administrators is that Kubernetes is much more verbose. A single docker-compose.yml file might be replaced by five or six separate YAML files. While this seems like more work, it provides much granular control. For instance, instead of a global environment variable, you can use Secrets to inject sensitive credentials into specific pods, ensuring that even if one container is compromised, the scope of the breach is limited. If you are looking to modernize your deployment pipelines, understanding these mappings is essential for automating your CI/CD workflows. To learn more about enterprise-grade infrastructure, you might find advanced deployment strategies useful in your journey.

Mastering persistent storage in a distributed environment

In a single-host Docker setup, volume management is simple: you point a container to a directory on the host machine’s hard drive. In a Kubernetes cluster, this approach breaks immediately. If a pod moves from Node A to Node B, it cannot access the files sitting on Node A’s local disk. This is the single biggest hurdle for administrators moving to Kubernetes scalability.

To solve this, Kubernetes uses a decoupled storage architecture involving three main components:

  • PersistentVolume (PV): A piece of storage in the cluster, provisioned by an administrator or a storage class. It is a resource in the cluster that has been provisioned by an administrator or provisioned dynamically through a StorageClass.
  • PersistentVolumeClaim (PVC): A request for storage by a user. It is similar to a Pod. A Pod consumes storage by referencing a PVC.
  • StorageClass: This allows for dynamic provisioning. Instead of an admin manually creating PVs, the StorageClass can talk to cloud providers (AWS EBS, Google Persistent Disk, Azure Disk) to create storage on-demand when a PVC is created.

When managing databases like PostgreSQL or MySQL in Kubernetes, you should almost always use StatefulSets rather than Deployments. While Deployments are perfect for stateless applications (like web servers), StatefulSets provide guarantees about the identity of the pods. Each pod in a StatefulSet gets a unique, persistent identifier that stays the same even if the pod is rescheduled. This is vital for distributed databases that need to maintain a consistent relationship with their storage volumes to ensure data integrity and proper clustering.

“Storage in Kubernetes is not just about mounting a disk; it is about managing the lifecycle of data independently of the lifecycle of the container.” – Industry Best Practice

When configuring your storage, always ensure you are using a robust StorageClass that supports ReadWriteOnce (RWO) for databases and ReadWriteMany (RWX) for shared file systems like NFS. Failure to understand these access modes can lead to “stuck” pods that cannot attach to their volumes because the previous node hasn’t released them yet.

Managing external traffic with ingress controllers

In the Docker Compose world, you likely mapped a host port directly to a container port (e.g., `-p 80:80`). In Kubernetes, this is considered bad practice for production environments. Instead, Kubernetes uses Services and Ingress Controllers to handle external traffic. While a “Service” can expose an application via a LoadBalancer type, this is often expensive and inefficient if you have dozens of microservices, as each one would require its own cloud load balancer.

This is where the Ingress Controller becomes the hero of the cluster. An Ingress Controller is a specialized pod that manages external access to the services in a cluster, typically providing HTTP/HTTPS routing, load balancing, and TLS termination. Popular choices include NGINX Ingress Controller and Traefik. Instead of creating a new