Docker Run to Compose
Convert docker run commands to docker-compose.yml instantly. Supports ports, volumes, environment variables, networks, restart policies, and more.
- Runs in your browser
- Your data never leaves your browser
- Free · No Sign-Up
Scan with WeChat to share this tool
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.
Common Use Cases
- Migration: Turn one-off docker run commands into reproducible compose configurations.
- Documentation: Convert run commands from README files or docs into compose format for your team.
- Multi-service setup: Generate individual service blocks and combine them into a single compose file.
- CI/CD: Replace fragile shell scripts of docker run commands with a declarative compose file.
Example: A PostgreSQL Container
Input:
docker run -d \
--name postgres \
-e POSTGRES_DB=myapp \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
--restart unless-stopped \
postgres:16-alpine
Output:
services:
postgres:
image: "postgres:16-alpine"
ports:
- "5432:5432"
volumes:
- "pgdata:/var/lib/postgresql/data"
environment:
- "POSTGRES_DB=myapp"
- "POSTGRES_USER=admin"
- "POSTGRES_PASSWORD=secret"
restart: unless-stopped
volumes:
pgdata:
pgdata is a named volume, so it is declared under the top-level volumes: key. Without that block Compose rejects the file: docker compose config reports service "postgres" refers to undefined volume pgdata. -d is dropped because you detach with docker compose up -d. --name becomes the service key; add container_name: yourself if scripts depend on the exact container name.
Example: Ports, Limits, and an Existing Network
docker run --rm -it --network app-net -p 127.0.0.1:8080:80/tcp \
--restart on-failure:3 --memory 512m --cpus 1.5 --cap-add NET_ADMIN \
ghcr.io/acme/web:1.2 npm start
services:
acme_web:
image: "ghcr.io/acme/web:1.2"
ports:
- "127.0.0.1:8080:80/tcp"
networks:
- app-net
restart: "on-failure:3"
cap_add:
- NET_ADMIN
stdin_open: true
tty: true
deploy:
resources:
limits:
memory: 512m
cpus: "1.5"
command: ["npm", "start"]
networks:
app-net:
external: true
With no --name, the service key comes from the image (ghcr.io/acme/web:1.2 → acme_web). -it is the short options -i and -t together; they become stdin_open and tty. --rm is dropped. --network becomes an external network, so it must already exist (docker network create app-net). --network host, none, bridge, and container:NAME become network_mode: instead.
Example: Options Without a Compose Key
docker run -d --name web --env-file .env --entrypoint /bin/sh -u 1000:1000 -w /app --gpus all nginx:1.27 -c "nginx -g 'daemon off;'"
# Not converted: --gpus all
services:
web:
image: "nginx:1.27"
entrypoint: ["/bin/sh"]
env_file:
- .env
user: "1000:1000"
working_dir: /app
command: ["-c", "nginx -g 'daemon off;'"]
--entrypoint takes one executable in docker run, so it becomes a one-item list; the arguments after the image are the command. --gpus has no single Compose key (GPUs are reserved under deploy.resources.reservations.devices), so the converter reads its value, keeps the image name right, and lists it in the first-line comment and the status line.
Every output above passes docker compose config (Docker Compose 5.1); the tool’s test suite runs that check on each example.
Flag Mapping
| docker run | docker-compose.yml |
|---|---|
-p, --publish | ports: |
-v, --volume | volumes:; named volumes also in the top-level volumes: |
-e, --env, --env-file | environment:, env_file: |
--network | networks: plus a top-level external: true network; host, none, bridge, container:… become network_mode: |
--entrypoint | entrypoint: (one-item list) |
-u, -w, -h, --domainname | user:, working_dir:, hostname:, domainname: |
--restart, --pull, --platform | restart:, pull_policy:, platform: |
-l, --label, --annotation | labels:, annotations: |
--add-host, --dns, --dns-option, --dns-search, --link, --expose | extra_hosts:, dns:, dns_opt:, dns_search:, links:, expose: |
--cap-add, --cap-drop, --privileged, --security-opt, --device | cap_add:, cap_drop:, privileged:, security_opt:, devices: |
--init, --read-only, -i, -t | init:, read_only:, stdin_open:, tty: |
--tmpfs, --volumes-from, --shm-size, --sysctl | tmpfs:, volumes_from:, shm_size:, sysctls: |
--memory, -m, --cpus | deploy.resources.limits |
--memory-reservation, --memory-swap, --cpu-shares, --cpuset-cpus, --pids-limit | mem_reservation:, memswap_limit:, cpu_shares:, cpuset:, pids_limit: |
--pid, --ipc, --uts, --userns, --cgroupns, --stop-signal | pid:, ipc:, uts:, userns_mode:, cgroup:, stop_signal: |
--name | service key |
-d, --rm, -q, --detach-keys | dropped |
| image and the arguments after it | image: and command: (JSON array) |
Options can be written as --flag value, --flag=value, -p 80:80, -p80:80, or grouped (-dit, -dp 80:80), as the Docker CLI accepts.
Options to Add by Hand
These are read but not converted, because the Compose key has a different shape (services reference):
| docker run | Compose key |
|---|---|
--health-cmd "curl -f http://localhost/" and the other --health-* | healthcheck: { test: ["CMD-SHELL", "curl -f http://localhost/"] } |
--log-driver, --log-opt | logging: { driver: …, options: { … } } |
--network-alias api, --ip | networks: { app-net: { aliases: [api], ipv4_address: … } } |
--mount | the long syntax of volumes: (type, source, target) |
--gpus | deploy.resources.reservations.devices |
--ulimit nofile=1024:2048 | ulimits: { nofile: { soft: 1024, hard: 2048 } } |
--stop-timeout 30 | stop_grace_period: 30s |
Limits
- One
docker runcommand per conversion. For several containers, convert each one and put the service blocks under a singleservices:key. docker runanddocker container runare recognised;podman runis not.- Shell features such as
$(...),$VAR, and&&are kept as literal text. Expand variables before converting, or use Compose interpolation in the result. - Relative bind paths such as
./dataare relative to the compose file’s directory in Compose, and to the current directory indocker run.
FAQ
What docker run flags are supported?
It reads every option that docker run accepts (Docker CLI 29.4). About 60 of them have a Compose key and are converted: ports, volumes, environment, env_file, entrypoint, user, working_dir, networks, restart, labels, extra_hosts, cap_add, devices, tmpfs, and more; see the mapping table. The rest are listed as not converted.
What compose file version is generated?
It writes no version key. The Compose Specification treats the top-level version field as obsolete, and Docker Compose v2 only prints a warning when it is present.
How is the service name derived?
If you use --name in your docker run command, that value is used as the service name (no container_name is written). Otherwise, the service name is derived from the image name by stripping the registry prefix and tag, and replacing slashes with underscores.
Does it support multi-line docker run commands?
Yes. Paste commands with backslash line continuations and the converter handles them correctly.
What happens to options without a Compose key?
Their value is still read, so the image name stays correct. They are listed in the status line and in a comment on the first line of the file (for example # Not converted: --gpus all), so you can add the matching Compose settings by hand. An option that docker run does not know is an error, as it is in docker run.
Are named volumes declared?
Yes. A -v source that is a name, such as pgdata:/var/lib/postgresql/data, is added to a top-level volumes: section. Paths that start with /, ., or ~ are bind mounts and are not declared.