Docker/Podman Composer — Examples

Hands-on conversions: nginx to Compose, volumes/ports/env, a multi-flag command, the Compose→Run reverse direction, and a podman example.

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 interface is in English, so tab and button names are given in their original spelling.

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:alpine

After clicking Convert to YAML:

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).

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:16

Result:

services:
  db:
    image: postgres:16
    container_name: db
    restart: unless-stopped
    ports:
      - 5432:5432
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=secret
    mem_limit: 512m
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -U postgres
      interval: 10s

Two things are worth noting: the --health-cmd becomes a test list in CMD-SHELL form, and --memory lands as mem_limit. The quoted health-check command stays a single argument thanks to the tokenizer.

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 bash

Result:

services:
  node:
    image: node:20
    tty: true
    stdin_open: true
    ports:
      - 3000:3000
    command: bash

What happens here: -it is split into -i and -t, yielding stdin_open: true and tty: true. The --rm is discarded (it has no Compose equivalent), and a -d would have vanished too. The bash after the image name becomes the command. 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 an nginx web server with almost everything at once:

docker run -d \
  -p 8080:80 \
  -p 443:443 \
  -v /var/www:/usr/share/nginx/html:ro \
  -v nginx-logs:/var/log/nginx \
  -e NGINX_HOST=example.com \
  -e NGINX_PORT=80 \
  --name webserver \
  --restart unless-stopped \
  --network mynet \
  --label app=webserver \
  --label env=production \
  --memory 256m \
  --health-cmd "curl -fs http://localhost/" \
  --health-interval 30s \
  --health-retries 3 \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx:1.27-alpine

Result:

services:
  webserver:
    image: nginx:1.27-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - 8080:80
      - 443:443
    volumes:
      - /var/www:/usr/share/nginx/html:ro
      - nginx-logs:/var/log/nginx
    environment:
      - NGINX_HOST=example.com
      - NGINX_PORT=80
    networks:
      - mynet
    labels:
      - app=webserver
      - env=production
    mem_limit: 256m
    logging:
      driver: json-file
      options:
        max-size: 10m
        max-file: "3"
    healthcheck:
      test:
        - CMD-SHELL
        - curl -fs http://localhost/
      interval: 30s
      retries: 3

Flags given multiple times (-p, -v, -e, --label, --log-opt) collect into lists or the logging.options block. Note the "3" for max-file: the serializer quotes a pure number meant as a string so YAML doesn't read it as an integer.

Example 5: The reverse direction — Compose → Run with multiple services

Switch to the Compose → Run tab and paste a Compose file with two services:

services:
  web:
    image: nginx:alpine
    container_name: web
    ports:
      - "8080:80"
    restart: unless-stopped
  app:
    image: node:20-alpine
    container_name: app
    restart: always
    working_dir: /app
    volumes:
      - ./app:/app
    environment:
      NODE_ENV: production
      PORT: "3000"
    ports:
      - "3000:3000"
    networks:
      - mynet
    depends_on:
      - web

After Convert to Commands (Runtime: docker), each service gets its own command:

# web
docker run -d \
  --name web \
  --restart unless-stopped \
  -p 8080:80 \
  nginx:alpine

# app
docker run -d \
  --name app \
  --restart always \
  --workdir /app \
  -p 3000:3000 \
  -v ./app:/app \
  -e NODE_ENV=production \
  -e PORT=3000 \
  --network mynet \
  node:20-alpine

Important: the depends_on: [web] does not appear in the generated command — docker run has no concept of start dependencies, so it's dropped. A top-level networks: definition with driver options would be ignored too; only the service reference mynet becomes --network mynet. The environment object is unpacked into individual -e KEY=VALUE flags.

Example 6: Generating commands for Podman

The same Compose input, but with podman selected in the Runtime switch. The web service then becomes:

# web
podman run -d \
  --name web \
  --restart unless-stopped \
  -p 8080:80 \
  nginx:alpine

The only difference is the command word podman instead of docker — the flags stay the same, because Podman reproduces the docker run options compatibly. Handy when you want to test an existing Compose file by hand on a Podman host.


Going deeper: the manual for every flag, the tips & tricks for the limits and knacks. You can try everything directly in the tool.