Docker/Podman Composer — Manual
Full feature reference: both directions, every run flag and Compose key, networks, volumes, $ quoting, the start script, and the podman specifics.
Back to the overview: Docker/Podman Composer · Open the tool live: www.jpkc.com/tools/docker/
This manual describes the Docker/Podman Composer in full: what happens in each of the two conversion directions, which run flags and Compose keys are supported, how networks, volumes, and dollar signs are handled, how the generated start script is laid out, and where the limits are. The tool's interface is in English, so tab, field, and button names are given exactly as they appear on screen.
Structure: three tabs, one client-side engine
The tool has three tabs: Run → Compose, Compose → Run, and Reference. The first two are the converters; the third is a built-in lookup sheet. Both converters run entirely in the browser — the shell tokenizer, flag parser, YAML emitter, and the js-yaml parser are all JavaScript. There is no server fetch and no API; your input never leaves the browser. All four editor fields are built on the ACE editor with syntax highlighting for shell (sh) or YAML.
Conversion is live: as soon as you type or paste something, the tool recalculates after a short pause (300 ms). There's no convert button. Each input field has an Example button that loads a ready-made sample and a Clear button that empties the field.
Compose project name
Above the tabs there's an optional Compose project name field (placeholder myapp). It applies to both directions, because Compose names networks, volumes, and containers after the project — and falls back to the folder name if you don't set one:
- Run → Compose: the name goes into the file as
name:at the very top (lowercased, invalid characters become_). - Compose → Run: the tool needs the name to reproduce the identifiers Compose itself would assign —
myapp_defaultfor the default network,myapp_datafor a volumedata,myapp-web-1for the container of serviceweb. Priority: the field wins overname:in the file; if neither is set, the tool usesmyappand tells you so.
The tool stores the project name and the chosen runtime (docker/podman) in your browser's localStorage, so they're still set on your next visit. Your commands and Compose files are not stored.
Notes below the output
Below both output fields a list appears when there's something to report: red for errors (such as "No image found in: …" or a YAML syntax error with line and column), yellow for warnings — almost everything the tool couldn't translate, or could only translate with a caveat. The list is announced to screen readers. Read it after every conversion: it tells you what's missing from the result and why. A few details are dropped without a note, though; the sections on the two directions below name them.
Direction 1: Run → Compose
In the Run → Compose tab you paste one or more commands into the Docker / Podman Run Command field. The result shows up right below in the Docker Compose YAML field.
How the command is read
Before any flag is evaluated, a tokenizer splits the text following POSIX shell rules. It handles:
- Line continuations with a backslash (
\at end of line, Windows line endings included) — long multi-line commands are joined into one command. - Single quotes (
'…') as literal text, double quotes ("…") in which only\$,\`,\",\\, and the newline are escaped, and ANSI-C quoting$'…'with\n,\t,\r, and friends. - Quotes in the middle of a word:
-e MSG="a b"yields the single argumentMSG=a b. - Comments (
#at the start of a word, up to the end of the line) and command separators: a newline,;,&&, and||each end a command. - Pipes and redirections (
|,&,<,>): the rest of the line is ignored, with a note. - Command substitution (
$(…), backticks) is kept as text, with a note — it only runs in a real shell.
Both spellings --flag value and --flag=value are supported. Combined short flags are split up even when the last one takes a value: -it, -dp 80:80, and -p8080:80 all work. -- ends the flags; the next word is the image. Boolean flags with =false (such as --init=false) are not set. The first argument that isn't a flag is the image; everything after it becomes the command.
Unknown flags are dropped, but not silently: the tool reports "Unknown flag … — dropped" and warns you if the following word may have been read as the image as a result.
Which commands are recognized
The input field may contain several commands — each run becomes its own service in one Compose file. Recognized are:
- The programs
docker,podman, andnerdctl(with or without a path); a leadingsudoand variable assignments likeFOO=1 docker run …are skipped. Global options before the subcommand (--context,-H…) are ignored with a note. run,create,container run, andcontainer create— one service each.network createandvolume create— they become top-level definitions (see Networks and volumes).- All other subcommands (
docker ps,docker compose …) and non-Docker commands are skipped with a note.
The service name comes from --name if set (lowercased, invalid characters become _); without --name it's derived from the image (last path segment without tag or digest, e.g. nginx:1.27-alpine → nginx). If a name occurs twice, the tool appends _2, _3, and so on.
Supported value flags (flag → Compose key)
These flags take a value and map to a Compose key. Flags given multiple times (ports, volumes, env …) end up as a YAML list or map.
- Identity:
--name→container_name;--hostname/-h→hostname;--domainname→domainname;--platform→platform;--mac-address→mac_address - Image & pull:
--pull→pull_policy;--entrypoint→entrypoint(as a one-element list);--runtime→runtime;--isolation→isolation - Ports & expose:
-p/--publish→ports;--expose→expose - Volumes:
-v/--volume→volumes(short syntax);--mount→volumesin long syntax (type,source,target,read_only,bind.propagation,volume.nocopy,volume.subpath,tmpfs.size/mode…; withtype=bindthe tool also addsbind.create_host_path: false, because--mountdoesn't create missing host directories);--tmpfs→tmpfs;--volumes-from→volumes_from - Environment:
-e/--env→environment;--env-file→env_file - Network:
--network/--net→networksornetwork_mode;--network-alias/--net-alias→aliases;--ip/--ip6→ipv4_address/ipv6_address;--link-local-ip→link_local_ips;--dns→dns;--dns-search→dns_search;--dns-opt/--dns-option→dns_opt;--add-host→extra_hosts;--link→links - Lifecycle & signals:
--restart→restart;--stop-signal→stop_signal;--stop-timeout→stop_grace_period(30becomes30s) - Runtime:
-u/--user→user;-w/--workdir→working_dir;--ipc→ipc;--pid→pid;--uts→uts;--userns→userns_mode;--cgroupns→cgroup;--cgroup-parent→cgroup_parent - Labels & annotations:
-l/--label→labels;--label-file→label_file;--annotation→annotations - Memory:
-m/--memory→mem_limit;--memory-swap→memswap_limit;--memory-reservation→mem_reservation;--memory-swappiness→mem_swappiness;--shm-size→shm_size - CPU:
--cpus→cpus;-c/--cpu-shares→cpu_shares;--cpuset-cpus→cpuset;--cpu-period,--cpu-quota,--cpu-rt-period,--cpu-rt-runtime,--cpu-count,--cpu-percent→ the keys of the same name with underscores - Other limits:
--pids-limit→pids_limit;--oom-score-adj→oom_score_adj;--ulimit→ulimits(nofile=1024:2048becomessoft/hard);--sysctl→sysctls;--storage-opt→storage_opt - Block I/O:
--blkio-weight,--blkio-weight-device,--device-read-bps,--device-read-iops,--device-write-bps,--device-write-iops→blkio_config - Devices & security:
--device→devices;--device-cgroup-rule→device_cgroup_rules;--gpus→deploy.resources.reservations.devices(drivernvidia, capabilitygpu,countordevice_ids; in the form--gpus "device=…,capabilities=…"the tool only readsdevice,count,capabilities, anddriver, other keys are dropped without a note);--cap-add→cap_add;--cap-drop→cap_drop;--security-opt→security_opt;--group-add→group_add - Logging:
--log-driver→logging.driver;--log-opt→logging.options(askey: value) - Health check:
--health-cmd→healthcheck.test(as[CMD-SHELL, …]);--health-interval,--health-timeout,--health-retries,--health-start-period,--health-start-interval→ the matchinghealthcheck.*fields
Supported boolean flags (no value)
-t/--tty→tty: true-i/--interactive→stdin_open: true--privileged→privileged: true--read-only→read_only: true--init→init: true--oom-kill-disable→oom_kill_disable: true--no-healthcheck→healthcheck.disable: true-d/--detach→ dropped (implicit in Compose)
Flags without a Compose equivalent
Some run options simply have no place in Compose. The tool drops them and tells you which ones in the notes list:
--rm— Compose keeps stopped containers; for one-off runs the note points you todocker compose run --rm.-P/--publish-all— Compose doesn't know it; list the ports underportsinstead.- No Compose key ("has no Compose equivalent — dropped"):
--cidfile,--cpuset-mems,--io-maxbandwidth,--io-maxiops,--kernel-memory,--use-api-socket,--volume-driver, plus the Podman options--pod,--systemd,--secret, and--tz. - Skipped silently, because they only affect how the container is started from a terminal:
-d,-a/--attach,--detach-keys,--sig-proxy,-q/--quiet,--disable-content-trust,--help, and the Podman options--replaceand--http-proxy.
Networks and volumes
A docker run uses networks and named volumes that exist outside of Compose. To keep the generated file valid — and to stop Compose from creating new, project-prefixed objects instead — the tool declares them at the top level:
--network NAME→networks: [NAME]on the service andNAME: {external: true}under the top-levelnetworks:.-v NAME:/pathand--mount type=volume,src=NAME,…→NAME: {external: true}under the top-levelvolumes:. Bind mounts (paths starting with/,./,~,$, or a Windows drive letter) are left alone.- If the input also contains
docker network create NAMEordocker volume create NAME, the tool writes a real definition withname: NAME(the exact name Docker created it under) instead ofexternal: true. For networks it carries over--driver,--opt,--label,--subnet,--gateway,--ip-range,--ipam-driver,--internal,--attachable, and--ipv6; for volumes--driver,--opt,--label, and--name.--scope,--aux-address, and--ipv4are read along with their value but neither carried over nor reported; other unknown options get the note "network create: … is not mapped." A created network or volume that norunuses also ends up as a definition in the file. --network host,none,bridge,default, andcontainer:NAMEbecomenetwork_mode.- Network options (
--network-alias,--ip,--ip6,--link-local-ip) belong to the first named network; the tool then writesnetworks:in map form withaliases,ipv4_address, and so on. The long form--network name=backend,alias=jobs,ip=…is read as well; unknown keys in it are skipped without a note. Without a named network, the tool drops these options with a note.
One difference remains by design: docker run without --network lands in the default bridge network, whereas Compose creates its own network <project>_default.
The YAML output format
If a project name is set, the output starts with name:, followed by the services: block and then the top-level networks: and volumes: blocks. Within a service, the common keys follow a fixed order: image, container_name, restart, entrypoint, command, ports, expose, volumes, tmpfs, environment, env_file, networks, network_mode, hostname, user, working_dir, healthcheck, logging; all other keys follow in the order of the flags. command is written as a list, one word per entry. No version: line is generated (modern Compose spec, where the field is obsolete).
Values are put in double quotes only when a YAML reader would otherwise interpret them differently. The tool checks this with a round trip through js-yaml, plus the YAML 1.1 pitfalls: port mappings like "8080:80" (older readers parse 22:22 as a base-60 number), pure numbers meant as strings like "3600", and the old booleans yes, no, on, off, y, n.
The result field carries an "editable" badge and really is editable: right inside the tool you can add what a run command can't tell the converter — depends_on or a build: block, for example. Copy copies the YAML; compose.yaml downloads it as the file compose.yaml (a locally generated blob). If you change the input afterwards, the live conversion overwrites your edits in the output field.
Direction 2: Compose → Run
In the Compose → Run tab you paste a Compose file into the Docker Compose YAML field. Below it, the Generated Run Commands field shows a ready-made shell script. This output field is read-only; Copy copies its contents, run.sh downloads it as the file run.sh — with #!/bin/sh and set -e prepended, so the script stops at the first error.
Choosing docker or podman
In the footer of the input field there's a Runtime radio switch with docker (default) and podman. It sets the command word for every generated line — run, build, network create, network connect, and volume create. The flags themselves are identical, because Podman reproduces the docker run options in a largely compatible way. GPUs are the exception: for Docker the tool writes --gpus all or --gpus '"device=0,1"', for Podman --device nvidia.com/gpu=all (CDI notation).
Structure of the generated script
The script is ordered so that it runs from top to bottom:
# Networks and volumes— networks come first, becausedocker rundoesn't create them. Each line is idempotent:docker network inspect NAME >/dev/null 2>&1 || docker network create … NAME. The tool carries overdriver,driver_opts,internal,attachable,enable_ipv6,ipam(driver,subnet,gateway,ip_range), andlabels. External networks only appear as a comment ("must already exist"). Volumes withdriver,driver_opts, orlabelsare created withdocker volume create; plain volumes are created bydocker runitself.- One block per service, starting with the comment line
# <service>, preceded by adocker buildif the service hasbuild:, thendocker run -dwith one flag per line (backslash continuations) and the image plus command on the last line. docker network connectfor every additional network of a service, right after itsrun.
The services appear in depends_on order: dependencies first. If there's a cycle, the tool warns you.
What is read and generated
The YAML parser (js-yaml) accepts a complete Compose file with services: as well as a bare services map, as long as every entry has image or build. Service, network, and volume names must follow the Compose rules (letters, digits, _, ., -, starting with a letter or digit) — otherwise the tool reports an error, and a service with an invalid name is left out.
Every command starts with <runtime> run -d and --name: taken from container_name, otherwise — as in Compose — <project>-<service>-1. After that, the Compose keys are translated back into run flags:
- Image & build:
image;build(string or object) →docker build -t <image> [-f dockerfile] [--target …] [--build-arg …] <context>before therun— withoutimage, the image is called<project>-<service>. If bothimageandbuildare missing, the tool reports an error. - Restart:
restart, or failing thatdeploy.restart_policy(none→no,on-failurewithmax_attempts→on-failure:N, anything elsealways). - Simple values:
hostname,domainname,platform,user(→--user),working_dir(→--workdir),mac_address,ipc,pid,uts,userns_mode(→--userns),cgroup(→--cgroupns),cgroup_parent,isolation,runtime,shm_size,stop_signal,pull_policy(→--pull,if_not_present→missing,buildis dropped) - Resources:
mem_limit(→--memory),mem_reservation,memswap_limit(→--memory-swap),mem_swappiness,cpus,cpu_shares,cpu_period,cpu_quota,cpu_rt_period,cpu_rt_runtime,cpuset(→--cpuset-cpus),cpu_count,cpu_percent,oom_score_adj,pids_limit,oom_kill_disable,blkio_config,sysctls,storage_opt,ulimits; plusdeploy.resources.limits(memory,cpus,pids) anddeploy.resources.reservations.memorywhen the service-level keys are missing; GPU reservations andgpus→--gpusor Podman CDI - Network:
network_mode(service:NAME→container:<container name>); otherwisenetworks(list or map) — the first network via--networkwith--network-alias <service>plus your ownaliases,--ip,--ip6,--link-local-ip, every further one vianetwork connect --alias …. Withoutnetworks, the container joins the network<project>_default, just as with Compose. Alsolinks/external_links(→--linkwith container names),extra_hosts(list,host=ipincluded, or map →--add-host),dns,dns_search,dns_opt(→--dns-option) - Ports:
portsas a short string or long object withhost_ip(IPv6 in square brackets),published,target,protocol;expose - Volumes: short syntax (named volumes get the project prefix,
<project>_<name>); long syntax →--mount type=…,source=…,target=…withreadonly,bind-propagation,volume-nocopy,volume-subpath,consistency— a bind mount with an absolute or relative path that Compose would be allowed to create becomes-vinstead, because only-vcreates missing host directories (withbind.propagationset, it stays--mount);type: tmpfs→--tmpfs target:size=…,mode=…;tmpfs;volumes_from(service → container name). Host paths starting with~/are written as"$HOME"/…, because the shell only expands~unquoted at the start of a word. That works for secrets/configs, and in the long syntax only when the mount becomes-v: if a bind mount stays--mount(withbind.create_host_path: falseorbind.propagation), the~ends up unexpanded in the script. In the short syntax the tool currently doubles the path (~/data:/data→-v "$HOME"/data/data:/data). In both cases, write$HOME/…or an absolute path instead. - Secrets & configs: with
file:→ a read-only bind mount (-v file:target:ro), secrets to/run/secrets/<target>, configs to/<target>(an absolutetargetis used as is); withouttarget, the target is the name of the secret or config - Environment:
env_file(string, list, or object withpath/required),environment(list or map →-e KEY=VALUE) - Labels & security:
labels,label_file,annotations,cap_add,cap_drop,security_opt,group_add,device_cgroup_rules,devices(string or object),privileged,read_only,init,stdin_open(→-i),tty(→-t) - Logging:
logging.driver→--log-driver,logging.options→ one--log-opt key=valueeach - Health check:
testas[CMD-SHELL, …]or a string →--health-cmd;[CMD, …]→ the words shell-quoted one by one, with a note thatdocker runonly knows the shell form and the image needs/bin/sh;disable: trueorNONE→--no-healthcheck; plusinterval,timeout,start_period,start_interval,retries - Stopping:
stop_grace_period→--stop-timeoutin whole seconds, rounded up (1m30s→90) - Entrypoint & command:
--entrypointtakes a single program only. Soentrypoint: [sh, -c]becomes--entrypoint sh, and the-cmoves in front of the command. Strings are split into words following shell rules, as Compose does.
For the app service from the Example button (project shop, build: ./app, deploy.resources.limits, network back with alias api, stop_grace_period: 1m30s) the tool produces this block:
# app
docker build -t shop-app ./app
docker run -d \
--name shop-app-1 \
--workdir /app \
--memory 512M \
--cpus 0.5 \
--network shop_back \
--network-alias app \
--network-alias api \
-e NODE_ENV=production \
-e 'PRICE_TAG=costs $5' \
--stop-timeout 90 \
shop-app node server.jsWhat is NOT translated on Compose import
Anything without a docker run equivalent is missing from the script. The tool reports most of it in the notes list:
depends_onconditions such asservice_healthy— the script only keeps the order; it doesn't wait for a healthy container.profiles— the command always runs.deploy.replicasandscalegreater than 1 —docker runstarts exactly one container.deploy.resources.reservations.cpusand device reservations without thegpucapability.- Secrets/configs from
environment:, withcontent:, or external (Swarm) — they can't be mounted as a file. env_filewithrequired: falseis still passed as--env-file;docker runaborts if the file is missing.- Relative paths (
./html) — they resolve against the current directory; run the script next to the Compose file. - All other service keys show up as
"<key>" is not converted.—x-…extension fields are skipped silently.
Sub-keys the tool doesn't read under a known key, on the other hand, are dropped without a note: the check for unknown keys only covers the top level of a service. From build the tool reads only context, dockerfile, target, and args — so build.cache_from or build.ssh vanish silently. From deploy it only evaluates restart_policy, replicas, and resources; deploy.placement, for example, goes missing without a report. For a service's networks it carries over only aliases, ipv4_address, ipv6_address, and link_local_ips (the latter only for the first network; for further networks it is dropped); a network's priority, mac_address, or driver_opts are lost. With files like that, compare input and output yourself.
Dollar signs and variables
The shell and Compose treat $ differently: in the shell $VAR expands and '$VAR' doesn't. In Compose, $VAR and ${VAR} are interpolated from the environment or the .env file, and $$ stands for a literal $. The tool translates in both directions so that a variable stays a variable and a literal stays a literal:
- Run → Compose:
-e "HOME_DIR=$HOME"becomesHOME_DIR=$HOME— Compose fills in the value at startup. A literal dollar sign, as in-e 'PRICE=$5', becomesPRICE=$$5. - Compose → Run: literal parts go into single quotes, and
$$turns back into$.${NAME},$NAME, and the forms with a default value (${NAME:-value},${NAME-value},${NAME:?…},${NAME:+…}) become a shell expansion in double quotes, e.g.-e NGINX_HOST="${HOST:-example.com}".
For security reasons, the tool only lets simple default values through to expansion (letters, digits, _ . , : / @ % + = -, and spaces). Everything else — quotes, $(…), backticks, backslashes — stays literal text in single quotes. That way a crafted Compose file can't execute code through the generated script. Keep in mind: Compose also reads variables from .env, the script doesn't — export them in your shell first.
The Reference tab
The third tab is a built-in lookup sheet and needs no input:
- Flag Reference: docker run → Compose — a table grouped by topic (Container Identity, Image & Command, Ports & Volumes, Environment, Network, Lifecycle, Runtime Flags, Security, Resources, Logging, Healthcheck, Labels) with the
runflag, the Compose key, and notes, for instance on--mount,--network host,--gpus, anddeploy.resources. - Compose-only Features —
depends_on(order yes, conditions no),build(becomesdocker build),profiles/replicas/scale(ignored with a note),secrets/configs(file variants as a mount), and interpolation (${VAR:-default}stays a shell variable). - Restart Policies —
no(default, never restart),always,unless-stopped,on-failure, andon-failure:3(max. 3 retries). - Tips — line continuation with
\, combined flags, severalruncommands as several services, checking withdocker compose config, and quoting ports.
Limits — in brief
- One
run= one service. Several commands produce several services in one file; options without a Compose equivalent (e.g.--rm,-P,--cidfile) and unknown flags are dropped, mostly with a note — the details dropped without one are listed under "Direction 1: Run → Compose". - Some Compose-only constructs get lost when you convert from Compose to
run:depends_onconditions,profiles,replicas/scale, secrets without a file, and unsupported keys. - No
docker/podmanin the background. The tool doesn't run or check anything against a real engine — it's a pure text-to-text translator. Validate the generated file withdocker compose configbefore you use it.
For the introduction and target audiences, see the overview page; for concrete walkthroughs, the examples. You can try everything directly in the tool.