From docker run to Compose: flags that map cleanly (and those that don’t)

Published: 2026-09-05

Which docker run flags become Compose YAML fields, which need manual edits (--rm, --mount, --gpus, health checks), and how to convert locally without uploading your command.

docker run is how you try an image once. Compose is how you declare the same container as a reusable service next to networks, volumes, and other apps. Turning a long docker run line into a services: fragment saves time—but only if you know which flags map 1:1 and which only look equivalent.

Paste commands that contain registry tokens, AWS_* env vars, or host paths carefully. Prefer a converter that runs in your browser so the command never hits someone else’s server. See Why “local only” matters for developer tools.

What you are converting into

A typical one-liner:

docker run -d --name web -p 8080:80 \
  -e NGINX_HOST=example.com \
  -v web-data:/usr/share/nginx/html:ro \
  --restart unless-stopped \
  nginx:alpine

becomes a Compose service under services: (Compose Spec often omits a top-level version:; older files still use version: "3.8"). The image, ports, env, bind/named volumes, restart policy, and optional container_name usually transfer cleanly. Detach (-d) is implied by Compose’s lifecycle—you do not need an equivalent key.

Service names are often derived from --name or the image repository (strip registry path and tag). Override the name when the derived slug is awkward or collides with another service.

Flags that map cleanly

docker run Compose field (typical)
image + optional args after -- image, command
-p / --publish ports
--expose expose
-e KEY=val / --env environment
-v / --volume volumes (short syntax)
--name container_name (+ service key)
--restart restart
-w / --workdir working_dir
-u / --user user
--entrypoint entrypoint
--hostname / -h hostname
--network / --net networks (plus top-level networks:)
-i / -t / -it stdin_open, tty
--privileged, --read-only, --init matching booleans
--platform platform
--device, --cap-add, --cap-drop devices, cap_add, cap_drop
--add-host, --dns, --tmpfs extra_hosts, dns, tmpfs
--shm-size, --pull, --stop-signal shm_size, pull_policy, stop_signal
-m / --memory, --cpus often deploy.resources.limits

Short clusters such as -dit expand the same way as separate flags. Quoted values and line continuations (\) are normal in pasted shell history; treat the result as a draft, then run docker compose config before production.

Flags that do not map 1:1

These show up constantly in READMEs and still need human Compose edits:

Flag / pattern Why it is awkward
--rm No Compose twin; Compose already manages create/remove with the project.
-P / --publish-all Publishes every EXPOSEd port; Compose wants explicit ports: mappings.
--env-file Path is not inlined; add env_file: yourself (and keep real secrets out of Git—see .env files).
--mount Richer than -v; converters often leave a note—prefer short -v or hand-write long volumes: / mount objects.
--gpus Needs Compose deploy / device request shapes that vary by engine; add manually.
Health-check flags (--health-cmd, …) Map to a healthcheck: block; not always auto-emitted.
--ulimit, --sysctl, --security-opt, --group-add Engine-specific; usually manual.
--log-driver / --log-opt Logging config belongs under Compose logging: when you need it.
--link Legacy; use user-defined networks and service DNS names instead.
--cpuset-cpus, --cpu-shares / -c Not the same as simple cpus limits; review before copying.

Unrecognized or exotic flags should appear as notes in the output, not silent drops. If a note is missing, assume the converter ignored it and check the original command.

Compose Spec vs version: "3.8"

Modern Compose files often use the Compose Specification without a version key. Some older tooling and tutorials still expect version: "3.8". The YAML under services: is largely the same for day-to-day fields; deploy.resources behavior can differ between Swarm and plain docker compose on a single host—verify limits where you actually run.

Custom networks named in --network may be declared at the top level. Special names like host, bridge, and none are often marked external because Compose does not create them the way it creates a project network.

A practical conversion workflow

  1. Copy the docker run (or docker container run) from docs, a script, or shell history.
  2. Convert locally to a services fragment; pick Compose Spec or v3.8 style to match your repo.
  3. Read every warning comment the tool emits (--rm, --env-file, --mount, GPUs, health checks).
  4. Move secrets from inline -e into Compose env_file or your secret store; do not commit real credentials.
  5. Align volume paths and network names with the rest of the stack; replace --link with service names on a shared network.
  6. Validate with docker compose config (or your CI equivalent), then bring the stack up.

For firewall and published ports after you settle on mappings, Common TCP/UDP ports and when to open them pairs well with Compose ports:.

Try it locally in your browser

Use the docker run to Compose converter to:

  • Paste a docker run command and get a Compose YAML fragment.
  • Map common flags (-p, -e, -v, --name, --restart, -it, networks, memory/cpus, and more).
  • See notes for flags that need manual Compose edits (--rm, --env-file, --mount, --gpus, health checks).
  • Choose Compose Spec (no version) or Compose v3.8 output—parsed entirely in your tab.

Related reading

All learn articles