Docker Compose in productie: wat werkt en wat niet

Praktische tips voor het draaien van Docker Compose in productieomgevingen. Van health checks tot logging en zero-downtime deploys.

Jean-Pierre Broeders

Freelance DevOps Engineer

24 februari 20265 min. leestijd

Docker Compose in productie: wat werkt en wat niet

Er wordt vaak gezegd dat Docker Compose niet geschikt is voor productie. Dat klopt deels. Voor grote gedistribueerde systemen met honderden services is Kubernetes de logische keuze. Maar voor kleinere projecten — een API met database, Redis en een reverse proxy — werkt Compose prima. Mits goed opgezet.

De basis goed neerzetten

Een veelgemaakte fout: het docker-compose.yml bestand dat tijdens development wordt gebruikt, één-op-één kopiëren naar de server. Dat gaat mis. Volumes die naar lokale mappen wijzen, poorten die rechtstreeks exposed worden, geen resource limits. Allemaal dingen die in development handig zijn maar in productie problemen veroorzaken.

Een productie-compose ziet er anders uit:

services:
  api:
    image: registry.example.com/myapp:${VERSION:-latest}
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    environment:
      - NODE_ENV=production
    networks:
      - internal
      - web

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    networks:
      - internal

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
    networks:
      - internal

volumes:
  pgdata:

secrets:
  db_password:
    file: ./secrets/db_password.txt

networks:
  internal:
  web:
    external: true

Enkele dingen vallen op. Geen build stappen — in productie worden voorgebouwde images gebruikt. De database draait op een named volume, niet op een bind mount. Secrets worden via Docker secrets beheerd, niet als plaintext environment variables. En er zijn aparte netwerken: internal voor communicatie tussen services, web als extern netwerk voor de reverse proxy.

Health checks zijn niet optioneel

Zonder health checks weet Docker niet of een container daadwerkelijk functioneert. De container kan draaien terwijl de applicatie binnenin crashed is of vastloopt. Met een health check detecteert Docker dit en herstart de container automatisch (dankzij restart: unless-stopped).

Het start_period is cruciaal. Veel applicaties hebben opstarttijd nodig — database migraties, cache warming, dat soort dingen. Zonder start period markeert Docker de container als unhealthy voordat die überhaupt klaar is met opstarten.

Logging goed regelen

Standaard logt Docker naar JSON bestanden die onbeperkt groeien. Op een server met 50GB disk is dat een tikkende tijdbom.

services:
  api:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Hiermee worden logs geroteerd: maximaal 3 bestanden van 10MB. Voor serieuze setups is het beter om een externe logging driver te gebruiken, bijvoorbeeld fluentd of gelf, die logs doorstuurt naar een centraal systeem.

Zero-downtime deploys

Dit is waar het lastig wordt. Docker Compose heeft geen ingebouwde rolling update functionaliteit zoals Kubernetes. Maar met een truc lukt het toch:

#!/bin/bash
set -e

VERSION=$1

# Pull nieuw image
docker compose pull api

# Scale naar 2 instances
docker compose up -d --no-deps --scale api=2 --no-recreate api

# Wacht tot nieuwe container healthy is
sleep 15

# Stop oude container
docker compose up -d --no-deps --scale api=1 --no-recreate api

Dit werkt alleen als de reverse proxy (Traefik, nginx) health-aware is en verkeer automatisch routeert naar gezonde containers. Met Traefik en Docker labels gaat dat vrij soepel.

Secrets beheren

Environment variables in een .env bestand op de server is gangbaar maar niet ideaal. Die bestanden worden vergeten, niet geroteerd, en staan soms per ongeluk in version control.

Betere opties:

  • Docker secrets (zoals in het voorbeeld hierboven) — werkt out of the box
  • Hashicorp Vault met een init script dat secrets ophaalt bij het starten
  • SOPS voor encrypted secrets in Git — decrypt bij deploy

De keuze hangt af van de complexiteit van de setup. Voor een enkele server met drie services is Docker secrets meer dan voldoende.

Backups niet vergeten

Named volumes zijn mooi, maar ze backuppen zichzelf niet. Een simpel script dat periodiek draait:

#!/bin/bash
BACKUP_DIR="/opt/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

docker compose exec -T db pg_dump -U postgres mydb | \
  gzip > "${BACKUP_DIR}/db_${TIMESTAMP}.sql.gz"

# Oude backups opruimen (ouder dan 30 dagen)
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete

Zet dit in een cron job en test regelmatig of de backups daadwerkelijk te restoren zijn. Een backup die niet werkt is geen backup.

Wanneer overstappen naar iets anders

Docker Compose op een enkele server heeft grenzen. Als een van deze situaties zich voordoet, wordt het tijd om alternatieven te overwegen:

  • Meer dan ~10 services die onderling afhankelijk zijn
  • Horizontaal schalen over meerdere servers nodig
  • Complexe netwerk policies vereist
  • Meerdere teams die onafhankelijk deployen

Op dat punt zijn Docker Swarm (simpeler) of Kubernetes (krachtiger) betere opties. Maar voor een heleboel projecten is dat punt nooit bereikt, en dat is prima. De beste tooling is tooling die past bij de schaal van het probleem.

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op