docker devops best-practices
Docker Compose Projekt-Struktur
Nach hunderten Docker-Projekten hat sich eine Struktur herauskristallisiert, die einfach funktioniert.
Die Struktur
projekt/
├── docker-compose.yml
├── docker-compose.override.yml (optional, für Dev)
├── .env
├── .env.example
├── config/
│ ├── nginx/
│ │ └── nginx.conf
│ └── app/
│ └── config.json
├── data/
│ └── .gitkeep
├── scripts/
│ ├── backup.sh
│ └── restore.sh
└── README.md
docker-compose.yml
Die Basis. Funktioniert in jeder Umgebung:
services:
app:
image: myapp:${TAG:-latest}
restart: unless-stopped
env_file:
- .env
volumes:
- ./config/app:/config:ro
- ./data:/data
networks:
- internal
db:
image: postgres:16-alpine
restart: unless-stopped
env_file:
- .env
volumes:
- db_data:/var/lib/postgresql/data
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
networks:
internal:
volumes:
db_data:
docker-compose.override.yml
Automatisch geladen in Dev. Ports exponieren, Debug-Mode, etc:
services:
app:
ports:
- "3000:3000"
environment:
- DEBUG=true
volumes:
- ./src:/app/src # Hot-Reload
db:
ports:
- "5432:5432"
.env und .env.example
.env.example - Template mit Kommentaren (committed):
# Database
POSTGRES_USER=app
POSTGRES_PASSWORD=changeme
POSTGRES_DB=myapp
# App
APP_SECRET=changeme
TAG=latest
.env - Echte Werte (NICHT committed!):
POSTGRES_USER=app
POSTGRES_PASSWORD=super-sicheres-passwort-123
POSTGRES_DB=myapp
APP_SECRET=noch-sichereres-geheimnis
TAG=1.2.3
.gitignore:
.env
data/*
!data/.gitkeep
Config-Verzeichnis
Für Konfigurationsdateien die gemountet werden:
config/
├── nginx/
│ ├── nginx.conf
│ └── sites/
│ └── app.conf
└── app/
└── config.json
Read-only mounten:
volumes:
- ./config/nginx:/etc/nginx/conf.d:ro
Data-Verzeichnis
Für persistente Daten die NICHT in ein Volume sollen:
volumes:
- ./data/uploads:/app/uploads
Wann ./data vs Docker Volume?
- Docker Volume: Datenbank, Cache - schneller, managed
- Bind Mount
./data: Uploads, Logs - einfacher Zugriff, Backup
Scripts
Wiederkehrende Aufgaben:
#!/bin/bash
# scripts/backup.sh
docker compose exec -T db pg_dump -U $POSTGRES_USER $POSTGRES_DB > backup.sql
#!/bin/bash
# scripts/restore.sh
docker compose exec -T db psql -U $POSTGRES_USER $POSTGRES_DB < backup.sql
Mehrere Umgebungen
Für Staging/Prod separate Compose-Files:
├── docker-compose.yml # Basis
├── docker-compose.override.yml # Dev (auto-loaded)
├── docker-compose.staging.yml
└── docker-compose.prod.yml
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d
# Produktion
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Meine Regeln
- Ein Projekt, ein Verzeichnis
- .env nie committen
- Healthchecks für alle Services
- restart: unless-stopped für Produktion
- Versions-Tags statt
in Prod
Fazit
Konsistente Struktur = weniger Kopfschmerzen. Einmal richtig aufsetzen, immer wieder verwenden.