Skip to content
ToolBoxGeniehome

Docker Run to Compose Converter

Developer Tools ยท Added

A docker run command that has grown to five lines of flags is a container nobody can reproduce. This parses one in your browser and writes the equivalent Compose service โ€” ports, volumes, environment, limits, healthcheck and the top-level declarations that named volumes and external networks need โ€” and lists the flags that have no Compose counterpart with the reason, rather than quietly discarding them.

Line continuations with a trailing backslash are fine. Everything after the image name is treated as the container's command.

Try:

How to use the docker run to compose converter

  1. 1Paste the full docker run command. Line continuations with a trailing backslash are fine.
  2. 2Read the generated compose.yaml, then check the 'not carried over' list underneath it.
  3. 3Copy or download the file, save it as compose.yaml, and start it with docker compose up -d.
  4. 4Move anything that looks like a credential out of the environment list and into an env_file before committing the result.

Examples

A database with a named volume

Input
docker run -d --name pg -e POSTGRES_PASSWORD=secret -p 5432:5432 -v pgdata:/var/lib/postgresql/data postgres:16
Result
A pg service with ports, environment and volumes, plus a top-level pgdata volume

The top-level declaration matters: without it Compose creates its own prefixed volume and the existing data is not what the container sees.

Flags with no equivalent

Input
docker run -d --rm --name web -p 8080:80 nginx:alpine
Result
The service, plus notes that -d and --rm do not map

Detaching is how you invoke Compose rather than a property of the service, and Compose keeps containers so they can be inspected.

A healthcheck

Input
docker run --health-cmd "redis-cli ping" --health-interval 10s redis:7
Result
healthcheck with test [CMD-SHELL, redis-cli ping] and interval 10s

CMD-SHELL is used because --health-cmd is a shell string, not an argument list.

About the docker run to compose converter

Why the two are not the same shape

A docker run command is an imperative act with a moment attached: start this container now, with these settings, attached to this terminal. A Compose file is a description of a desired state that something else reconciles โ€” and the difference is exactly why some flags convert cleanly and some cannot convert at all. Ports, volumes, environment and restart policy are all properties of the container that exists afterwards, so they map one to one. Detach, remove-on-exit and interactive attachment are properties of the invocation, and there is nothing in the file to hold them.

Understanding which category a flag falls into makes the conversion predictable. If the flag would still be meaningful about a container that is already running, it has a Compose key. If it only makes sense at the moment of starting, it belongs on the compose command line instead.

The two declarations that are easy to forget

A named volume referenced by a service also has to be declared at the top level of the file, or Compose invents its own project-prefixed volume โ€” and the data you expected to find is quietly somewhere else. The converter emits that declaration whenever it sees a named volume, which is the difference between a file that works and one that appears to work until you look for the data.

External networks are the same story with an extra decision. A --network flag naming an existing network means an existing network, so the generated file marks it external: true. If instead you want Compose to create the network for this project, delete that line โ€” the choice is genuinely yours, which is why it is generated with a note rather than assumed silently.

After the conversion

Run docker compose config against the result before anything else. It validates the file, resolves variables and prints the fully expanded configuration, which catches indentation mistakes and unknown keys in a second. Then bring the stack up and compare docker inspect on the new container with the old one if you still have it โ€” the mounts, environment and network settings should match.

The larger win is not the file itself but what it enables. Once a container is described in a file, it can be reviewed, versioned and extended: a second service alongside it, a depends_on relationship, a healthcheck that gates startup. Almost every multi-container setup began as one long docker run command that somebody finally wrote down.

Frequently asked questions

Why is there no version key at the top of the file?
Because it is obsolete. The version field belonged to the old Compose file format versions 2 and 3, where it selected which schema to validate against. The current Compose specification dropped it, and recent versions of Docker Compose print a warning when they find one. Omitting it is correct for any Compose released in the last few years; if you are on something genuinely old, adding version: "3.8" at the top will not break anything generated here.
What happened to my -d flag?
It is reported as unsupported, because detaching is not a property of a service. In the docker run world, -d says 'start this and give me my terminal back'; in Compose the equivalent decision is made when you run the stack, with docker compose up -d. The same applies to --rm, which asks Docker to delete the container when it exits โ€” Compose deliberately keeps containers so they can be inspected and restarted, and the closest equivalent is docker compose run --rm for a one-off task.
Why does the converter use mem_limit rather than deploy.resources.limits?
Because the deploy section is the Swarm form and plain docker compose up ignores it unless you pass --compatibility. Since the input was a docker run command, the intent was almost certainly a container on one machine, so the service-level mem_limit and cpus keys are the ones that will actually take effect. If you are targeting Swarm, move them into a deploy block โ€” the values are the same, only the location changes.
Is it safe to paste a command containing a password?
The conversion happens entirely in your browser, so nothing is sent anywhere and nothing is stored. The risk is what you do next: a Compose file usually ends up in version control, and an environment variable with a password in it goes with it. The converter flags values that look like credentials for exactly that reason. Move them into an env_file that is listed in .gitignore, or into Docker secrets, before the file is committed.
What if my command has a flag this does not know?
It appears in the 'not carried over' list with a note, and the rest of the conversion proceeds. That is deliberate: silently ignoring a flag would produce a Compose file that looks complete and runs a different container from the one you asked for. Docker's run reference has hundreds of options and the long tail is rarely used, so the sensible design is to handle the common ones properly and be honest about the rest. A command containing a shell pipe or a chained && is rejected outright for the same reason.