18.3.2 Compose Kubernetes Migration
A focused guide to Compose Kubernetes Migration, connecting core concepts with practical Docker and container operations.
Compose Kubernetes migration covers the practical path from a Compose-based deployment to genuine Kubernetes manifests, including the automated conversion tooling available as a starting point, the manual concept mapping needed where automation falls short, and the points where Compose's simpler model breaks down and genuinely requires a redesigned approach rather than a direct, mechanical translation.
Automated conversion as a starting point, not a finished result
The kompose tool converts a Compose file directly into a set of equivalent Kubernetes manifests, which is useful for quickly seeing a rough, initial translation, but the output should be treated as a draft requiring substantial review rather than a production-ready result:
kompose convert -f docker-compose.yml
Generated: deployment.yaml, service.yaml, persistentvolumeclaim.yaml
The generated manifests typically lack resource requests and limits, meaningful liveness and readiness probes, and any provider-specific configuration that a genuine production deployment would need, all of which require manual addition after the automated conversion provides its initial, rough starting structure.
Mapping Compose concepts to Kubernetes resources
Several Compose concepts have a reasonably direct, if not perfectly automatic, Kubernetes equivalent worth understanding explicitly when reviewing or manually completing a migration:
services:
api:
image: my-api
ports:
- "3000:3000"
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- image: my-api
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
spec:
ports:
- port: 3000
A single Compose service typically maps to a pair of Kubernetes resources, a Deployment governing the actual running pods and a Service providing stable network access to them, a one-to-two mapping that automated conversion tools generally handle reasonably well.
Volumes mapping to persistent volume claims
A Compose named volume maps conceptually to a Kubernetes PersistentVolumeClaim, though the actual underlying storage provisioning behavior, and the available storage class options, depend entirely on the target cluster's specific infrastructure, which is one of the portability gaps covered in dedicated content elsewhere:
volumes:
pgdata:
apiVersion: v1
kind: PersistentVolumeClaim
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
This mapping requires deciding explicitly on a storage size and class, neither of which Compose's simpler volume model requires specifying at all, making this one of the more involved manual completion steps after an automated conversion.
depends_on requires a genuinely different mechanism
Compose's depends_on health condition has no direct equivalent in Kubernetes; achieving similar startup ordering requires either an init container that waits for a dependency to become reachable, or relying on the application's own retry logic combined with Kubernetes's readiness probe mechanism to keep a pod out of service rotation until it is genuinely ready:
services:
api:
depends_on:
db:
condition: service_healthy
initContainers:
- name: wait-for-db
image: busybox
command: ["sh", "-c", "until nc -z db 5432; do sleep 1; done"]
This is a genuine redesign point rather than a mechanical translation, since Kubernetes's architecture deliberately does not provide a direct, built-in equivalent to Compose's health-aware dependency ordering, expecting applications and their startup logic to handle this resilience themselves instead.
Networks mapping to namespaces and network policies
Compose's network-based service isolation maps conceptually to Kubernetes namespaces combined with NetworkPolicy resources, but the translation is not one-to-one, since Kubernetes's default behavior, unlike Compose's network-based isolation, allows all pods within a cluster to reach each other unless a NetworkPolicy explicitly restricts that traffic:
services:
api:
networks:
- internal
db:
networks:
- internal
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
podSelector:
matchLabels:
app: db
ingress:
- from:
- podSelector:
matchLabels:
app: api
This requires deliberately authoring a NetworkPolicy for any genuine isolation requirement, since simply moving services into the same namespace does not by itself recreate the network-level boundaries a Compose network definition implicitly provided.
Deciding when migration is actually warranted
Migrating to Kubernetes is justified specifically by a genuine need for capabilities Compose and Swarm do not provide, sophisticated autoscaling, a mature ecosystem of operators and extensions, multi-cluster federation, rather than undertaken simply because Kubernetes is the more widely discussed, popular choice; a team without a genuine, demonstrated need for these specific capabilities often finds the migration investment considerably exceeds the actual operational benefit gained.
docker stack deploy -c docker-compose.yml my-stack
Continuing to operate successfully on Compose or Swarm, when their capabilities genuinely match the actual current and near-future needs, is a legitimate, deliberate choice rather than something to abandon purely due to Kubernetes's greater general popularity and ecosystem size.
Common mistakes
- Treating
kompose's automated output as production-ready rather than a rough starting draft requiring substantial manual review and completion. - Not adding resource requests, limits, and meaningful health probes during the manual completion step, carrying over an under-specified configuration directly from the automated conversion.
- Assuming Compose's
depends_onhealth condition has a direct, automatic Kubernetes equivalent, rather than recognizing this as a genuine redesign point requiring init containers or application-level retry logic. - Assuming moving services into the same Kubernetes namespace recreates Compose's network-based isolation automatically, without authoring explicit
NetworkPolicyresources for any genuine isolation requirement. - Migrating to Kubernetes without a genuine, demonstrated need for its specific additional capabilities, rather than because it is the more widely known, popular choice.
Compose Kubernetes migration benefits from automated conversion tooling as a useful starting point, but the actual, complete translation requires deliberate manual work specifically around storage provisioning, startup ordering redesign, and explicit network policy authoring, none of which a mechanical, one-to-one mapping fully resolves, and the decision to migrate at all should rest on a genuine, demonstrated need for Kubernetes's specific additional capabilities rather than general popularity alone.