---
# === IDENTITY ===
id: software/migrations/docker-compose-to-kubernetes/2026
canonical_question: "How do I migrate from Docker Compose to Kubernetes?"
aliases:
  - "Docker Compose to Kubernetes migration"
  - "convert docker-compose.yml to Kubernetes"
  - "kompose convert docker compose"
  - "docker compose to k8s"
  - "migrate compose to Helm chart"
  - "Docker Compose to Kubernetes manifests"
  - "docker-compose to deployment yaml"
  - "docker compose to k8s with Kustomize"
entity_type: software_reference
domain: software > migrations > docker_compose_to_kubernetes
region: global
jurisdiction: global
temporal_scope: 2020-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.93
version: 2.1
first_published: 2026-02-17

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: "Ingress-NGINX retired 2026-03-24 (no further releases, bugfixes, or security patches); Kubernetes 1.36 released 2026-04-22 with User Namespaces GA and gitRepo volume permanently removed; Kubernetes 1.33 graduated sidecar containers to stable (2025-04); Kubernetes 1.25 removed PodSecurityPolicy (2022-08)"
  next_review: 2026-11-13
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Kompose skips `build:` directives — all images must be pre-built and pushed to a container registry before conversion"
  - "Kubernetes 1.25+ removed PodSecurityPolicy — use Pod Security Admission (PSA) labels on namespaces instead"
  - "Never deploy without resource requests/limits — a single pod without limits can consume all node resources and crash the cluster"
  - "Kubernetes Secrets are base64-encoded, not encrypted — enable encryption at rest or use an external secret manager (Vault, AWS Secrets Manager, Sealed Secrets)"
  - "Ingress-NGINX retired 2026-03-24 — no further releases, bugfixes, or security patches; migrate to Gateway API or an alternative controller (F5 NGINX Ingress, HAProxy, Traefik) for new and existing deployments"
  - "Gateway API (v1.4+) is the recommended replacement for legacy Ingress — Ingress API still works but Ingress-NGINX is EOL"
  - "Docker Compose `depends_on` has no Kubernetes equivalent — implement init containers, readiness probes, or application-level retry logic"
  - "gitRepo volume plugin was permanently removed in Kubernetes 1.36 (2026-04) — use initContainers cloning into emptyDir or a sidecar instead"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User only needs Docker Compose for local development and has no production K8s requirements"
    use_instead: "Docker Compose is sufficient — Kubernetes adds unnecessary operational complexity for local-only workflows"
  - condition: "User wants to migrate to a PaaS (Railway, Fly.io, Render) instead of Kubernetes"
    use_instead: "Evaluate PaaS options directly — they accept Docker images without K8s manifest complexity"
  - condition: "User is migrating from Docker Swarm (not Compose) to Kubernetes"
    use_instead: "software/migrations/docker-swarm-to-kubernetes/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: service_count
    question: "How many services are in your docker-compose.yml?"
    type: choice
    options: ["1-3 services", "4-10 services", "10+ services"]
  - key: target_environment
    question: "Where will Kubernetes run?"
    type: choice
    options: ["Local (Minikube/kind)", "Cloud managed (EKS/GKE/AKS)", "Self-hosted (kubeadm/k3s)"]
  - key: packaging_preference
    question: "How do you want to manage K8s manifests across environments?"
    type: choice
    options: ["Plain YAML manifests", "Helm charts", "Kustomize overlays", "Not sure"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/migrations/docker-compose-to-kubernetes/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-05-17)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/migrations/heroku-to-aws/2026"
      label: "Heroku to AWS Migration"
    - id: "software/migrations/monolith-to-microservices/2026"
      label: "Monolith to Microservices Migration"
  solves:
    - id: "software/migrations/jenkins-to-github-actions/2026"
      label: "Jenkins to GitHub Actions (CI/CD for K8s deployments)"
  alternative_to:
    - id: "software/migrations/docker-swarm-to-kubernetes/2026"
      label: "Docker Swarm to Kubernetes Migration"
  often_confused_with:
    - id: "software/migrations/docker-swarm-to-kubernetes/2026"
      label: "Docker Swarm to Kubernetes (Swarm orchestration, not Compose file conversion)"

