Docker/Podman Composer — Examples
Hands-on runs: nginx and Postgres to Compose, several commands with a network, dollar signs, Compose as a start script with build and depends_on, podman.
Back to the overview: Docker/Podman Composer · Open the tool live: www.jpkc.com/tools/docker/
The manual explains both directions and every supported flag in detail. This page shows concrete conversions with input and the actual result. The tool converts live — paste the input and the result appears underneath a moment later. Tab and button names are given in their original spelling. Unless stated otherwise, the Compose project name field is empty.
Example 1: A simple nginx container (Run → Compose)
The classic — a short run command becomes a Compose file. Input in the Run → Compose tab:
docker run -d --name web -p 8080:80 nginx:alpineResult in the Docker Compose YAML field:
services:
web:
image: nginx:alpine
container_name: web
ports:
- "8080:80"Note: the -d (detach) disappears — it's implicit in Compose. The service name web comes from --name; without --name, the tool would have derived it from the image (nginx). The port mapping is quoted because older YAML readers might otherwise interpret 8080:80 as a number.
Example 2: Volumes, ports, environment, and a health check
A realistic database container with several flag types. Input:
docker run -d \
--name db \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
-p 5432:5432 \
--restart unless-stopped \
--health-cmd "pg_isready -U postgres" \
--health-interval 10s \
--memory 512m \
postgres:16Result:
services:
db:
image: postgres:16
container_name: db
restart: unless-stopped
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secret
healthcheck:
test:
- CMD-SHELL
- pg_isready -U postgres
interval: 10s
mem_limit: 512m
volumes:
pgdata:
external: trueThree things are worth a closer look: the --health-cmd becomes a test list in CMD-SHELL form, and --memory lands as mem_limit. Thanks to the tokenizer, the quoted health-check command stays a single argument. And the tool declares the named volume pgdata at the bottom as external: true — that way Compose uses the volume your docker run already created instead of making a new <project>_pgdata.
Example 3: A multi-flag command with combined short flags
Here combined flags and a trailing command come into play. Input:
docker run -it --rm -p 3000:3000 node:20 bashResult:
services:
node:
image: node:20
command:
- bash
ports:
- "3000:3000"
stdin_open: true
tty: trueA yellow note appears below the output:
--rm: Compose keeps stopped containers; use "docker compose run --rm" for one-off runs.What happens here: -it is split into -i and -t, yielding stdin_open: true and tty: true. The --rm is dropped because it has no Compose equivalent — the note tells you the alternative. The bash after the image name becomes the command, written as a list with one word per entry. Since no --name is set, the tool derives the service name from the image: node.
Example 4: The full feature set (the Example button)
The Example button in the run tab loads a small setup of three commands: a network, an nginx web server, and a Node app that share the network:
# Network shared by both containers
docker network create mynet
docker run -d \
--name webserver \
-p 8080:80 \
-v /var/www:/usr/share/nginx/html:ro \
-v nginx-logs:/var/log/nginx \
-e NGINX_HOST=example.com \
--restart unless-stopped \
--network mynet \
--label app=webserver \
--memory 256m \
--health-cmd "curl -fs http://localhost/ || exit 1" \
--health-interval 30s \
--log-opt max-size=10m \
nginx:1.27-alpine
docker run -d --name app --network mynet -w /app -v "$PWD/app:/app" \
-e NODE_ENV=production -e "GREETING=Hello, world" \
node:22-alpine node server.jsResult:
services:
webserver:
image: nginx:1.27-alpine
container_name: webserver
restart: unless-stopped
ports:
- "8080:80"
volumes:
- /var/www:/usr/share/nginx/html:ro
- nginx-logs:/var/log/nginx
environment:
- NGINX_HOST=example.com
networks:
- mynet
healthcheck:
test:
- CMD-SHELL
- curl -fs http://localhost/ || exit 1
interval: 30s
logging:
options:
max-size: 10m
labels:
- app=webserver
mem_limit: 256m
app:
image: node:22-alpine
container_name: app
command:
- node
- server.js
volumes:
- $PWD/app:/app
environment:
- NODE_ENV=production
- GREETING=Hello, world
networks:
- mynet
working_dir: /app
networks:
mynet:
name: mynet
volumes:
nginx-logs:
external: trueThe two run commands become two services in one file; the comment line is skipped. Because docker network create mynet is part of the input, the tool declares the network with name: mynet — so Compose uses exactly that network instead of a new <project>_mynet. The volume nginx-logs wasn't created anywhere, so it counts as external. The || in the health check survives because it sits inside quotes. $PWD stays a variable that Compose fills in at startup.
Example 5: The reverse direction — Compose → Run with multiple services
Switch to the Compose → Run tab, enter shop in the Compose project name field, and paste a Compose file with two services:
services:
web:
image: nginx:alpine
container_name: web
ports:
- "8080:80"
restart: unless-stopped
networks:
- mynet
depends_on:
- app
app:
image: node:20-alpine
container_name: app
restart: always
working_dir: /app
volumes:
- ./app:/app
environment:
NODE_ENV: production
PORT: "3000"
networks:
- mynet
networks:
mynet:With runtime docker, you get this script:
# Networks and volumes
docker network inspect shop_mynet >/dev/null 2>&1 || docker network create shop_mynet
# app
docker run -d \
--name app \
--restart always \
--workdir /app \
--network shop_mynet \
--network-alias app \
-v ./app:/app \
-e NODE_ENV=production \
-e PORT=3000 \
node:20-alpine
# web
docker run -d \
--name web \
--restart unless-stopped \
--network shop_mynet \
--network-alias web \
-p 8080:80 \
nginx:alpineSeveral things stand out. First, the script creates the network — docker run wouldn't — and only if it doesn't exist yet. As with Compose, it's called shop_mynet. Because web depends on app via depends_on, app starts first. Each container gets its service name as --network-alias, so web reaches the app as app, just like under Compose. The environment object is unpacked into individual -e KEY=VALUE flags. A yellow note reminds you that the relative path ./app depends on the current directory — run the script next to the Compose file. run.sh downloads the whole thing as a ready-to-run script (with #!/bin/sh and set -e) — start it with sh run.sh, or run chmod +x run.sh first.
Example 6: Generating commands for Podman
The same Compose input, but with podman selected in the Runtime switch. The network and the web service then become:
# Networks and volumes
podman network inspect shop_mynet >/dev/null 2>&1 || podman network create shop_mynet
# web
podman run -d \
--name web \
--restart unless-stopped \
--network shop_mynet \
--network-alias web \
-p 8080:80 \
nginx:alpineThe difference is the command word podman instead of docker, including for the network commands — the flags stay the same, because Podman reproduces the docker run options compatibly. Only GPU reservations are written differently for Podman (--device nvidia.com/gpu=all). Handy when you want to test an existing Compose file by hand on a Podman host. The tool remembers your runtime choice for your next visit.
Example 7: Dollar signs and variables
A $ means something different in the shell than in Compose. Input in the Run → Compose tab:
docker run -d --name shop -e 'PRICE=$5' -e "HOME_DIR=$HOME" -v "$PWD/data:/data" \
--network host myorg/shop:1.2Result:
services:
shop:
image: myorg/shop:1.2
container_name: shop
volumes:
- $PWD/data:/data
environment:
- PRICE=$$5
- HOME_DIR=$HOME
network_mode: host'$5' is in single quotes, so it's a literal dollar sign — in Compose that becomes $$5. $HOME and $PWD are variables and stay that way; Compose fills them in at startup. --network host isn't a network Compose could manage, so it becomes network_mode: host.
The reverse direction shows how the tool makes such values shell-safe again. Project name shop, input in the Compose → Run tab:
services:
shop:
image: myorg/shop:1.2
environment:
- PRICE=$$5
- HOME_DIR=$HOME
- GREETING=${GREETING:-Hello}
volumes:
- $PWD/data:/data
network_mode: hostResult:
# shop
docker run -d \
--name shop-shop-1 \
--network host \
-v "$PWD"/data:/data \
-e 'PRICE=$5' \
-e HOME_DIR="$HOME" \
-e GREETING="${GREETING:-Hello}" \
myorg/shop:1.2$$5 turns back into a literal $5 in single quotes, while the variables end up in double quotes and expand in the shell — default value included. Without container_name, the container is called shop-shop-1, just as Compose would name it.
Example 8: A Compose file with build, depends_on, and two networks
The Example button in the Compose → Run tab loads the shop project with everything that makes the reverse direction interesting — name: shop is in the file, so the project field can stay empty:
name: shop
services:
webserver:
image: nginx:1.27-alpine
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- nginx-logs:/var/log/nginx
environment:
NGINX_HOST: ${HOST:-example.com}
networks:
- front
- back
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost/ || exit 1"]
interval: 30s
retries: 3
logging:
driver: json-file
options:
max-size: 10m
depends_on:
app:
condition: service_healthy
app:
build: ./app
working_dir: /app
command: node server.js
environment:
NODE_ENV: production
PRICE_TAG: "costs $$5"
networks:
back:
aliases: [api]
deploy:
resources:
limits:
memory: 512M
cpus: "0.5"
stop_grace_period: 1m30s
networks:
front:
back:
internal: true
volumes:
nginx-logs:Result (runtime docker):
# Networks and volumes
docker network inspect shop_back >/dev/null 2>&1 || docker network create --internal shop_back
docker network inspect shop_front >/dev/null 2>&1 || docker network create shop_front
# 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.js
# webserver
docker run -d \
--name shop-webserver-1 \
--restart unless-stopped \
--network shop_front \
--network-alias webserver \
-p 8080:80 \
-v ./html:/usr/share/nginx/html:ro \
-v shop_nginx-logs:/var/log/nginx \
-e NGINX_HOST="${HOST:-example.com}" \
--log-driver json-file \
--log-opt max-size=10m \
--health-cmd 'wget -qO- http://localhost/ || exit 1' \
--health-interval 30s \
--health-retries 3 \
nginx:1.27-alpine
docker network connect --alias webserver shop_back shop-webserver-1The script shows nearly every trick of the reverse direction: build: ./app becomes a docker build -t shop-app in front of the run. The internal network back is created with --internal. deploy.resources.limits turns into --memory and --cpus, 1m30s into --stop-timeout 90, and command: node server.js follows the image. $$5 becomes a literal $5 in single quotes, ${HOST:-example.com} a shell variable with a default value. The webserver joins the first network via --network and the second via docker network connect; the named volume is called shop_nginx-logs, just as with Compose.
Two yellow notes come with it: the relative path ./html depends on the current directory, and the script doesn't check the service_healthy condition — it only starts the containers in the right order.
Going deeper: the manual for every flag, the tips & tricks for the limits and knacks. You can try everything directly in the tool.