Docker Compose is de tool waarmee je meerdere containers in één YAML-bestand definieert en ze allemaal met één commando start. Een echte applicatie bestaat zelden uit één enkel proces: een webserver, een database en, vaker wel dan niet, een cachelaag draaien samen. Ze één voor één starten met docker run is zowel omslachtig als foutgevoelig. In dit artikel bouwen we van nul af aan een typische stack van web-, PostgreSQL- en Redis-services, en behandelen we communicatie tussen services, persistente data en omgevingsvariabelen aan de hand van praktische voorbeelden.
Welk probleem lost Docker Compose op?
Eén container draaien is eenvoudig, maar in echte projecten wordt het snel ingewikkeld. Elke service heeft een eigen image, poort, netwerk en omgevingsvariabelen — en ze moeten vaak in een specifieke volgorde starten. Compose verplaatst die complexiteit naar een declaratief bestand:
- Eén bron: de hele stack staat in
compose.yaml; een teamgenoot kloont de repo en typt simpelwegdocker compose up. - Automatisch netwerk: Compose maakt een gedeeld netwerk voor je services, en containers bereiken elkaar via de servicenaam.
- Reproduceerbaarheid: hetzelfde bestand levert dezelfde stack op een ontwikkelaarsmachine en in CI.
Let op: in modern Docker is het commando docker compose (met een spatie); de oude docker-compose (met een streepje) werkt nog steeds, maar is vervangen door Compose V2.
Je eerste compose.yaml-bestand
Maak een map en voeg er een compose.yaml aan toe. Laten we beginnen met alleen een webservice en een database:
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Een paar belangrijke details: de regel "8080:80" onder ports koppelt poort 8080 op de host aan poort 80 in de container. depends_on laat de webservice na db starten (al garandeert het niet dat db daadwerkelijk klaar is — daar komen we zo op terug). Het volumes-blok maakt een persistent volume dat de databasebestanden bewaart, zelfs als de container wordt verwijderd.
Een cachelaag toevoegen: Redis
De meeste applicaties gebruiken Redis voor sessies, wachtrijen of caching. Een derde service toevoegen is slechts een kwestie van één extra blok:
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
depends_on:
- db
- cache
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
cache:
image: redis:7-alpine
command: ["redis-server", "--save", "60", "1"]
volumes:
db-data:
Het mooie hieraan: vanuit de webcontainer bereik je de database via de hostnaam db en Redis via cache. De interne DNS die Compose opzet, vertaalt de servicenaam automatisch naar het juiste IP-adres. In je applicatieconfiguratie worden de verbindingsreeksen dus postgres://app:secret@db:5432/appdb en redis://cache:6379 — geen hard-gecodeerde IP-adressen nodig.
Omgevingsvariabelen en het .env-bestand
Wachtwoorden hard in de YAML zetten is een slecht idee. Compose leest automatisch een naburig .env-bestand en vervangt variabelen met de ${...}-syntaxis:
# .env
POSTGRES_PASSWORD=een-zeer-geheime-waarde
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: appdb
Voeg .env toe aan je .gitignore zodat geheimen nooit in de repository belanden. Een voorbeeld .env.example achterlaten voor de rest van het team is een goede gewoonte.
Echte gereedheid met health checks
depends_on bepaalt alleen de startvolgorde; het garandeert niet dat de database verbindingen accepteert. De webservice kan proberen te verbinden terwijl db nog opstart, en falen. De oplossing is een healthcheck definiëren en depends_on combineren met condition: service_healthy:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 3s
retries: 5
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
depends_on:
db:
condition: service_healthy
Nu start de webcontainer pas nadat het commando pg_isready succesvol terugkeert. Dit elimineert de meeste van die "de database is nog niet klaar"-fouten.
Dagelijks gebruik: de kerncommando's
Dit zijn de commando's die je het vaakst zult gebruiken om de stack te draaien en te beheren:
docker compose up -d— start alle services op de achtergrond.docker compose ps— toont de draaiende services en hun status.docker compose logs -f web— volgt de logs van een specifieke service live.docker compose exec db psql -U app appdb— voert een commando uit in de draaiende db-container.docker compose down— stopt en verwijdert de services. Voeg-vtoe om ook de volumes te verwijderen.
Als je een image hebt gewijzigd (bijvoorbeeld je bouwt je eigen Dockerfile), herbouw je met docker compose up -d --build.
Veelgestelde vragen
Wat is het verschil tussen docker-compose en docker compose?
docker-compose (met streepje) is de oude V1-tool, geschreven in Python. docker compose (met spatie) is de op Go gebaseerde V2-plug-in die in de Docker CLI is geïntegreerd. Op actuele installaties gebruik je de versie met spatie; de YAML-syntaxis is grotendeels hetzelfde.
Gaan mijn gegevens verloren wanneer de container wordt verwijderd?
Nee — benoemde volumes gedefinieerd onder volumes staan los van de levenscyclus van de container. docker compose down verwijdert de containers, maar het volume blijft; je gegevens staan er bij de volgende up nog. Alleen down -v verwijdert ook de volumes.
Is Compose geschikt voor productie?
Voor kleine tot middelgrote opstellingen op één server is het prima geschikt. Voor systemen met meerdere nodes, automatische schaling en zelfherstel is een orkestrator als Kubernetes de betere keuze. Denk aan Compose voor ontwikkeling en eenvoudige productiescenario's, en aan Kubernetes voor grote schaal.
Wil je je stack in één bestand samenbrengen? Om je ontwikkeling en uitrol met Docker Compose te vereenvoudigen, neem contact met me op — laten we samen de infrastructuur van je project plannen.