# === SOURCES (9 authoritative sources) ===
sources:
  - id: src1
    title: "Translate a Docker Compose File to Kubernetes Resources"
    author: Kubernetes.io
    url: https://kubernetes.io/docs/tasks/configure-pod-container/translate-compose-kubernetes/
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src2
    title: "Kompose — Go from Docker Compose to Kubernetes"
    author: Kompose Project
    url: https://kompose.io/
    type: official_docs
    published: 2026-01-17
    reliability: authoritative
  - id: src3
    title: "Docker Compose to Kubernetes: Step-by-Step Migration"
    author: vCluster / Loft Labs
    url: https://www.vcluster.com/blog/docker-compose-to-kubernetes-step-by-step-migration
    type: technical_blog
    published: 2025-09-15
    reliability: high
  - id: src4
    title: "Helm: The Package Manager for Kubernetes"
    author: Helm
    url: https://helm.sh/docs/
    type: official_docs
    published: 2025-10-01
    reliability: authoritative
  - id: src5
    title: "Migrating from Docker Compose to Kubernetes — Konveyor Move2Kube"
    author: Konveyor / CNCF
    url: https://move2kube.konveyor.io/tutorials/migrating-from-docker-compose-to-kubernetes
    type: official_docs
    published: 2025-03-06
    reliability: high
  - id: src6
    title: "Application Migration from Docker Compose to Kubernetes"
    author: Loovatech / Medium
    url: https://medium.com/@loovatech/application-migration-from-docker-compose-to-kubernetes-da683c2f6ffe
    type: technical_blog
    published: 2025-07-10
    reliability: high
  - id: src7
    title: "Kubernetes Deployments, Services, and Ingress"
    author: Kubernetes.io
    url: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
    type: official_docs
    published: 2025-10-01
    reliability: authoritative
  - id: src8
    title: "Gateway API v1.4: New Features"
    author: Kubernetes.io
    url: https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/
    type: official_docs
    published: 2025-11-06
    reliability: authoritative
  - id: src9
    title: "Ingress NGINX Retirement: What You Need to Know"
    author: Kubernetes.io
    url: https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/
    type: official_docs
    published: 2025-11-11
    reliability: authoritative
---

# How to Migrate from Docker Compose to Kubernetes

## TL;DR

- **Bottom line**: Use `kompose convert` (v1.38, Jan 2026) to auto-generate Kubernetes manifests from `docker-compose.yml`, then refine with health checks, resource limits, secrets, and NetworkPolicies — expect 70-80% auto-conversion with manual tuning for production readiness.
- **Key tool/command**: `kompose convert -f docker-compose.yml` generates Deployments, Services, and PVCs from your compose file.
- **Watch out for**: Kompose skips `build:` directives — all images must be pre-built and pushed to a registry before conversion. No readiness probes or resource limits are generated. **Ingress-NGINX retired 2026-03-24** — for new clusters use Gateway API or an alternative controller (F5 NGINX, HAProxy, Traefik). [src9]
- **Works with**: Docker Compose v2/v3, Kubernetes 1.25–1.36, Kompose 1.34–1.38, Helm 3.x, Kustomize (built into kubectl), Move2Kube 0.3.15+.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Kompose skips `build:` directives — all images must be pre-built and pushed to a container registry before conversion [src1, src2]
- Kubernetes 1.25+ removed PodSecurityPolicy — use Pod Security Admission (PSA) namespace labels instead [src7]
- Never deploy without `resources.requests` and `resources.limits` — a single pod without limits can consume all node resources [src3]
- Kubernetes Secrets are base64-encoded, not encrypted at rest by default — enable encryption at rest or use an external secret manager (Vault, Sealed Secrets, AWS Secrets Manager) [src3]
- **Ingress-NGINX retired 2026-03-24** — no further releases, bugfixes, or security patches. Migrate to Gateway API or an alternative (F5 NGINX Ingress Controller, HAProxy, Traefik) [src9]
- Gateway API (v1.4+) is the recommended replacement for legacy Ingress on new deployments [src8]
- gitRepo volume plugin was permanently removed in Kubernetes 1.36 (2026-04-22) — use initContainers cloning into emptyDir instead [src7]
- Docker Compose `depends_on` has no Kubernetes equivalent — use init containers, readiness probes, or application-level connection retry logic [src6]

## Quick Reference

