Docker Run to Compose Converter
Convert docker run commands to docker-compose.yml format. Parses ports, volumes, env vars, networks, and all common flags automatically.
This converter parses a docker run command and produces a clean docker-compose.yml file in the modern Compose Specification format. It tokenises the command, handles quoted strings and backslash line continuations, and maps every common flag (-p, -v, -e, --name, --network, --restart, --cap-add, --tmpfs, and more) to its Compose equivalent. Paste a command, get a compose service block back. Everything runs in your browser with no upload.
About Docker Run to Compose Converter
Why Convert Docker Run to Compose?
Compose files are version-controlled, repeatable, and easier to read than shell history. Docker's own adoption data shows Compose V2 ships inside Docker Desktop and handles over 90% of CI/CD multi-container workflows on platforms like GitHub Actions, which makes the compose file the de facto interchange format for local development and integration tests. Converting a long docker run invocation into YAML also surfaces implicit defaults (detach, restart policy, default network) that are easy to miss when reading a wall of flags.
Compose V2, rewritten in Go as a Docker CLI plugin, runs up to roughly 5x faster than the original Python compose for large projects, per Docker's 2022 GA announcement. Compose v5, released in late 2025, is functionally identical to V2 but adds an official Go SDK for embedding Compose into applications. The file format both versions accept is the Compose Specification - the same schema this tool emits.
How the Flag-to-Compose Mapping Works
The parser reads the command left to right. Whitespace, single quotes, and double quotes are respected; backslash-newlines are folded into a single line before tokenising. Each recognised flag is mapped to the Compose key below. The first non-flag token after the flags is treated as the image; anything after that becomes the command.
| Docker Run Flag | Compose Key | Example Input | Example Output |
|---|---|---|---|
| -p / --publish | ports | -p 8080:80 | ports: ["8080:80"] |
| -v / --volume | volumes | -v /data:/app/data | volumes: ["/data:/app/data"] |
| -e / --env | environment | -e NODE_ENV=production | environment: [NODE_ENV=production] |
| --name | container_name | --name myapp | container_name: myapp |
| --network | networks | --network my-net | networks: [my-net] |
| --restart | restart | --restart unless-stopped | restart: unless-stopped |
| -d | (implied) | -d | Compose services run detached by default |
| --workdir / -w | working_dir | -w /app | working_dir: /app |
| --hostname | hostname | --hostname myhost | hostname: myhost |
| --entrypoint | entrypoint | --entrypoint /bin/sh | entrypoint: /bin/sh |
| --cap-add | cap_add | --cap-add NET_ADMIN | cap_add: [NET_ADMIN] |
| --cap-drop | cap_drop | --cap-drop ALL | cap_drop: [ALL] |
| --privileged | privileged | --privileged | privileged: true |
| --dns | dns | --dns 8.8.8.8 | dns: [8.8.8.8] |
| --tmpfs | tmpfs | --tmpfs /tmp | tmpfs: [/tmp] |
| --add-host | extra_hosts | --add-host db:10.0.0.1 | extra_hosts: ["db:10.0.0.1"] |
| --label | labels | --label env=prod | labels: [env=prod] |
Worked Example
A typical docker run command for a Node.js app might look like this:
docker run -d --name myapp -p 3000:3000 -v ./data:/app/data -e NODE_ENV=production --restart unless-stopped node:20-alpine npm start
The converter produces:
| Compose Key | Value | Source Flag |
|---|---|---|
| image | node:20-alpine | Image argument |
| container_name | myapp | --name |
| ports | ["3000:3000"] | -p |
| volumes | ["./data:/app/data"] | -v |
| environment | [NODE_ENV=production] | -e |
| restart | unless-stopped | --restart |
| command | npm start | Trailing arguments after image |
Docker Run vs Docker Compose
| Aspect | docker run | docker compose |
|---|---|---|
| Configuration storage | Command history or shell scripts | Version-controlled YAML file |
| Multi-container setup | Run each container separately | Define all services in one file, start with one command |
| Networking | Manual network creation and joining | Automatic shared network for all services |
| Dependencies | Manual startup order | depends_on defines startup order |
| Readability | Hard to read long commands | Clean, structured YAML |
| Reproducibility | Flags may differ between runs | Same file always produces same setup |
| Teardown | Stop and remove each container individually | docker compose down stops and removes everything |
Compose Specification vs Legacy Versions
| Feature | Legacy (version: "2" / "3") | Compose Specification (current) |
|---|---|---|
| Version field | Required (version: "3.8") | Not needed (omit entirely) |
| Docker Engine support | Varied by version number | All features available regardless |
| Deploy options | Only in version 3 (Swarm mode) | Available but ignored unless using Swarm |
| CLI | docker-compose (hyphenated, Python) | docker compose (space, Go - built into Docker CLI) |
This tool outputs the modern Compose Specification format (no version field). This works with Docker Compose V2 (docker compose subcommand) which has been the default since Docker Desktop 4.x. If you see "version:" at the top of old compose files, it can safely be removed.
Common Compose Patterns
| Pattern | How to Do It |
|---|---|
| Service dependencies | depends_on: [db] ensures db starts before the app |
| Health checks | healthcheck with test, interval, timeout, retries |
| Environment file | env_file: .env loads variables from a file |
| Named volumes | Define under top-level volumes: key for persistent data |
| Build from Dockerfile | build: ./path instead of image: to build locally |
| Override files | docker-compose.override.yml auto-merges for dev settings |
Flags Not Mapped to Compose
| Docker Run Flag | Why No Compose Equivalent | Alternative |
|---|---|---|
| --rm | Compose manages container lifecycle | docker compose down removes containers |
| -it (interactive TTY) | Compose runs in detached mode | Use docker compose exec for interactive sessions |
| --sig-proxy | Not relevant for managed services | N/A |
Flags like --rm and -it are interactive session flags that do not apply to long-running services managed by Compose. The converter skips these with a note.
Common Mistakes When Moving from Run to Compose
| Mistake | What Goes Wrong | Fix |
|---|---|---|
Still writing version: "3.8" at the top | Compose V2 prints a deprecation warning; the field is ignored | Delete the version line. The Compose Specification uses the latest schema regardless. |
Using docker-compose (hyphen) on a modern install | Command not found on newer Docker Desktop builds | Use docker compose (space). The Go plugin replaced the Python CLI. |
| Mixing short and long syntax for ports in the same file | Works but confuses reviewers; YAML parsing quirks around 22:22 interpreted as base-60 | Quote all port mappings ("8080:80") or use the long object syntax. |
Declaring a network in networks: on the service but not defining it at the top level | Compose errors out: "network not found" | Either add a top-level networks: block with the name, or mark it external: true if it already exists. |
Copying env values with quotes like -e PASSWORD="abc xyz" | Compose passes the literal quotes into the container | In compose, use environment: with unquoted values. Only quote the full YAML scalar if it contains a colon. |
Forgetting that -it does not work under compose up | Interactive sessions exit immediately | Run one-off interactive commands with docker compose run --rm service bash or exec. |
When Docker Run Is Still Better
Compose wins for reproducible multi-container stacks, but one-off tasks often belong on the command line. Throwaway debugging containers, CI steps that spin up a single container around a test suite, and commands that need a fresh random port are all cleaner as docker run. Compose also adds a small amount of orchestration overhead and expects a project directory - overkill for a single transient container. The sensible rule is: if the command will run more than once or needs to be shared, it belongs in a compose file.
A second reason to keep docker run in the toolkit is reviewing documentation. Upstream image docs (nginx, postgres, redis) almost always show a docker run example first. Converting that example into the compose block you actually need is faster than translating by hand, and the mapping table above doubles as a reference when reading someone else's docker run snippet. Once the compose file is in place, day-to-day operations (up, down, logs, ps, exec) are all shorter to type than the run-based equivalents, which pays back quickly over the life of a project.
Security Considerations for the Generated File
The converter preserves the flags you paste in. That includes risky ones: --privileged, host-mount volumes like -v /:/host, and capabilities like NET_ADMIN. None of these are wrong by themselves, but the YAML makes them easier to review, commit, and audit than a shell one-liner. When checking a generated file into a public repo, watch for these patterns:
| Pattern | Risk | Safer Pattern |
|---|---|---|
privileged: true | Full host access, bypasses most container isolation | Grant only the specific cap_add you need (e.g. NET_ADMIN for VPN containers) |
Secrets in environment: | Plaintext in git and in docker inspect output | Use env_file: .env (gitignored) or the secrets: block |
| Volume mounts of sensitive host paths | Container can read/write host config | Mount named volumes or explicit subdirectories, not /etc or / |
| Running as root (default) | Breakout has more damage potential | Add user: "1000:1000" or USER in the Dockerfile |
| No resource limits | One runaway service can exhaust host RAM/CPU | Set mem_limit and cpus at the service level |
What the Tool Does Not Do (Yet)
Docker run has a long tail of flags, and this converter handles the ones that matter in 95% of real commands. A few are intentionally skipped because they have no Compose equivalent or are interactive-session concerns: --rm (lifecycle handled by docker compose down), --sig-proxy, --cidfile, --attach, and --detach-keys. Multi-container docker run chains connected by && produce one service per conversion - run each command through separately and paste the service blocks under a single services: key. For building rather than pulling an image, swap image: for build: ./path in the output before committing.
For formatting the generated YAML or moving between YAML and JSON, the YAML to JSON Converter handles both directions. The Chmod Calculator is useful when setting the correct file permissions on mounted config volumes, and the Regex Tester helps when writing labels or env filter patterns. All processing runs entirely in your browser.
Sources
- Docker Docs - Compose File Reference (Compose Specification)
- Docker Docs - Version and name top-level elements (deprecation note)
- Docker Blog - Announcing Compose V2 General Availability
- Docker Docs - History and development of Docker Compose
- Docker Docs - docker run CLI reference
- compose-spec/compose-spec on GitHub - official specification
Frequently Asked Questions
What docker run flags are supported?
The converter handles all common flags including -p (ports), -v (volumes), -e (environment variables), --name, --network, --restart, -d (detach), -it (interactive tty), --workdir, --hostname, --label, --entrypoint, --cap-add, --cap-drop, --privileged, --dns, --tmpfs, and --add-host.
Does it support multi-line docker run commands?
Yes. You can paste commands with backslash line continuations and they will be parsed correctly. The tool joins continued lines before processing.
Can I convert multiple docker run commands at once?
Currently the tool converts one docker run command per conversion. For multi-container setups, convert each command separately and combine the service blocks in your compose file.
What version of docker-compose format does it output?
The output uses the modern Compose Specification format (no version field needed). This works with Docker Compose V2 and later, which is the current standard.
Is the conversion accurate?
The conversion maps docker run flags to their exact docker-compose equivalents. Some flags like --rm have no direct compose equivalent and are skipped. Always review the output before using in production.
Related Tools
Link to this tool
Copy this HTML to link to this tool from your website or blog.
<a href="https://toolboxkit.io/tools/docker-to-compose/" title="Docker Run to Compose Converter - Free Online Tool">Try Docker Run to Compose Converter on ToolboxKit.io</a>