Docker Deployment¶
TCM365 stellt Docker Compose Konfigurationen für Entwicklung, Produktion und Self-hosted Deployments bereit. Dieser Guide behandelt alle drei Konfigurationen und die zugrunde liegende Container-Architektur.
Container-Architektur¶
TCM365 verwendet drei Docker Images:
| Image | Basis-Image | Zweck |
|---|---|---|
tcm365-backend |
node:18-alpine |
NestJS Backend API Server |
tcm365-frontend |
node:22-alpine |
Vue 3 Static Assets via Nginx |
nginx (Reverse Proxy) |
nginx:alpine |
Reverse Proxy für API und Frontend |
Node Version Mismatch
Das Backend nutzt node:18-alpine, während das Frontend node:22-alpine verwendet. Dies ist bekannter Technical Debt, der im Projekt-Backlog getrackt wird. Der Grund: Vite 7 benötigt Node 22+, während das NestJS Backend auf Node 18 LTS läuft.
Docker Compose Konfigurationen¶
Entwicklung: docker-compose.local.yml¶
Startet ausschliesslich PostgreSQL und Redis für lokale Entwicklung. Backend und Frontend laufen nativ auf dem Entwicklungsrechner.
Gestartete Services:
| Service | Port | Zweck |
|---|---|---|
postgres |
5432 | PostgreSQL 15 Datenbank |
redis |
6379 | Redis 7 Cache (optional) |
Verwendung mit lokalen Entwicklungsservern:
Services stoppen:
Daten zurücksetzen (destruktiv):
Volume-Löschung
Der Parameter -v löscht alle Named Volumes einschliesslich der PostgreSQL-Daten. Alle Snapshots, Konfigurationen und Benutzer gehen dabei verloren.
Produktion: docker-compose.yml¶
Startet den vollständigen Stack einschliesslich Backend, Frontend, PostgreSQL, Redis und Nginx Reverse Proxy.
Gestartete Services:
| Service | Port | Zweck |
|---|---|---|
backend |
8000 | NestJS API Server (intern) |
frontend |
80 | Nginx mit Vue Static Assets |
postgres |
5432 | PostgreSQL 15 Datenbank |
redis |
6379 | Redis 7 Cache |
nginx |
80/443 | Reverse Proxy (externer Einstiegspunkt) |
Zugriff auf die Applikation:
Coolify: docker-compose.coolify.yml¶
Optimiert für Coolify Self-hosted PaaS Deployment:
Diese Konfiguration ist für die Zusammenarbeit mit Coolifys eingebautem Reverse Proxy und SSL Management konzipiert.
Dockerfiles¶
Backend: Dockerfile.backend-js¶
Multi-Stage Build für das NestJS Backend:
# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY backend-js/package*.json ./
RUN npm ci
COPY backend-js/ ./
COPY VERSION ../VERSION
RUN npm run build
# Stage 2: Production
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 8000
CMD ["node", "dist/main.js"]
Wichtige Punkte:
- Multi-Stage Build minimiert die Image-Größe
- Die
VERSIONDatei wird in den Container kopiert für die Versionsanzeige - Die Produktions-Stage enthält nur kompiliertes JavaScript und Produktions-Dependencies
- Port 8000 wird exponiert
Frontend: Dockerfile.frontend¶
Multi-Stage Build für das Vue 3 Frontend:
# Stage 1: Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
COPY VERSION ../VERSION
RUN npm run build
# Stage 2: Auslieferung mit Nginx
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY infrastructure/docker/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
Wichtige Punkte:
- Node 22 wird für die Build-Stage verwendet (Vite 7 benötigt dies)
- Die Produktions-Stage nutzt Nginx zur Auslieferung statischer Assets
- Die
VERSIONDatei steht während des Builds für Version Injection zur Verfügung
Nginx Konfiguration¶
Die infrastructure/docker/nginx.conf konfiguriert den Reverse Proxy:
server {
listen 80;
server_name _;
# Frontend Static Assets
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
# Backend API Proxy
location /api {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Health Check Proxy
location /health {
proxy_pass http://backend:8000;
}
}
Die Nginx Konfiguration:
- Liefert die Vue 3 SPA mit
try_filesFallback für Client-Side Routing aus - Leitet
/api/*und/healthRequests an den Backend Container weiter - Uebergibt die echte Client-IP via
X-Forwarded-For - Behandelt den SPA History Mode (alle Nicht-API Routen liefern
index.htmlaus)
Umgebungsvariablen für Docker¶
Backend Container¶
Umgebungsvariablen werden dem Backend Container via .env Datei oder Docker Compose environment Sektion übergeben:
# docker-compose.yml Auszug
services:
backend:
build:
context: ../..
dockerfile: infrastructure/docker/Dockerfile.backend-js
environment:
- PORT=8000
- APP_SECRET_KEY=your-production-secret-min-32-chars
- APP_DEBUG=false
- DATABASE_HOST=postgres
- DATABASE_PORT=5432
- DATABASE_USERNAME=tcm_user
- DATABASE_PASSWORD=tcm_password
- DATABASE_NAME=tcm_db
- STORAGE_BACKEND=local
- LOCAL_STORAGE_PATH=/app/data
- REDIS_URL=redis://redis:6379/0
- CORS_ORIGINS=http://localhost,https://tcm.example.com
volumes:
- backend-data:/app/data
depends_on:
postgres:
condition: service_healthy
DATABASE_HOST in Docker
Innerhalb von Docker Compose muss DATABASE_HOST auf den Service-Namen postgres gesetzt werden, nicht auf localhost. Docker Compose stellt die DNS-Aufloesung zwischen Containern über Service-Namen bereit.
PostgreSQL Container¶
services:
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_USER=tcm_user
- POSTGRES_PASSWORD=tcm_password
- POSTGRES_DB=tcm_db
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U tcm_user -d tcm_db"]
interval: 10s
timeout: 5s
retries: 5
Redis Container¶
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
Volumes¶
Docker Compose verwendet Named Volumes für Datenpersistenz:
| Volume | Container Mount | Zweck |
|---|---|---|
postgres-data |
/var/lib/postgresql/data |
PostgreSQL Datenbankdateien |
redis-data |
/data |
Redis Persistenz (falls aktiviert) |
backend-data |
/app/data |
Lokaler Dateispeicher (Snapshots, Reports) |
Volume-Persistenz
Named Volumes ueberleben Container-Neustarts und docker compose down. Um Daten vollständig zurückzusetzen, verwenden Sie docker compose down -v, was alle Volumes entfernt.
Bauen und Aktualisieren¶
Alle Images bauen¶
Einzelnen Service neu bauen¶
Aktualisieren und neu starten¶
Migrationen in Docker ausführen¶
# Migrationen im laufenden Backend Container ausführen
docker compose exec backend npm run migration:run
# Admin-Benutzer erstellen
docker compose exec backend npm run db:seed
Health Checks¶
Die Docker Compose Konfiguration beinhaltet Health Checks für alle Services:
| Service | Health Check | Intervall | Retries |
|---|---|---|---|
postgres |
pg_isready -U tcm_user -d tcm_db |
10s | 5 |
redis |
redis-cli ping |
10s | 5 |
backend |
curl -f http://localhost:8000/health |
30s | 3 |
Der Backend Container wartet auf einen gesunden PostgreSQL-Status bevor er startet (depends_on mit condition: service_healthy).
Troubleshooting Docker¶
Container startet nicht¶
# Container Logs prüfen
docker compose logs backend
docker compose logs postgres
# Container Status prüfen
docker compose ps
Datenbankverbindung abgelehnt¶
Stellen Sie sicher, dass das Backend den Docker Service-Namen (postgres) als Datenbank-Host verwendet, nicht localhost:
DATABASE_HOST=postgres # Docker Service-Name (korrekt)
DATABASE_HOST=localhost # Nur für host-basierte Entwicklung
Port-Konflikte¶
Falls die Ports 5432, 6379 oder 8000 bereits belegt sind:
Ports in docker-compose.yml ändern:
Container Speicherverbrauch¶
# Docker Speicherverbrauch prüfen
docker system df
# Ungenutzte Images und Container bereinigen
docker system prune
# Alle gestoppten Container und ungenutzten Images entfernen (aggressiver)
docker system prune -a
Regelmäßige Bereinigung
Führen Sie docker system prune regelmäßig aus, um ungenutzten Speicherplatz durch alte Build-Caches und gestoppte Container freizugeben.