DevKitHub

Programming

docker run to Docker Compose Converter

Paste one or more docker run commands and get the compose.yaml that starts the same containers. Every option with no Compose equivalent is named, not dropped.

8 lines
1 service16 lines
  • Not in the file: -d. Each is explained below.
  • -d is left out: running in the background is chosen when you start Compose, with docker compose up -d, not per service.
  • pgdata is declared with name: set to the same name, so Compose uses the volume docker run used, data included. Without it Compose would create a new, empty one called <project>_pgdata. Compose warns on the first start that the volume was not created by Compose; add external: true to say that is intended.
  • Every $ in a value is written as $$, because Compose reads $ as the start of a variable. The container still receives a single $.

Summary

Services
postgres
Ignoredno Compose equivalent
-d

Save the file as compose.yaml, the name Docker Compose looks for first, and start it with docker compose up -d. The service name applies to the first command when several are pasted.

This tool runs entirely in your browser. Your input is never uploaded, stored or logged.

How it works

A docker run command is read the way your shell and then the docker CLI read it. The shell part comes first: quotes, backslash escapes, $'...' strings and line continuations are resolved, and the input is cut into separate commands at new lines, ; and &&, so each docker run in a pasted script becomes a service. The options are then read with docker's own flag table: -dit is three switches, -p80:80 and --publish=80:80 mean the same as -p 80:80, a switch such as --init never takes the next word, and the first word that is not an option is the image. Everything after the image is the container's command, which is why docker run nginx -p 80:80 publishes nothing; that case gets a warning.

Each option becomes the Compose Specification key for the same engine setting, and where the two tools behave differently, the file follows docker run. A named volume is declared with name: set to itself, because Compose otherwise creates a new, empty <project>_pgdata and the database appears to have lost its data. A network docker run joined must already exist, so it is declared external: true, unless the same input creates it with docker network create. --env-file becomes env_file with format: raw, the parser docker run uses. Memory and CPU limits use the service-level mem_limit and cpus from the core specification rather than the optional deploy.resources; --gpus becomes the device reservation under deploy that Docker Compose applies without Swarm. Ports are always quoted, because 22:22 unquoted is the number 1342 to a YAML 1.1 parser, and there is no version: key, which the specification marks obsolete.

Dollar signs need the most care. Compose substitutes $VARIABLE in every value, so a literal $, such as one in a single-quoted password or in a health check like pg_isready -U $POSTGRES_USER, is written as $$ and the container still receives one $. A $VARIABLE your shell would have expanded stays a variable, which Compose fills in from the environment or a .env file, and $(pwd) at the start of a bind mount becomes ., the folder that holds compose.yaml. Options with no Compose equivalent, such as -d, --rm, --cidfile and anything docker does not recognise, are listed as ignored with the reason. Shell loops, functions and variables assigned earlier in a script are not followed, and PowerShell is read only as far as its backtick line continuations.

Common problems

Every example below is run against this tool in our test suite, so what it says here is what the tool actually does.

The "pass" variable is not set. Defaulting to a blank string.

Why:
Compose substitutes variables in every value of the file, so a password such as s3cr3t$pass copied from a docker run command reaches the container as s3cr3t followed by the value of $pass, which is usually empty.
Fix:
Write a literal $ as $$ in a Compose file. The converter does this for every $ the shell would not have expanded.

After moving to Compose, the database starts empty.

Why:
A volume under the top-level volumes key without a name is created as <project>_pgdata, a different volume from the pgdata that docker run used. The old data is still there, just not mounted.
Fix:
Give the volume name: pgdata, as the converter does, or external: true if it must already exist. docker volume ls shows both.

No image: every option was read, but nothing after them names the image to run.

docker run -d \
  -p 80:80
  nginx
Why:
A line that is not continued with a backslash ends the command, and here the line before the image lost its backslash. A shell runs docker run -d -p 80:80 on its own and then tries to run nginx as a separate command.
Fix:
End every line but the last with a backslash, or put the image on the same line as the last option.

-p 8080:80:tcp is not a valid port mapping: the protocol goes after a slash, as in 8080:80/tcp.

docker run -p 8080:80:tcp nginx
Why:
Colons separate the host IP, the host port and the container port, so a third colon makes docker read 8080 as an IP address. The protocol is a suffix after a slash.
Fix:
Write -p 8080:80/tcp, or -p 53:53/udp for UDP. TCP is the default and can be left out.

data/db is not a valid volume name, and it is not a host path either.

docker run -v data/db:/var/lib/db postgres
Why:
A volume source that does not start with /, ./ or ~ is a volume name to both docker and Compose, and a volume name cannot contain a slash.
Fix:
Write ./data/db for a folder next to where you run the command, which Compose resolves from the folder that holds compose.yaml.

The container starts, but -p 80:80 published nothing.

Why:
docker reads options only up to the image. In docker run nginx -p 80:80 the -p 80:80 comes after the image, so it is passed to nginx as arguments. The converter reads it the same way, puts it in command: and warns.
Fix:
Put every docker option before the image: docker run -p 80:80 nginx.

Frequently asked questions

Does a Compose file still need the version key?
No. The Compose Specification keeps the top-level version only for backward compatibility: Docker Compose ignores it, warns that it is obsolete, and always validates the file against the latest schema. The converter leaves it out.
Should the file be called compose.yaml or docker-compose.yml?
compose.yaml is the preferred name. Docker Compose looks for compose.yaml and compose.yml, and for docker-compose.yaml and docker-compose.yml for backward compatibility, and uses compose.yaml when more than one exists.
What replaces docker run -d and --rm in Compose?
Neither is a setting of the service. docker compose up -d starts every service in the background, and docker compose down stops and removes the containers. For a one-off container that is removed when it exits, docker compose run --rm SERVICE is the equivalent of docker run --rm.
How does --gpus all look in a Compose file?
As a device reservation under deploy.resources.reservations.devices, with count: all and capabilities: [gpu], the form Docker's GPU guide uses and every Compose v2 release reads; Docker Compose applies it without Swarm. Compose 2.30.0 added a shorter gpus: key that means the same. As in docker run, no driver is named unless the command names one.

Last updated