| Docker Compose | Kubernetes Equivalent | Notes |
|---|---|---|
| `services:` | `Deployment` + `Service` | Each compose service becomes one Deployment + one Service [src1] |
| `image:` | `containers[].image` | Direct mapping in pod spec [src1] |
| `build:` | (skipped by Kompose) | Pre-build and push to registry (Docker Hub, ECR, GCR, GHCR) [src6] |
| `ports: "8080:80"` | `Service` (type: ClusterIP or LoadBalancer) | ClusterIP for internal; LoadBalancer or Gateway API HTTPRoute for external [src1, src8] |
| `volumes:` (named) | `PersistentVolumeClaim` | Kompose creates PVCs automatically [src1, src2] |
| `volumes:` (bind mount) | `ConfigMap` or `hostPath` | ConfigMap for config files; avoid hostPath in production [src6] |
| `environment:` | `env:` in container spec or `ConfigMap` | Sensitive values must use Kubernetes `Secret` [src3] |
| `env_file:` | `ConfigMap` from file | `kubectl create configmap --from-env-file=.env` [src1] |
| `depends_on:` | (no direct equivalent) | Use init containers or readiness probes instead [src6] |
| `restart: always` | `restartPolicy: Always` | Default in Deployments [src7] |
| `networks:` | Kubernetes networking (flat) + NetworkPolicy | All pods can reach each other by service name; use NetworkPolicy to restrict [src7] |
| `deploy.replicas:` | `spec.replicas:` | Direct mapping in Deployment; add HPA for auto-scaling [src2] |
| `deploy.resources:` | `resources.requests/limits` | Set CPU and memory requests/limits [src3] |
| `healthcheck:` | `livenessProbe` / `readinessProbe` / `startupProbe` | Kompose may not convert these — add manually [src6] |
| `secrets:` | `Secret` | `kubectl create secret generic` or use Sealed Secrets for GitOps [src3] |

## Decision Tree

```
START
├── Is this for local development only?
│   ├── YES → Keep Docker Compose (K8s is overkill for local dev) [src3]
│   └── NO → Continue to Kubernetes ↓
├── How many services?
│   ├── 1–3 services → Start with Kompose convert [src1, src2]
│   ├── 4–10 services → Kompose + Helm charts or Kustomize for templating [src4]
│   └── 10+ services → Full Helm chart architecture with subcharts, or Move2Kube [src4, src5]
├── Do you need auto-scaling?
│   ├── YES → Kubernetes HPA (HorizontalPodAutoscaler) [src7]
│   └── NO → Fixed replicas in Deployment spec
├── Where will K8s run?
│   ├── Local testing → Minikube, kind, or k3d [src1]
│   ├── Cloud managed → EKS, GKE, or AKS [src3]
│   └── Self-hosted → kubeadm or k3s
├── Do you need environment-specific configs?
│   ├── YES (simple overlays) → Kustomize [src3]
│   ├── YES (complex templating) → Helm values.yaml per environment [src4]
│   └── NO → Plain Kubernetes manifests
├── Need advanced conversion with Dockerfiles + source analysis?
│   ├── YES → Move2Kube (plan → transform pipeline) [src5]
│   └── NO → Kompose is sufficient
└── DEFAULT → kompose convert → refine → Helm chart or Kustomize → deploy
```

## Step-by-Step Guide

### 1. Install Kompose and convert your compose file

```bash
# Install Kompose (v1.38 as of Feb 2026)
# macOS:
brew install kompose
# Linux:
curl -L https://github.com/kubernetes/kompose/releases/latest/download/kompose-linux-amd64 -o kompose
chmod +x kompose && sudo mv kompose /usr/local/bin/

# Convert docker-compose.yml to Kubernetes manifests
kompose convert -f docker-compose.yml
# Output: deployment.yaml, service.yaml, pvc.yaml per service

# Alternative: convert directly to Helm chart
kompose convert -f docker-compose.yml --chart
```

**Verify**: Check generated files: `ls *.yaml` — one Deployment + Service per compose service. [src1, src2]

### 2. Push images to a container registry

Kompose skips `build:` directives. All images must be in a registry. [src6]

```bash
# Build and push each service image
docker build -t myregistry/web:1.0 ./web
docker push myregistry/web:1.0

docker build -t myregistry/api:1.0 ./api
docker push myregistry/api:1.0

# Update generated YAML image references
sed -i 's|web:latest|myregistry/web:1.0|g' web-deployment.yaml
sed -i 's|api:latest|myregistry/api:1.0|g' api-deployment.yaml
```

### 3. Add health checks and resource limits

Kompose output lacks production essentials. Add these manually. [src3, src6]

