docker run to Kubernetes YAML

Paste a docker run command, line continuations and all, and get a Deployment and a Service. The flags that mean something different in Kubernetes, and the ones with no equivalent, are reported rather than quietly dropped.

Paste below, or drop a file anywhere on this panel

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.

Examples

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.

--privileged with a host mount

The flags that translate into a pod nobody should apply

docker run -d --privileged -p 8080:80 -v /:/host nginx:latest

A secret in -e

An environment value that belongs in a Secret, and the restart policy that has no direct equivalent

docker run -e DB_PASSWORD=hunter2 --restart always myapp:1.2

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Translating -v / to a hostPath volume

    It works and it gives the pod the node's filesystem. Anything that escapes the container owns the node.

    Instead:Use a PersistentVolumeClaim, or a ConfigMap for configuration. A hostPath is a last resort with a written reason.

  2. Expecting --restart always to map to something

    Kubernetes restartPolicy has no direct equivalent, because restarting is what a Deployment does by default. The flag has no meaning in a manifest.

    Instead:Drop it. A Deployment restarts failed containers already.

  3. Keeping -p host:container as a hostPort

    hostPort binds the node's port and limits the pod to one replica per node, which is almost never what was meant.

    Instead:Use a Service. NodePort or LoadBalancer depending on how it should be reached.

What it catches

Most of a docker run maps across without comment. These are the parts where a literal translation is accepted by the cluster and behaves differently.

-m 512m is not memory: 512m

Docker's m is mebibytes. Kubernetes' 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 conversion is done here and the finding is critical, because this one is silent at every stage where anyone would look.

There is no host port to translate

-p 8080:80 maps a port on your machine, and a cluster has no equivalent. A Service is a different object with a different job, and a NodePort cannot be 8080 because the range starts at 30000. The honest translation for local work is kubectl port-forward, which is what the finding says instead of quietly emitting a NodePort that will not apply.

Trailing words are args, not command

Words after the image replace the image's CMD and are still passed to its ENTRYPOINT. In Kubernetes, command replaces ENTRYPOINT and args replaces CMD, so args is the faithful mapping. Nearly every converter for this gets it backwards, and the result is a working image that starts exiting immediately or cannot find a binary that is definitely present.

The flags with no equivalent at all

--restart=no and --rm cannot be expressed by a Deployment, whose pod template is required to use restartPolicy: Always; that workload is a Job. --ulimit has no field anywhere in the pod spec. --memory-swap has nothing to configure because Kubernetes disables swap. Each is named as a gap rather than dropped silently.

Where the two models stop lining up

docker run starts one container on the machine you typed it on. A Deployment describes a workload that a scheduler places somewhere, replicates, restarts and moves. Most flags have a direct equivalent. The ones that do not are the ones worth reading, because they are where an apparently faithful translation changes behaviour.

The container is not on your machine any more

This is the root of most of the differences. A bind mount assumes a filesystem that follows the container, and a pod can be scheduled onto any node. A published port assumes a host that clients can reach, and a pod's node is an implementation detail. Both translate literally, and both are usually the wrong answer.

docker                    kubernetes
-v /data:/data            hostPath: needs that exact path
                          on whichever node it lands on

-v mydata:/data           PVC: follows the pod, which is
                          what the docker volume did

-p 8080:80                Service: a name and an address
                          inside the cluster. Getting in
                          from outside is a separate call

ENTRYPOINT, CMD, command and args

Four names for two slots. Kubernetes command overrides the image's ENTRYPOINT and args overrides its CMD, and each overrides independently. Docker's trailing words replace CMD only, which is why they belong in args. Putting them in command instead discards the entrypoint the image was built around.

docker run myimage foo bar
  ENTRYPOINT stays, CMD becomes [foo bar]

args: ["foo", "bar"]          correct
command: ["foo", "bar"]       wipes the ENTRYPOINT

docker run --entrypoint /bin/sh myimage -c 'x'
  command: ["/bin/sh"]
  args: ["-c", "x"]

Limits, requests, and the class you land in

A docker flag is a ceiling and there is nothing else to set. Kubernetes needs a request as well, which is what the scheduler reserves and what decides the QoS class. Requests are set equal to limits here, making the pod Guaranteed: predictable, and the last thing evicted under node pressure. Lowering the request makes it Burstable, able to use spare capacity and evictable before Guaranteed pods.

docker run -m 512m --cpus 0.5

resources:
  requests:            <- new concept. What the
    cpu: 500m             scheduler sets aside
    memory: 512Mi
  limits:              <- what docker gave you
    cpu: 500m
    memory: 512Mi

requests == limits         Guaranteed
requests <  limits         Burstable
nothing set                BestEffort, evicted first

The image's HEALTHCHECK is ignored completely

A HEALTHCHECK baked into a Dockerfile has no effect in Kubernetes. The kubelet does not read it and never has. Without probes, a pod is considered ready the moment its process starts, so traffic arrives before the application can serve it, and a process that hangs without exiting is never restarted.

HEALTHCHECK CMD curl -f http://localhost/   # ignored

readinessProbe:   should traffic go here yet
livenessProbe:    is it wedged, restart it
startupProbe:     hold the other two off until
                  a slow start finishes

--health-cmd is converted. A HEALTHCHECK inside the
image is not visible from a command line, so it is not.

--network host needs one more line than you think

hostNetwork: true is the direct translation, and on its own it breaks DNS. A pod in the host network namespace resolves against the node's resolver, so no in-cluster Service name works. dnsPolicy: ClusterFirstWithHostNet is what fixes it, and leaving it out is the most common mistake with hostNetwork by a distance.

spec:
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet   <- not optional

# without the second line, every Service lookup fails
# and the pod otherwise looks completely healthy