Das ultimative Docker Compose Cheat Sheet
Dieser Artikel wurde mit ChatGPT automatisch aus dem Englischen ins Deutsche übersetzt.
TLDR; Hol dir das Docker Compose Cheat Sheet als PDF oder Bild. Damit du mitarbeiten kannst, muss Docker auf deinem Entwicklungsrechner installiert sein. In diesem Artikel schreiben wir eine eigene compose.yaml, starten Anwendungen mit mehreren Containern und lernen, sie zu verwalten. Den vollständigen Quellcode findest du auf GitHub.
Das ultimative Docker Compose Cheat Sheet herunterladen
Lade das Docker Compose Cheat Sheet herunter, um den Artikel Schritt für Schritt durchzuarbeiten. Teile es gerne mit deinen Kolleginnen, Kollegen und Freunden.
Möchtest du weitere Ressourcen wie diese?
Komm in unsere Community und melde dich für unseren Newsletter an, um bei DevOps auf dem Laufenden zu bleiben. Du erhältst unsere neuesten Ressourcen und Erkenntnisse direkt in dein Postfach!
Was ist Docker Compose?
Docker Compose ist ein Werkzeug zum Definieren und Ausführen von Docker-Anwendungen mit mehreren Containern. Damit konfigurierst und verwaltest du Anwendungsdienste, Netzwerke und Volumes.
Compose verwendet Textdateien im YAML-Format. Darin definierst du zusammengehörige Docker-Container, die sich gemeinsam steuern lassen. Das vereinfacht die Bereitstellung und Skalierung von Anwendungen.
Was unterscheidet Docker und Docker Compose?
Mit Docker baust du Images, startest Container und erstellst Volumes und Netzwerke. Wenn du Unterstützung bei den Grundlagen brauchst, lies „Das ultimative Docker Cheat Sheet“ oder schau dir unsere Anleitungen auf YouTube an.
Docker Compose hilft dir, mehrere Container zu verwalten. Dazu gehören das Bauen von Images, das Starten von Containern sowie das Einrichten von Volumes und Netzwerken für die Kommunikation zwischen Containern.
Compose nutzt Docker, um mehrere Container mit einer Datei und einem CLI-Befehl zu verwalten.
Warum brauchst du Docker Compose?
Die gleichzeitige Arbeit mit mehreren Containern kann kompliziert werden. Für einen einzelnen Container reicht Docker allein gut aus. Doch bald braucht deine Anwendung weitere Dienste: etwa eine Datenbank, einen In-Memory-Cache, eine Nachrichtenwarteschlange oder einen Storage-Bucket. Die Liste ließe sich lange fortsetzen.
Mit Docker allein müsstest du jeden dieser Container einzeln verwalten. Hier hilft Docker Compose: Es vereinfacht die Konfiguration und Verwaltung mehrerer Container.
Was ist eine compose.yaml?
Diese YAML-Datei legt fest, wie Docker-Container in einer Anwendung zusammenarbeiten. Sie bildet die Grundlage von Docker Compose und die zentrale Stelle zur Konfiguration und Verwaltung aller Bestandteile. Darin definieren wir Services, Volumes und Netzwerke.
Was ist ein Service in Docker Compose?
Ein Service beschreibt die Container, die du starten möchtest. Darin laufen deine Anwendungen, etwa Skripte, Datenbanken, Webanwendungen oder Webserver. In diesem Artikel verwenden wir Container und Service im jeweiligen Beispiel weitgehend gleichbedeutend.
Was ist ein Volume in Docker Compose?
Volumes speichern Daten dauerhaft. Sie lassen sich als Verzeichnisse in Container einbinden. Die Dateien bleiben erhalten, auch wenn der Container entfernt wird, und stehen beim nächsten Start wieder zur Verfügung.
Was ist ein Netzwerk in Docker Compose?
Netzwerke ermöglichen die Kommunikation zwischen Containern. Darüber können isolierte Docker-Container kontrolliert Informationen austauschen.
Wie schreibst du eine compose.yaml?
Wir erstellen eine compose.yaml für eine Anwendung mit zwei Containern: einem Backend und einem Webfrontend. Im Laufe des Artikels ergänzen wir einen dritten Container mit einer Postgres-Datenbank. Diese verbinden wir mit einem Volume, damit unsere Daten erhalten bleiben, wenn wir den Container entfernen und neu erstellen. Ein Netzwerk ermöglicht die Kommunikation zwischen Backend und Datenbank. Den vollständigen Quellcode findest du auf GitHub.
# change directory to the root of our application
$ cd /path/to/the/application/root
# now we create an empty file called compose.yaml
$ touch compose.yaml
In diesem Artikel betrachten wir die Einträge version, services, volumes und networks auf der obersten Ebene. version stammt aus älteren Compose-Dateiformaten; aktuelle Compose-Versionen benötigen diese Angabe nicht mehr.
# string that represents the version of docker compose used
# for backwards compatibility and just informational
version: '3'
# an object where each key represents a new service
# e.g., your client application, web server, database, ...
services:
client:
# define your client
# e.g., image, ports, environment variables, networks, volumes, ...
server:
# define your server
# e.g., image, ports, environment variables, networks, volumes, ...
database:
# define your database
# e.g., image, ports, environment variables, networks, volumes, ...
# an object where each key represents a new volume
# e.g., to persist the database, store images, documents, ...
# volumes need to be explicitly bound to a service
volumes:
database_volume:
# define the settings of your volume
# if you leave this empty, default values will be applied
# an object where each key represents a network
# e.g., to communicate with containers in the same network
# networks need to be explicitly bound to a service
# docker creates a default network for all services in a compose file
# every service joins the default network and can contact every other container
# by its name e.g., docker sets up a DNS entry in server
# for the client and database
# so a call from the server container to <protocol>://database:<port>
# is equivalent to <protocol>://<ip-address-of-database>:<port>
networks:
# we can also define explicit networks
# and let only some containers join
# e.g., database and server
server_database_network:
# define the settings of your network
# if you leave this empty, default values will be applied
# an object where each key represents a config
# e.g., to adapt behavior without the need for rebuilding an image
# configs need to be explicitly bound to a service
configs:
some_config:
# an object where each key represents a secret
# e.g., to adapt behavior without the need for rebuilding an image
# secrets act like configs but with a specific focus on sensitive information
# secrets need to be explicitly bound to a service
secrets:
some_secret:
In den nächsten Abschnitten konfigurieren wir Services, Volumes und Netzwerke. Anschließend kombinieren wir sie, um Webanwendung, Webserver und Datenbank zu starten. Server und Datenbank verbinden wir über ein Netzwerk. Ein Volume speichert die Datenbankinhalte, damit sie nach dem Entfernen und Neuerstellen des Containers weiter verfügbar sind.
Wie startest du einen Service aus einem Dockerfile?
Du kannst in Docker Compose ein Dockerfile als Grundlage angeben. Compose baut daraus das Image und startet anschließend den Container.
version: '3'
# all the services that we are defining
# services are running containers
services:
# we are defining a service called client
# this is the client side of our application
client:
# we use the build command to create the image
# from the Dockerfile that we pass to this command
# in this case "Dockerfile.client"
# this image will then be used to create the container
# we also pass the context of the build
# as the compose.yml file is in the same directory
# as the source code, we can use the .
# to refer to the current directory
build:
context: .
dockerfile: Dockerfile.client
Wie startest du einen Service aus einem Image?
Du kannst auch ein bereits gebautes image verwenden. Findet Docker es nicht lokal, versucht es, das Image von Docker Hub herunterzuladen. In unserem Beispiel verwenden wir Postgres in Version 16.1.
version: '3'
# all the services that we are defining
# services are running containers
services:
# we are defining a service called database
# this is the Postgres database of our application
database:
# this time we do not use the build command
# we use the image command to use an existing image
# by default, docker compose will look at the local registry
# to find the image
# if it is not available locally, it will pull it from
# the docker hub registry
image: postgres:16.1
Wie veröffentlichst du Ports mit Docker Compose?
Über ports im Service-Objekt machst du Container vom Host aus erreichbar. Unser Client soll unter http://localhost:80 im Browser verfügbar sein.
version: '3'
services:
client:
build:
context: .
dockerfile: Dockerfile.client
# the ports that we want to publish
# the first port is the port on the host system
# the second port is the port inside the container
# so we map the port 80 of the container to the
# port 80 of the host
# ports is an array, so you can add as many ports as needed
ports:
- "80:80"
Wie verwendest du Umgebungsvariablen in Docker Compose?
Manche Anwendungen benötigen Umgebungsvariablen, etwa für Passwörter, Domains, Ports, IP-Adressen oder API-Schlüssel. Du kannst sie als Objekt unter environment angeben oder mit env_file auf eine Datei verweisen, die sie enthält.
version: '3'
services:
database:
# this time we do not use the build command
# we use the image command to use an existing image
# by default, docker compose will look at the local registry
# to find the image
# if it is not locally available, it will pull it from
# the docker hub registry
image: postgres:16.1
ports:
# the ports that we want to publish
# the first port is the port on the host system
# the second port is the port inside the container
# so we map the port 3000 of the container to the
# port 3000 of the host
- "5432:5432"
# we can define and pass environment variables
# to the container
# we will use these variables to connect to the database
# in our server
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: user
POSTGRES_DB: database
# if you use a .env file
# you can use ${YOUR_ENV_VAR}
# and Docker will replace this value
# with the value in your .env file
YOUR_ENV_VAR: ${YOUR_ENV_VAR}
# you can also pass complete .env files
# even multiples
env_file:
- .env.local
- .env.override
# env files are structured like
# `YOUR_ENV_VAR=your-env-value`
# each line is a new environment variable
Wie startest du einen abgestürzten Service erneut?
Rechner, Anwendungen, Stromversorgung und Menschen können ausfallen oder Fehler machen. Das gilt auch für Docker-Container. Mit restart im Service-Objekt legst du fest, wann Compose beziehungsweise Docker einen gestoppten Service wieder starten soll.
version: '3'
services:
client:
build:
context: .
dockerfile: Dockerfile.client
ports:
- "80:80"
# whenever our container stops, we want it to restart
# unless explicitly stopped manually by us
restart: always
Wie wartest du in Docker Compose auf einen anderen Service?
Zwischen Services bestehen oft Abhängigkeiten. Beispielsweise soll dein Backend erst starten, nachdem der Datenbank-Container gestartet wurde. Dafür trägst du den Service-Namen unter depends_on ein.
version: '3'
services:
server:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
restart: always
# we want our server to wait for the database to be ready
# docker compose only checks if the container is running
# not if the database is ready
depends_on:
- database
Wie wartest du auf einen bestimmten Zustand eines anderen Services?
Ohne zusätzliche Bedingung wartet Compose nur darauf, dass der Container gestartet wurde. Die Datenbank darin muss zu diesem Zeitpunkt noch nicht bereit sein. Um auf ihre Betriebsbereitschaft zu warten, ergänzen wir einen Healthcheck im Datenbank-Service und eine Bedingung bei depends_on. Unter dem Service-Namen, hier database, setzen wir condition auf service_healthy. Der abhängige Container startet dann erst, wenn der Healthcheck der Datenbank erfolgreich war. Im nächsten Abschnitt richten wir diesen Check ein.
version: '3'
services:
server:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
restart: always
# we want our server to wait for the database to be ready
depends_on:
# here we specify the name of the service
# in our case, our service is named database
database:
# we add a condition
# only if this condition is met
# the server service will start
condition: service_healthy
Wie definierst du Healthchecks in Docker Compose?
Ein healthcheck prüft, ob ein Service funktionsfähig ist. Das Objekt bietet mehrere Optionen:
- test: Der Befehl, der den Zustand des Services prüft.
- interval: Der Abstand zwischen den Prüfungen.
- timeout: Dauert eine einzelne Prüfung länger als diese Zeit, gilt sie als fehlgeschlagen.
- start_period: Eine Anlaufphase für den Container. Fehlgeschlagene Prüfungen zählen während dieser Zeit zunächst nicht als Fehler. Eine erfolgreiche Prüfung setzt den Status bereits auf „healthy“.
- retries: Die Anzahl aufeinanderfolgender fehlgeschlagener Prüfungen, bevor der Service als „unhealthy“ gilt.
services:
database:
image: postgres:16.1
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: user
POSTGRES_DB: database
restart: always
# in Docker Compose, we can define health checks
# health checks are commands that are executed
# to check whether the container is healthy or not
healthcheck:
# in this case, we check if the database is ready
# by using the pg_isready command
test: ["CMD", "pg_isready", "-U", "user", "-d", "database"]
# we check if the database is ready every 2 seconds
interval: 2s
# when a duration of a check takes more than 2 seconds
# we consider it a failure
timeout: 2s
# we retry 3 times before we set the status to unhealthy
retries: 3
# we give the container 2 seconds for bootstrapping
# before we consider a failed health check
start_period: 2s
Wie speicherst du Daten dauerhaft mit Volumes in Docker Compose?
Wenn wir Daten in die Datenbank schreiben und anschließend alle Container stoppen und entfernen, gehen ohne zusätzliche Speicherung auch die Datenbankinhalte verloren. Volumes sorgen dafür, dass sie unabhängig vom Container erhalten bleiben.
Bei einem benannten Volume gibst du einen Namen und einen Pfad im Container an. Docker verwaltet den Speicherort auf dem Host. Der Containerpfad bezeichnet den Teil des Dateisystems, dessen Daten erhalten bleiben sollen.
volumes:
- name_of_volume:/path/inside/of/container
Ein Bind-Mount funktioniert ähnlich, verwendet als ersten Teil aber einen absoluten Pfad auf dem Host. Diesen Speicherort wählst du selbst.
volumes:
- /path/on/host/system:/path/inside/of/container
Für unser Beispiel verwenden wir ein benanntes Volume und den Pfad, unter dem Postgres seine Daten speichert.
database:
image: postgres:16.1
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: user
POSTGRES_DB: database
restart: always
healthcheck:
test: ["CMD", "pg_isready", "-U", "user", "-d", "database"]
interval: 2s
timeout: 2s
retries: 3
start_period: 2s
# we want to persist the data of the database
# so we use a volume
# the volume is defined at the bottom of this file
# we use a named volume
# the name is postgres_data_volume
# and we mount the path /var/lib/postgresql/data
# from the container to the volume
# when we create a named volume, docker will create
# a directory on the host system to store the data
# this is managed by docker
# it follows the same rules as the port mapping
# the first path (or name) is the path to the host system
# the second path is the path inside the container
volumes:
- postgres_data_volume:/var/lib/postgresql/data
# here we define the volumes that we use
# if we want that a service uses a volume
# we need to explicitly use it in the service
volumes:
# we create a named volume called
# postgres_data_volume
postgres_data_volume:
# if we do not specify anything here
# docker will use the default settings for this volume
Wie kommunizieren Services über Netzwerke in Docker Compose?
Docker-Netzwerke ermöglichen die Kommunikation zwischen Containern. Compose erstellt standardmäßig ein gemeinsames Netzwerk für die Services einer Compose-Datei. Um die Konfiguration zu zeigen, definieren wir selbst ein Netzwerk, das server und database verbindet. Docker legt für die Services darin DNS-Einträge an. So können sie einander über die Namen aus der Compose-Datei erreichen. Unser Backend verwendet database als Datenbank-Hostnamen. Aus dem Datenbank-Container ließe sich mit curl -X GET http://server:3000/ eine HTTP-Anfrage an den Server senden, sofern curl dort installiert ist.
version: '3'
services:
client:
build:
context: .
dockerfile: Dockerfile.client
ports:
- "80:80"
restart: always
server:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
restart: always
# docker compose uses a default network if we do not specify one
# but for this example we created our own network
# that connects our database and our server
# the definition of the network is at the bottom of this file
networks:
- server_database
depends_on:
database:
condition: service_healthy
database:
image: postgres:16.1
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: user
POSTGRES_DB: database
restart: always
healthcheck:
test: ["CMD", "pg_isready", "-U", "user", "-d", "database"]
interval: 2s
timeout: 2s
retries: 3
start_period: 2s
volumes:
- postgres_data_volume:/var/lib/postgresql/data
# docker compose uses a default network if we do not specify one
# but for this example we created our own network
# that connects our database and our server
# the definition of the network is at the bottom of this file
networks:
- server_database
volumes:
postgres_data_volume:
# here we define the networks that we use
# if we want that a service uses a network
# we need to explicitly use it in the service
networks:
# we create a network called
# server_database
server_database:
# if we do not specify anything here
# docker will use the default settings for this network
Wie startest du mehrere Services mit Docker Compose?
Ein einziger Befehl startet alle in der compose.yaml definierten Container. Abhängigkeiten aus depends_on werden dabei berücksichtigt.
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# start all containers
$ docker compose up
# if you want to start all containers in a background process
# add the flag --detach
$ docker compose up --detach
# use this for the following section as well
# verify all containers
# by listing all containers running on this system
$ docker container ls
# verify the volume
# by listing all volumes on this system
$ docker volume ls
# verify the network
# by listing all networks on this system
$ docker network ls
Wie startest du einen einzelnen Service mit Docker Compose?
Du kannst einen Service gezielt über seinen Namen starten. Für die Datenbank verwendest du beispielsweise folgenden Befehl:
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# start the database container
# docker compose up <service-name>
$ docker compose up database
# add the --detach flag to start it as a background process
$ docker compose server --detach
# this command will start the server and the database
# because the server depends on the database
Wie stoppst und startest du mehrere Services erneut?
Mit einem Befehl stoppst du alle Container gleichzeitig. Sie werden dabei nicht entfernt und lassen sich anschließend wieder starten.
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# stop all containers
$ docker compose stop
# if you want to restart it
$ docker compose restart
Wie stoppst und startest du einen einzelnen Service erneut?
Gib den Service-Namen an, um nur diesen Service zu stoppen oder neu zu starten.
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# stop the database container
# docker compose stop <service-name>
$ docker compose stop database
# if you want to restart it
$ docker compose restart database
Wie entfernst du mehrere Services mit Docker Compose?
Mit folgendem Befehl entfernst du alle gestoppten Container:
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# remove all stopped containers
$ docker compose rm
# this will ask you if you are sure about this action
# if you would like to remove them, confirm this action by typing
# "Y" in your terminal
Wie entfernst du einen einzelnen Service mit Docker Compose?
Gib den Service-Namen an, um nur dessen gestoppten Container zu entfernen.
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# remove the database container
# the container needs to be stopped before you can remove it
# docker compose stop <service-name>
$ docker compose stop database
$ docker compose rm database
Wie stoppst und entfernst du mehrere Services gleichzeitig?
Mit folgendem Befehl stoppst und entfernst du die Container aus einer compose.yaml in einem Schritt:
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# stop and remove all containers
$ docker compose down
Wie greifst du auf mit Docker Compose gestartete Services zu?
Mit Docker verwendest du docker exec -it <container-name> <command>. Mehr dazu erfährst du in unserem Artikel zu den Docker-Grundlagen.
Mit Compose verwendest du docker compose exec und gibst den Service-Namen an.
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# create an ssh like session with a service
# docker compose exec --interactive --tty <service-name> sh
$ docker compose exec --interactive --tty database sh
# now you are inside the container
# use exit to close the connection
$ exit
Wie liest du Container-Logs in Docker Compose?
Mit folgendem Befehl greifst du auf die Logs zu:
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# to access all container logs
$ docker compose logs
Wie liest du die Logs eines bestimmten Services?
Gib den Namen des Services an, um nur dessen Logs anzuzeigen.
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# to access the logs of the server
# docker container logs <service-name>
$ docker compose logs server
Fazit
Du kennst jetzt den Unterschied zwischen Docker und Docker Compose. Du kannst eine eigene compose.yaml schreiben und damit mehrere Container gemeinsam verwalten. Außerdem weißt du, wie du Daten mit Volumes dauerhaft speicherst, Services über Netzwerke verbindest und vom Host aus auf Container und Logs zugreifst.
Wenn du Unterstützung bei der Containerisierung brauchst, kontaktiere uns gerne. Oder komm für Fragen und Diskussionen in unsere neue Community – kostenlose Kekse für die ersten 42 Mitglieder, und nur noch 6 sind übrig 😱!
Komm in unsere CommunityHat dir dieser Artikel gefallen? Teile ihn mit deinen Kolleginnen, Kollegen und Freunden.
Melde dich für unseren Newsletter an!
Verpasse keine neuen Tipps, Anleitungen und Updates – melde dich jetzt für unseren Newsletter an! Wir schicken dir nur relevante und hilfreiche Informationen. Entdecke mit uns die Welt von Docker und darüber hinaus.
Mit deiner Anmeldung akzeptierst du die Datenschutzerklärung. Du kannst dich jederzeit über den Link am Ende unserer E-Mails abmelden.