```yaml
# Add to each container spec in deployment.yaml
containers:
  - name: web
    image: myregistry/web:1.0
    ports:
      - containerPort: 8080
    # Startup probe (K8s 1.20+) — prevents liveness from killing slow-starting containers
    startupProbe:
      httpGet:
        path: /health
        port: 8080
      failureThreshold: 30
      periodSeconds: 2
    # Liveness probe — restarts unresponsive containers
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 30
    # Readiness probe — controls traffic routing
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    # Resource limits (prevent noisy neighbors)
    resources:
      requests:
        cpu: "100m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"
```

### 4. Convert environment variables to Secrets and ConfigMaps

```bash
# Non-sensitive config → ConfigMap
kubectl create configmap app-config \
  --from-literal=NODE_ENV=production \
  --from-literal=LOG_LEVEL=info

# Sensitive data → Secret
kubectl create secret generic app-secrets \
  --from-literal=DATABASE_URL='postgres://user:pass@db:5432/myapp' \
  --from-literal=API_KEY='sk-xxx'

# For GitOps: use Sealed Secrets to safely commit encrypted secrets
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
```

```yaml
# Reference in Deployment
envFrom:
  - configMapRef:
      name: app-config
  - secretRef:
      name: app-secrets
```

### 5. Set up Gateway API or Ingress for external access

Replace compose `ports:` with proper external access. **Gateway API is the recommended approach for new clusters** (K8s 1.31+). Ingress-NGINX retired on 2026-03-24 — if you must stay on Ingress, choose an actively maintained controller (F5 NGINX Ingress, HAProxy, Traefik). [src7, src8, src9]

```yaml
# Option A: Gateway API (recommended for new deployments)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
    - name: my-gateway
  hostnames: ["myapp.com"]
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: web
          port: 8080

---
# Option B: Legacy Ingress (still widely supported)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts: [myapp.com]
      secretName: myapp-tls
  rules:
    - host: myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 8080
```

### 6. Add NetworkPolicies

Compose `networks:` isolation does not carry over. By default, all K8s pods can talk to each other. [src7]

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-web-only
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: web
      ports:
        - port: 3000
```

### 7. Package as Helm chart or Kustomize (recommended)

```bash
# Option A: Helm chart
helm create myapp
# Replace templates/ with your refined manifests
# Parameterize values in values.yaml
helm install myapp ./myapp -f values-production.yaml
helm upgrade myapp ./myapp -f values-production.yaml

# Option B: Kustomize overlays (built into kubectl)
# base/
#   deployment.yaml, service.yaml, kustomization.yaml
# overlays/production/
#   kustomization.yaml (patches: replicas, resources, image tags)
kubectl apply -k overlays/production/
```

## Code Examples

### Python: Programmatic Kompose conversion with validation

> Full script: [python-programmatic-kompose-conversion-with-valida.py](scripts/python-programmatic-kompose-conversion-with-valida.py) (33 lines)

```python
# Input:  docker-compose.yml path
# Output: Validated Kubernetes manifests
import subprocess
import yaml
from pathlib import Path
# ... (see full script)
```

### Bash: End-to-end migration script

> Full script: [bash-end-to-end-migration-script.sh](scripts/bash-end-to-end-migration-script.sh) (27 lines)

```bash
#!/bin/bash
# Input:  docker-compose.yml
# Output: Deployed Kubernetes application
set -euo pipefail
COMPOSE_FILE="${1:-docker-compose.yml}"
# ... (see full script)
```

### Alternative: Move2Kube for complex migrations

For larger projects with multiple Dockerfiles and source code, Move2Kube (CNCF Konveyor project) provides a more comprehensive migration path. [src5]

```bash
# Install Move2Kube (v0.3.15+)
curl -L https://github.com/konveyor/move2kube/releases/latest/download/move2kube-linux-amd64 -o move2kube
chmod +x move2kube && sudo mv move2kube /usr/local/bin/

# Step 1: Plan — analyzes Docker Compose + Dockerfiles + source
move2kube plan -s ./my-compose-project

# Step 2: Transform — interactive Q&A, generates K8s manifests + Helm + CI/CD
move2kube transform -s ./my-compose-project

# Output: deploy/yamls/, deploy/cicd/, deploy/scripts/
```

## Anti-Patterns

### Wrong: Using hostPath volumes in production

```yaml
# ❌ BAD — hostPath ties pods to specific nodes and breaks scaling
volumes:
  - name: data
    hostPath:
      path: /data/myapp
