Docker/Podman Composer — Anwendungsbeispiele
Praxisnahe Durchläufe: nginx und Postgres nach Compose, mehrere Befehle samt Netz, Dollarzeichen, Compose als Startskript mit build und depends_on, podman.
Zurück zur Übersicht: Docker/Podman Composer · Tool live öffnen: www.jpkc.com/tools/docker/
Das Manual erklärt beide Richtungen und alle unterstützten Flags im Detail. Diese Seite zeigt konkrete Konvertierungen mit Eingabe und tatsächlichem Ergebnis. Das Tool wandelt live um — du fügst die Eingabe ein, das Ergebnis steht nach einem Augenblick darunter. Die Oberfläche ist auf Englisch, Tab- und Button-Namen stehen deshalb im Original. Wenn nicht anders angegeben, ist das Feld Compose project name leer.
Beispiel 1: Einfacher nginx-Container (Run → Compose)
Der Klassiker — ein kurzer Run-Befehl wird zur Compose-Datei. Eingabe im Tab Run → Compose:
docker run -d --name web -p 8080:80 nginx:alpineErgebnis im Feld Docker Compose YAML:
services:
web:
image: nginx:alpine
container_name: web
ports:
- "8080:80"Beachte: Das -d (detach) verschwindet — in Compose ist das implizit. Der Service-Name web kommt aus --name; ohne --name hätte das Tool ihn aus dem Image abgeleitet (nginx). Die Port-Angabe steht in Anführungszeichen, weil ältere YAML-Leser 8080:80 sonst als Zahl deuten könnten.
Beispiel 2: Volumes, Ports, Environment und Healthcheck
Ein realistischer Datenbank-Container mit mehreren Flag-Typen. Eingabe:
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:16Ergebnis:
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: trueDrei Dinge lohnen die Aufmerksamkeit: Der --health-cmd wird zu einer test-Liste in der CMD-SHELL-Form, und --memory landet als mem_limit. Der per Anführungszeichen geklammerte Healthcheck-Befehl bleibt dank Tokenizer als ein Argument erhalten. Und das benannte Volume pgdata deklariert das Tool unten als external: true — Compose nutzt so das Volume, das dein docker run schon angelegt hat, statt ein neues <projekt>_pgdata zu erzeugen.
Beispiel 3: Multi-Flag-Befehl mit kombinierten Kurz-Flags
Hier kommen kombinierte Flags und ein nachgestelltes Kommando ins Spiel. Eingabe:
docker run -it --rm -p 3000:3000 node:20 bashErgebnis:
services:
node:
image: node:20
command:
- bash
ports:
- "3000:3000"
stdin_open: true
tty: trueUnter der Ausgabe erscheint dazu ein gelber Hinweis:
--rm: Compose keeps stopped containers; use "docker compose run --rm" for one-off runs.Was hier passiert: -it wird in -i und -t zerlegt und ergibt stdin_open: true und tty: true. Das --rm fällt weg, weil es keine Compose-Entsprechung hat — der Hinweis nennt dir die Alternative. Das bash nach dem Image-Namen wird zum command, als Liste mit einem Wort pro Eintrag. Da kein --name gesetzt ist, leitet das Tool den Service-Namen aus dem Image ab: node.
Beispiel 4: Voller Funktionsumfang (der Example-Knopf)
Der Example-Knopf im Run-Tab lädt ein kleines Setup aus drei Befehlen: ein Netz, einen nginx-Webserver und eine Node-App, die sich das Netz teilen:
# 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.jsErgebnis:
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: trueDie zwei run-Befehle werden zu zwei Services in einer Datei; die Kommentarzeile überspringt das Tool. Weil docker network create mynet in der Eingabe steht, deklariert das Tool das Netz mit name: mynet — Compose verwendet damit genau dieses Netz statt eines neuen <projekt>_mynet. Das Volume nginx-logs wurde nirgends angelegt und gilt deshalb als external. Das || im Healthcheck bleibt erhalten, weil es in Anführungszeichen steht. $PWD bleibt eine Variable, die Compose beim Start einsetzt.
Beispiel 5: Die Gegenrichtung — Compose → Run mit mehreren Services
Wechsle in den Tab Compose → Run, trag im Feld Compose project name shop ein und füge eine Compose-Datei mit zwei Services ein:
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:Mit Runtime docker entsteht dieses Skript:
# 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:alpineMehrere Dinge fallen auf: Zuerst legt das Skript das Netz an — docker run würde das nicht tun —, und zwar nur, wenn es noch nicht existiert. Es heißt wie bei Compose shop_mynet. Weil web per depends_on auf app wartet, startet app zuerst. Jeder Container bekommt seinen Service-Namen als --network-alias, sodass web die App wie unter Compose als app erreicht. Das environment-Objekt wird zu einzelnen -e KEY=VALUE-Flags aufgelöst. Ein gelber Hinweis erinnert daran, dass der relative Pfad ./app vom aktuellen Verzeichnis abhängt — führ das Skript neben der Compose-Datei aus. Über run.sh lädst du das Ganze als lauffähiges Skript (mit #!/bin/sh und set -e) herunter — starte es mit sh run.sh oder nach chmod +x run.sh direkt.
Beispiel 6: Befehle für Podman erzeugen
Dieselbe Compose-Eingabe, aber im Runtime-Schalter podman gewählt. Aus dem Netz und dem web-Service wird dann:
# 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:alpineDer Unterschied ist das Befehlswort podman statt docker, auch für die Netzbefehle — die Flags bleiben gleich, weil Podman die docker run-Optionen kompatibel nachbildet. Nur GPU-Reservierungen schreibt das Tool für Podman anders (--device nvidia.com/gpu=all). Praktisch, wenn du eine vorhandene Compose-Datei auf einem Podman-Host von Hand testen willst. Die Runtime-Wahl merkt sich das Tool für deinen nächsten Besuch.
Beispiel 7: Dollarzeichen und Variablen
Ein $ bedeutet in der Shell etwas anderes als in Compose. Eingabe im Tab Run → Compose:
docker run -d --name shop -e 'PRICE=$5' -e "HOME_DIR=$HOME" -v "$PWD/data:/data" \
--network host myorg/shop:1.2Ergebnis:
services:
shop:
image: myorg/shop:1.2
container_name: shop
volumes:
- $PWD/data:/data
environment:
- PRICE=$$5
- HOME_DIR=$HOME
network_mode: host'$5' steht in einfachen Anführungszeichen, ist also ein wörtliches Dollarzeichen — in Compose wird daraus $$5. $HOME und $PWD sind Variablen und bleiben es; Compose setzt sie beim Start ein. --network host ist kein Netz, das Compose verwalten könnte, und wird deshalb zu network_mode: host.
Die Gegenrichtung zeigt, wie das Tool solche Werte wieder shell-sicher macht. Projektname shop, Eingabe im Tab Compose → Run:
services:
shop:
image: myorg/shop:1.2
environment:
- PRICE=$$5
- HOME_DIR=$HOME
- GREETING=${GREETING:-Hello}
volumes:
- $PWD/data:/data
network_mode: hostErgebnis:
# 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 wird wieder zum wörtlichen $5 in einfachen Anführungszeichen, die Variablen landen in doppelten Anführungszeichen und expandieren in der Shell — samt Vorgabewert. Ohne container_name heißt der Container wie bei Compose shop-shop-1.
Beispiel 8: Compose-Datei mit build, depends_on und zwei Netzen
Der Example-Knopf im Tab Compose → Run lädt das Projekt shop mit allem, was die Gegenrichtung interessant macht — name: shop steht in der Datei, das Projektfeld kann leer bleiben:
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:Ergebnis (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-1Das Skript zeigt fast alle Kniffe der Gegenrichtung: build: ./app wird zu einem docker build -t shop-app vor dem run. Das interne Netz back entsteht mit --internal. deploy.resources.limits landet als --memory und --cpus, 1m30s als --stop-timeout 90, und command: node server.js steht nach dem Image. $$5 wird zum wörtlichen $5 in einfachen Anführungszeichen, ${HOST:-example.com} zur Shell-Variable mit Vorgabewert. Der webserver hängt per --network am ersten Netz und per docker network connect am zweiten; das benannte Volume heißt wie bei Compose shop_nginx-logs.
Zwei gelbe Hinweise gehören dazu: Der relative Pfad ./html hängt vom aktuellen Verzeichnis ab, und die Bedingung service_healthy prüft das Skript nicht — es startet die Container nur in der richtigen Reihenfolge.
Noch tiefer: das Manual für jedes Flag, die Tipps & Tricks für die Grenzen und Kniffe. Ausprobieren kannst du alles direkt im Tool.