docker-compose to Kubernetes
Convert a docker-compose.yml into Kubernetes Deployment and Service manifests.
Related Tools
Convert a .env file into a base64-encoded Kubernetes Secret manifest.
Convert a .env file into a docker-compose environment section.
Format and validate YAML with 2-space or 4-space indentation.
Validate JSON against a JSON Schema with JSON-pointer error paths.
Convert a .env file into gh secret set commands for GitHub Actions.
Convert a docker-compose.yml into docker run commands.
Documentation
What is docker-compose to Kubernetes?
This tool converts a docker-compose.yml file into the closest equivalent set of Kubernetes manifests — a Deployment and, if the service exposes ports, a matching Service per compose service, plus a PersistentVolumeClaim for each named volume. It's a starting point for moving a local Compose stack onto a cluster, not a drop-in replacement for hand-tuned production YAML.
How it works
Each service's image, ports, environment, and volumes become a single-container Deployment with replicas taken from deploy.replicas (defaulting to 1). Docker Compose service and volume names are lowercased and stripped of characters Kubernetes doesn't allow in object names (only lowercase letters, digits, and - are valid). Manifests are serialized with js-yaml and joined with --- document separators so the whole output can be piped straight into kubectl apply -f.
Named volumes (e.g. db-data:/var/lib/postgresql/data) get a dedicated PersistentVolumeClaim (1Gi default, one per unique volume name, deduplicated across services), since that's the portable, multi-node-safe equivalent. Bind mounts (a literal host path like ./html:/usr/share/nginx/html) become hostPath volumes instead, with a comment warning that hostPath only works if that exact path exists on whichever node the pod is scheduled to.
Features
- Generates
Deployment+Servicepairs, ready forkubectl apply - Named volumes become deduplicated
PersistentVolumeClaimobjects; bind mounts becomehostPathwith a portability warning - Sanitizes compose service/volume names into valid Kubernetes RFC 1123 object names
- Flags
depends_onand build-only services with explanatory comments rather than guessing - Carries
deploy.replicasstraight intospec.replicas - Copy, download as a single
.yamlfile, or load sample data
Example
Input:
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/dataOutput:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# ^ default 1Gi size — adjust to what db-data actually needs.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
spec:
replicas: 1
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: db
image: postgres:15
ports:
- containerPort: 5432
protocol: TCP
env:
- name: POSTGRES_PASSWORD
value: secret
volumeMounts:
- name: db-data
mountPath: /var/lib/postgresql/data
volumes:
- name: db-data
persistentVolumeClaim:
claimName: db-data
---
apiVersion: v1
kind: Service
metadata:
name: db
spec:
selector:
app: db
ports:
- port: 5432
targetPort: 5432
protocol: TCPCommon errors
"No services found" means the input is missing a top-level services: map. A service with no Deployment in the output, only a comment, means it had a build: section but no image: — Kubernetes can't build images, so you need to build and push one to a registry first. A service or volume name with underscores or uppercase letters gets silently rewritten to a hyphenated lowercase equivalent to satisfy Kubernetes naming rules — check the generated metadata.name matches what you expect if you're scripting around it.
Best practices
Treat the output as a first draft: resize the generated PVCs to real storage requirements, replace any hostPath volume with a proper PVC before running on more than one node, and add liveness/readiness probes plus resource requests/limits, none of which a docker-compose file has an equivalent for. If services had depends_on, add a readiness probe or initContainer to actually enforce that ordering — Kubernetes won't infer it from the comment.
Frequently Asked Questions
What happens to a service that only has a "build" section, no "image"?▾
Kubernetes can't build a container image itself — it only runs images that already exist in a registry. A build-only service is skipped with a comment explaining you need to build and push that image first, rather than generating a Deployment that references an image that doesn't exist anywhere.
How are bind-mount volumes (./data:/path) handled?▾
Converted to hostPath volumes, with a comment flagging the real limitation: hostPath only works if that exact path exists on the specific node the pod happens to land on, which breaks the moment you have more than one node. For anything beyond local single-node testing (e.g. minikube), use a PersistentVolumeClaim instead — named volumes already get one generated automatically.
Does depends_on control startup order in the output?▾
No — Kubernetes doesn't have a direct equivalent to depends_on, so it's surfaced as a comment rather than faked with something that wouldn't actually enforce the ordering. If strict startup order matters, add a readiness probe to the dependency and/or an initContainer that waits for it.
Are named volumes and bind mounts treated differently?▾
Yes — a named volume (e.g. db-data:/var/lib/postgresql/data) gets its own PersistentVolumeClaim generated (default 1Gi, meant to be resized to what you actually need), since that's the portable, cluster-friendly equivalent. A bind mount (a real host path) becomes hostPath instead, since a PVC would silently change the semantics of "this specific directory on my machine."