```

### Correct: Use PersistentVolumeClaims

```yaml
# ✅ GOOD — PVCs are portable and work with cloud storage [src1]
volumes:
  - name: data
    persistentVolumeClaim:
      claimName: myapp-data
```

### Wrong: Putting secrets in ConfigMaps or env vars in plain text

```yaml
# ❌ BAD — secrets visible in kubectl describe
env:
  - name: DATABASE_PASSWORD
    value: "my-secret-password"
```

### Correct: Use Kubernetes Secrets or external secret managers

```yaml
# ✅ GOOD — secrets stored securely [src3]
env:
  - name: DATABASE_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secrets
        key: password
```

### Wrong: Relying on depends_on for service ordering

```yaml
# ❌ BAD — Kubernetes has no depends_on
# Compose: depends_on: [db, redis]
# This causes startup crashes when dependencies aren't ready
```

### Correct: Use init containers and readiness probes

```yaml
# ✅ GOOD — init container waits for DB before app starts [src6]
initContainers:
  - name: wait-for-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z db-service 5432; do sleep 2; done']
```

### Wrong: Deploying without resource limits

```yaml
# ❌ BAD — pod can consume all node resources and trigger OOM kills on other pods
containers:
  - name: api
    image: myregistry/api:1.0
    # No resources block = unlimited
```

### Correct: Always set resource requests and limits

```yaml
# ✅ GOOD — bounded resource usage, enables HPA [src3]
containers:
  - name: api
    image: myregistry/api:1.0
    resources:
      requests:
        cpu: "100m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"
```

## Common Pitfalls

- **Kompose skips `build:` directives**: Images must be pre-built and in a registry. Fix: build, tag, push before conversion. [src6]
- **No readiness probes generated**: Kompose doesn't create health checks. Without readiness probes, services receive traffic before being ready. Fix: add `readinessProbe`, `livenessProbe`, and `startupProbe` to every container. [src3, src6]
- **compose `depends_on` not converted**: Kubernetes doesn't guarantee startup order. Fix: use init containers or implement connection retry logic in your application. [src6]
- **Bind mounts don't work in K8s**: Local file paths (e.g., `./config:/app/config`) don't exist in cluster nodes. Fix: use ConfigMaps for config files, PVCs for data, Secrets for credentials. [src1]
- **Default resource limits too permissive**: Without limits, a single pod can consume all node resources. Fix: always set `resources.requests` and `resources.limits`. [src3]
- **NetworkPolicy missing**: Compose `networks:` doesn't create K8s NetworkPolicies. By default all pods can communicate. Fix: implement NetworkPolicies to restrict pod-to-pod traffic. [src7]
- **PodSecurityPolicy removed in K8s 1.25**: If your migration guide references PSP, it is outdated. Fix: use Pod Security Admission (PSA) namespace labels: `pod-security.kubernetes.io/enforce: restricted`. [src7]
- **Secrets not encrypted at rest**: K8s Secrets are only base64-encoded by default. Fix: enable etcd encryption at rest, or use Sealed Secrets / external secret managers for GitOps workflows. [src3]
- **Choosing Ingress-NGINX in 2026**: The project was retired on 2026-03-24 — no more security patches. Fix: pick Gateway API for new clusters, or migrate to F5 NGINX Ingress Controller, HAProxy, or Traefik for an Ingress-compatible drop-in. [src9]
- **Using `gitRepo` volumes**: Permanently removed in Kubernetes 1.36 (deprecated since 1.11). Fix: use an initContainer that clones the repo into an `emptyDir` volume, then mount that volume in the main container. [src7]

## Diagnostic Commands

```bash
# Validate compose file before conversion
docker compose config -f docker-compose.yml

# Convert and see what Kompose generates (stdout preview)
kompose convert -f docker-compose.yml --stdout

# Dry-run deployment (validates manifests without applying)
kubectl apply --dry-run=client -f k8s-manifests/

# Check pod status after deployment
kubectl get pods -o wide
kubectl describe pod <pod-name>
kubectl logs <pod-name> --previous  # logs from crashed container

# Verify services and endpoints
kubectl get svc
kubectl get endpoints

# Test service connectivity from inside the cluster
kubectl run test --rm -it --image=busybox:1.36 -- wget -qO- http://web-service:8080/health

# Check Gateway API routes (if using Gateway API)
kubectl get httproutes
kubectl describe httproute app-route

# Verify NetworkPolicies
kubectl get networkpolicies
kubectl describe networkpolicy api-allow-web-only

