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

More docker tools

Docker Container Image Reference Parser Is that a registry or a Docker Hub user? Dockerfile Linter What this image will do to you later Docker Compose to Kubernetes And why depends_on has no equivalent Docker Logging Driver Generator json-file never rotates Dockerfile Non-Root USER Generator Numeric, or Kubernetes rejects it Docker OCI Image Labels Generator One of them actually does something Docker Hub Pull Rate Limit Calculator Counted per IP, not per person Docker Registry Storage Calculator Deleting a tag frees nothing Docker Build Context Analyzer All of it uploads before anything runs Docker Compose to docker run Converter Names stop resolving Docker Compose Version Migrator version: is obsolete now Docker Compose Network and Port Viewer Everything reaches everything Docker Command Explainer Which flags hand over the host Docker BuildKit Cache Mount Generator apt deletes the cache you just mounted Docker BuildKit Secret Mount Generator ARG is in docker history forever Docker .dockerignore Pattern Tester *.log does not match logs/app.log Docker Image Manifest Viewer unknown/unknown is not broken Docker Image Config Viewer Anyone who can pull can read your Env Docker Registry Token Decoder Check the scope before the credentials Docker HEALTHCHECK Generator Docker does nothing when it fails Docker Network Subnet Calculator 31 networks, then it stops Docker History Analyzer The rm did not save anything Docker Run to Compose Converter Some flags have no equivalent Docker CMD and ENTRYPOINT Tester Why docker stop takes ten seconds Docker Port Mapping Tester -p reaches past your firewall Docker -v to --mount Converter -v invents missing directories Docker Restart Policy Simulator The backoff resets after ten seconds Docker Exit Code Explainer 137 is two different problems Docker CPU Limit Converter --cpu-shares limits nothing Docker Memory Limit Calculator -m 512m allows a gigabyte Docker Compose Interpolation Tester An unset variable is an empty string Docker Environment Variable Precedence Tester Your shell does not reach the container Docker Compose Override Merge Previewer An override can add a port, never remove one Dockerfile Multi-Stage Build Analyzer Which stages reach the image Dockerignore Validator It is not .gitignore

Elsewhere on the site