privileged: true
A container with full host access, which has no safe Kubernetes equivalent
services:
web:
image: nginx:latest
privileged: true
ports:
- "80:80"Paste a docker-compose.yml and get a Deployment and a Service for each service, with PersistentVolumeClaims and ConfigMaps where they are warranted. Everything that does not translate, starting with depends_on and bind mounts, is reported rather than quietly dropped.
Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.
The answer appears here
Paste on the left and press Convert. Nothing leaves this tab.
Nothing else to flag.
No formatting problems, and nothing the rules object to. Worth remembering what that covers: this reads the file you pasted, not the account or cluster it will be applied to.
No finding matches that filter.
Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.
A container with full host access, which has no safe Kubernetes equivalent
services:
web:
image: nginx:latest
privileged: true
ports:
- "80:80"A value that must become a Secret rather than being copied into the manifest
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD: hunter2These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
There is no safe equivalent. The nearest Kubernetes setting is securityContext.privileged, which grants the same full host access and most clusters block it by policy.
Instead:Work out which capability was actually needed and grant only that.
A password in Compose becomes a password in a Deployment, in version control, visible to anyone with get on the namespace.
Instead:Move it to a Secret and reference it with secretKeyRef.
Kubernetes has no equivalent. Pods start in parallel and a dependency that is not ready produces crash loops until it is.
Instead:Use readiness probes and let the application retry, or an init container that waits.
kompose is a CLI, and nothing equivalent runs in a browser. Most of a Compose file maps across without comment. These are the parts where the literal translation is accepted by the cluster and behaves differently, or where there is nothing to translate to at all.
There is no field, no annotation and no object in Kubernetes that makes one pod wait for another. Compose starts the dependency first and, with condition: service_healthy, blocks until its healthcheck passes. Kubernetes creates every pod at the same moment and the deciding factor becomes image pull time. Init containers order containers inside one pod, not pods against each other. This is reported rather than dropped, because a converter that drops it hands you a stack that comes up in the wrong order and blames your application.
A named volume becomes a PersistentVolumeClaim. ./html:/usr/share/nginx/html does not: cluster nodes have no copy of your working directory. No hostPath is emitted for it. hostPath would apply cleanly, work on a single-node cluster, and break the first time the pod was rescheduled, which is worse than not translating it, and it is a standard privilege escalation route that most admission policies reject anyway.
Compose command overrides the image CMD; Kubernetes command overrides the ENTRYPOINT. So Compose command maps to Kubernetes args, and Compose entrypoint maps to Kubernetes command. Nearly every converter for this gets it backwards, and the result is a working image that exits immediately or cannot find a binary that is definitely present.
Compose byte suffixes are 1024-based, so m is mebibytes. In a Kubernetes quantity m is milli, one thousandth. Copied across as written, the manifest asks for half a byte, the API server accepts it without comment, and the container is OOMKilled on its first allocation. The value is converted here and the finding is critical, because nothing on the path from the file to the crash mentions units.
ports: "8080:80" maps a port on the machine running Compose, and a cluster has no such machine. A Service is a different object with a different job, and a NodePort cannot be 8080 because the range starts at 30000. The finding says kubectl port-forward, which is the true equivalent, rather than emitting a NodePort that will not apply. The long syntax, ranges, /udp and IP-qualified forms are all read.
Kubernetes has one flat pod network: every pod in every namespace can reach every other pod until a NetworkPolicy says otherwise. A user-defined Compose network did two jobs, DNS and isolation, and only the DNS half needs no translation. If a network was a boundary, it has to be rewritten as a NetworkPolicy deliberately.
Compose describes containers on one machine, started in an order you chose, sharing that machine's filesystem and network. Kubernetes describes workloads a scheduler places, replicates, restarts and moves, in no order at all. Most fields have a direct equivalent. The ones that do not are where an apparently faithful conversion changes behaviour, and they are what this page is for.
This is the single most misunderstood part of the migration. Compose waits; Kubernetes does not, and there is no setting that makes it. The replacement is not an ordering mechanism at all: it is a readiness probe on the dependency, so it receives no traffic until it can serve, and a client that retries its first connection with a backoff. That combination also survives the dependency restarting at three in the morning, which ordered startup never covered.
compose
depends_on:
db:
condition: service_healthy
kubernetes
db gets a readinessProbe, so its Service has
no endpoints until it answers
api retries its first connection and logs
the wait instead of exiting 1
what does NOT work
initContainers orders containers in ONE pod
a sleep in the command moves the race, keeps it
restartPolicy tuning there is nothing to tune A named volume is a claim on storage, so it becomes a PersistentVolumeClaim. A bind mount is a path on the machine you typed the command on, and there is no cluster equivalent. Two more details bite afterwards: a Compose volume has no size and a PVC must request one, and a named volume mounted by two services needs ReadWriteMany, which the default storage class in every managed cluster does not offer.
- pgdata:/var/lib/postgresql/data
PVC. Follows the pod, which is what the
named volume did.
- ./html:/usr/share/nginx/html
Nothing. Config becomes a ConfigMap, code
becomes part of the image, data becomes
a PVC. Live-reload has no equivalent.
- /var/lib/app anonymous, so emptyDir.
Deleted with the pod, and
a Deployment replaces pods.
shared by two services needs ReadWriteMany. EBS,
GCE PD and Azure Disk are
ReadWriteOnce: one node. Four names for two slots, and the two systems use the same word for different slots. Kubernetes command overrides the image's ENTRYPOINT and args overrides its CMD, each independently. Compose command overrides CMD only, leaving the ENTRYPOINT in place, which is why it belongs in args. Putting it in command instead discards the entrypoint the image was built around.
compose
entrypoint: /app/bin/server
command: ["--port", "9000"]
kubernetes
command: ["/app/bin/server"] <- entrypoint
args: ["--port", "9000"] <- command
get it backwards and the image's entrypoint
never runs. The failure is exit 0 on start,
or "executable file not found" for a binary
that is definitely in the image. A published port is a mapping on a host. A Service is a name and a virtual address inside the cluster, which is a different thing that happens to be spelled with two port numbers. Nothing in a cluster can give you 8080 on your laptop except port-forward, and nothing can give the outside world port 80 except an Ingress or a LoadBalancer. A NodePort exists and is not the answer: the range starts at 30000.
ports: "8080:80"
Service
port: 80 what clients ask for
targetPort: 80 where the container listens
kubectl port-forward svc/web 8080:80
the real equivalent of the compose mapping
expose: "9000"
needed no Service in compose, needs one here.
A service with no ports gets no Service object
and no DNS name, so dependents get NXDOMAIN. Compose has one healthcheck and Kubernetes has three probes answering different questions: readiness decides whether traffic is routed here, liveness decides whether to restart, and startup holds both off while a slow process boots. The Compose test is reused for readiness and liveness, which is the safe default and not always right. A liveness probe that checks a downstream dependency restarts this pod when the dependency is what is down.
test: ["CMD", "curl", "-f", "http://x/health"]
exec runs argv directly. No shell.
test: curl -f http://x/health || exit 1
the shell form. Wrapped in sh -c, which
needs a shell in the image: distroless and
scratch have none, and the probe fails 127.
start_period: 40s
a startupProbe, not initialDelaySeconds.
initialDelaySeconds delays the first check
and nothing else, so a slow start still
trips liveness on the second one.
A HEALTHCHECK in the Dockerfile is ignored
entirely. The kubelet does not read it. restart: always is the Kubernetes default and needs no field, because Always is the only restartPolicy a Deployment allows. restart: no cannot be expressed at all: a container meant to run once and stop is a Job. Of the deploy block, only replicas and resources carry over; the rest describes Swarm scheduling and its Kubernetes relatives are separate fields with different semantics.
restart: always nothing to write
restart: unless-stopped nothing to write, and
there is no stopped
state. Scale to 0.
restart: "no" this is a Job
restart: on-failure Job, restartPolicy
OnFailure
deploy.replicas spec.replicas
deploy.resources requests and limits
deploy.mode: global DaemonSet, not a
Deployment
deploy.placement nodeSelector, affinity,
topology spread: pick one
deploy.update_config strategy.rollingUpdate,
different fields