# Check resource usage vs limits
kubectl top pods
kubectl top nodes
```

## Version History & Compatibility

| Tool | Version | Status | Key Changes |
|---|---|---|---|
| Kompose | 1.38 (Jan 2026) | Current | Compose Specification v3 full support, Helm chart output, ARM64 builds [src2] |
| Kompose | 1.34 | Stable | Improved compose-go library, better volume handling [src2] |
| Kubernetes | 1.36 "Haru" (Apr 2026) | Current | User Namespaces GA, Mutating Admission Policies GA, OCI VolumeSource GA, SELinux mount labels GA, gitRepo volume removed [src7] |
| Kubernetes | 1.35 (Dec 2025) | Supported | Timbernetes release, continued Gateway API improvements [src7] |
| Kubernetes | 1.33 (Apr 2025) | Supported | Sidecar containers GA, native init container restartPolicy:Always [src7] |
| Kubernetes | 1.31 (Aug 2024) | Supported | AppArmor GA, pod-level resource limits (alpha) [src7] |
| Kubernetes | 1.25 (Aug 2022) | EOL | Removed PodSecurityPolicy, stable Ingress API [src7] |
| Helm | 3.16+ | Current | OCI registry support, improved schema validation [src4] |
| Docker Compose | v2 (Go) | Current | `docker compose` (space) replaces `docker-compose` (hyphen) |
| Move2Kube | 0.3.15 (Mar 2025) | Current | Enhanced Docker Compose parsing, Helm + Kustomize output [src5] |
| Gateway API | v1.4 (Nov 2025) | Current | External auth filter, BackendTLSPolicy stable [src8] |
| Ingress-NGINX | EOL 2026-03-24 | Retired | No more releases, bugfixes, or security patches — migrate to Gateway API or alternative controller [src9] |

## When to Use / When Not to Use

| Migrate When | Don't Migrate When | Use Instead |
|---|---|---|
| Production workloads need scaling | Local development only | Keep Docker Compose |
| Multiple environments (staging, prod) | Team < 3 engineers and single-server | Docker Compose + systemd |
| Need rolling updates and self-healing | Budget doesn't cover K8s ops overhead | Railway, Fly.io, or Render |
| Cloud-managed K8s available (EKS/GKE/AKS) | App is a single stateless container | Cloud Run, App Runner, or Fargate |
| Need service mesh or mTLS | No compliance or multi-tenancy requirements | Docker Compose + Traefik |
| GitOps workflow required | Prototype or proof-of-concept stage | Docker Compose |

## Important Caveats

- Docker Compose is still the best tool for local development. Many teams use Compose locally + Kubernetes in production (hybrid approach). Tools like Tilt and Skaffold bridge the gap.
- Kompose is a starting point, not the final product. Always review and refine generated manifests for production — especially probes, resource limits, secrets, and NetworkPolicies.
- Kubernetes has a steep learning curve — budget 2-4 weeks for team training before a production migration. Consider starting with a managed service (EKS, GKE, AKS) to reduce ops burden.
- Storage classes vary by cloud provider — PVC behavior and default StorageClasses differ between EKS (gp3), GKE (pd-standard), and AKS (managed-premium). Test PVC provisioning before migrating stateful workloads.
- Kubernetes 1.33+ graduated sidecar containers to stable — if your compose file uses sidecar patterns, use `restartPolicy: Always` on init containers for proper lifecycle management.
- Move2Kube (CNCF Konveyor) is a more comprehensive alternative to Kompose for projects with complex source analysis needs — it generates Helm charts, CI/CD pipelines, and Kustomize overlays in one pass.
- **Ingress-NGINX is end-of-life as of 2026-03-24**. If your old migration notes recommend `ingressClassName: nginx`, treat them as outdated. New clusters should default to Gateway API + HTTPRoute; existing clusters running ingress-nginx should migrate to F5 NGINX Ingress Controller, HAProxy, or Traefik to keep receiving security patches. [src9]
- Kubernetes 1.36 (Apr 2026) graduated User Namespaces to GA — container root maps to a non-privileged user on the host, materially improving container escape isolation. Worth enabling for new deployments. [src7]

## Related Units

- [Heroku to AWS Migration](/software/migrations/heroku-to-aws/2026)
- [Monolith to Microservices](/software/migrations/monolith-to-microservices/2026)
- [Jenkins to GitHub Actions](/software/migrations/jenkins-to-github-actions/2026)
