Migrating Docker Compose to Kubernetes With AI Help
A practical walkthrough of converting a docker-compose.yml into clean Kubernetes manifests with AI drafting the boilerplate and you reviewing every line.
- #kubernetes
- #docker-compose
- #migration
- #ai
I inherited a docker-compose.yml that ran a six-service app perfectly well on one beefy VM, and a mandate to get it onto Kubernetes “this quarter.” Compose and Kubernetes look superficially similar — services, images, ports, volumes — but the mapping is full of small traps. Doing the conversion by hand is slow and error-prone. Doing it entirely with a one-shot tool gives you manifests you don’t understand. The approach that actually worked was a middle path: let an AI model draft the manifests service by service, and review each one like I would a junior engineer’s PR.
Why not just use kompose?
kompose convert exists and it’s a fine starting point. But it produces literal, naive output: every service becomes a Deployment plus a Service, host volumes become hostPath mounts, and depends_on is silently dropped because Kubernetes has no equivalent. You end up rewriting most of it anyway.
The reason I reach for an AI model instead is that it can reason about intent. When I tell it “this postgres service holds data,” it knows to suggest a StatefulSet with a PersistentVolumeClaim rather than a Deployment with an ephemeral mount. That contextual judgment is what saves real time. I still run kompose sometimes just to compare outputs.
Start by sharing the compose file, nothing else
The compose file is safe to share with the model — it’s declarative config. What I scrub first: any inline secrets, real passwords in environment: blocks, and private registry paths. The model never sees my cluster, my kubeconfig, or production credentials. Its job is to transform text I hand it.
My opening prompt:
Here is a
docker-compose.yml. Convert each service to Kubernetes manifests. Use Deployments for stateless services and StatefulSets for anything with a named volume holding data. Generate a Service per workload, ConfigMaps for non-secret env, and leave a# TODO: secretcomment wherever a value looks sensitive. Do not invent resource limits — leave them as TODOs.
That last sentence matters. Left alone, models confidently fabricate resources blocks with numbers pulled from nowhere. I want placeholders I fill in from real metrics, not guesses.
Map the concepts deliberately
Here is the translation table I keep in front of me while reviewing the model’s output:
| Compose | Kubernetes |
|---|---|
service (stateless) | Deployment + Service |
service (with data volume) | StatefulSet + PVC + headless Service |
ports | Service ports / container containerPort |
environment | ConfigMap or Secret |
volumes (named) | PersistentVolumeClaim |
depends_on | (no equivalent — use readiness probes) |
restart: always | default pod restart policy |
networks | NetworkPolicy if isolation matters |
The depends_on row trips people up. Compose starts containers in order; Kubernetes does not. The model knows this and, when prompted, replaces ordering assumptions with proper readiness probes so dependent pods wait for their dependencies to actually be ready, not just started.
What a converted service looks like
For a stateless API service, the draft came back clean:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: "registry.example.com/api:1.4.0"
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: api-config
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
# TODO: set resources from real metrics
The probe was a nice touch the model added on its own — but I verified the /healthz path actually exists, because models will happily invent a health endpoint that your app doesn’t serve.
Verify before anything touches a cluster
I never apply generated manifests directly. The review pipeline is entirely offline first:
# Syntactic + schema validation, no cluster needed
kubectl apply --dry-run=client -f manifests/
# Server-side dry run against a non-prod cluster
kubectl apply --dry-run=server -f manifests/ --namespace staging
Client-side dry-run catches malformed YAML and bad field names. Server-side dry-run runs the manifests through admission control and the API server’s validation without persisting anything — it catches the subtler problems like an invalid securityContext or a Service selecting no pods.
Pro Tip: Run kubectl apply --dry-run=server against staging, never prod, even though it doesn’t persist objects. Admission webhooks can have side effects, and you want the human-in-the-loop discipline of treating “talks to prod” as a line the AI workflow never crosses.
The things AI consistently gets wrong
After a dozen of these migrations, the recurring miss list is predictable, and it’s the whole reason a human reviews every file:
- Shared volumes. Compose lets two services mount the same named volume. In Kubernetes,
ReadWriteOncePVCs can’t span nodes. The model needs reminding to useReadWriteManyor rethink the design. - Localhost networking. Compose services on the same network reach each other on
localhostwhen in the same pod, but separate pods use Service DNS names. Generated code sometimes keepslocalhostreferences that quietly break. .envfiles. Models tend to inline env values rather than splitting genuine secrets into a Secret object.
I treat the first pass as a draft, then go through it asking “does this actually match how the app talks to itself?”
Bring secrets in safely
The model leaves # TODO: secret markers; I fill them with a real secrets workflow, not by pasting values into a manifest. That usually means an external secrets operator pulling from a vault, so no plaintext credential ever lands in Git or in the model’s context.
Wrap up
Converting Compose to Kubernetes is mechanical enough that AI saves you hours, and judgment-heavy enough that you can’t fully automate it. Let the model draft Deployments, Services, and ConfigMaps; you decide what’s stateful, what’s secret, and what gets applied. The model never holds your kubeconfig, and nothing reaches the cluster without a human running the command.
Keep going with the Kubernetes & Helm category, reusable prompts, and the Kubernetes prompt pack. If you want a second set of eyes on the generated manifests, the code review dashboard catches the localhost-and-shared-volume mistakes before they ship.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.