# =================================================================== # Docker Compose - Client Applications (OPTIMIZED & SECURED) # Meldestelle Project - Frontend Components # =================================================================== # Security & Performance Improvements: # - Resource limits and reservations for all clients # - Security hardening (non-root users, no-new-privileges) # - Optimized build configurations and caching # - Enhanced health checks and monitoring # - Production-ready configurations # =================================================================== # Usage Scenarios: # # 1. STANDALONE CLIENT DEPLOYMENT: # docker-compose -f docker-compose.clients.yml.optimized up -d # - Clients run independently without api-gateway dependency # - Set GATEWAY_HOST environment variable to external API Gateway # # 2. MULTI-FILE WITH INFRASTRUCTURE: # docker-compose -f docker-compose.yml.optimized -f docker-compose.clients.yml.optimized up -d # - Infrastructure services start first, then clients connect # # 3. COMPLETE SYSTEM: # docker-compose -f docker-compose.yml.optimized -f docker-compose.services.yml.optimized -f docker-compose.clients.yml.optimized up -d # - Full stack deployment with all components # =================================================================== version: '3.9' services: # =================================================================== # Web Application - Kotlin/JS + Nginx with Security Hardening # =================================================================== web-app: build: context: . dockerfile: dockerfiles/clients/web-app/Dockerfile args: - BUILD_DATE=${BUILD_DATE:-$(date -u +"%Y-%m-%dT%H:%M:%SZ")} - VERSION=${APP_VERSION:-1.0.0} - NODE_ENV=${NODE_ENV:-production} container_name: meldestelle-web-app environment: NODE_ENV: ${NODE_ENV:-production} APP_TITLE: ${APP_NAME:-Meldestelle} APP_VERSION: ${APP_VERSION:-1.0.0} # API Gateway Configuration API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081} # Nginx Worker Processes (for Performance) NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} NGINX_WORKER_CONNECTIONS: ${NGINX_WORKER_CONNECTIONS:-1024} # Security Headers NGINX_SECURITY_HEADERS: "true" ports: - "${WEB_APP_PORT:-4000}:4000" networks: - meldestelle-network volumes: # Nginx cache directory - web-app-cache:/var/cache/nginx # Temp directory with proper permissions - web-app-tmp:/tmp/nginx deploy: resources: limits: cpus: '1.0' memory: 512M reservations: cpus: '0.25' memory: 128M healthcheck: test: ["CMD", "curl", "--fail", "--max-time", "5", "http://localhost:4000/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s restart: unless-stopped security_opt: - no-new-privileges:true # Run as nginx user (built into nginx image) user: "101:101" # nginx:nginx labels: - "traefik.enable=true" - "traefik.http.routers.web-app.rule=Host(`${WEB_APP_DOMAIN:-localhost}`) && PathPrefix(`/`)" - "traefik.http.services.web-app.loadbalancer.server.port=4000" - "traefik.http.routers.web-app.tls=true" # =================================================================== # Desktop Application - Kotlin Desktop + VNC with Security # =================================================================== desktop-app: build: context: . dockerfile: dockerfiles/clients/desktop-app/Dockerfile args: - BUILD_DATE=${BUILD_DATE:-$(date -u +"%Y-%m-%dT%H:%M:%SZ")} - VERSION=${APP_VERSION:-1.0.0} container_name: meldestelle-desktop-app environment: # API Configuration - fallback to external gateway if not in same compose network API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081} # VNC Configuration with Security DISPLAY: ":99" VNC_PORT: "5901" NOVNC_PORT: "6080" VNC_PASSWORD_FILE: /run/secrets/vnc_password # App Information APP_TITLE: ${APP_NAME:-Meldestelle} APP_VERSION: ${APP_VERSION:-1.0.0} # JVM Settings for Desktop App JAVA_OPTS: "-Xmx1g -Xms256m -XX:+UseG1GC -XX:MaxGCPauseMillis=200" ports: - "${DESKTOP_VNC_WEB_PORT:-6080}:6080" # Web-based VNC (noVNC) - "${DESKTOP_VNC_PORT:-5901}:5901" # VNC direct access volumes: # Desktop app data directory - desktop-app-data:/app/data # X11 socket for display - /tmp/.X11-unix:/tmp/.X11-unix:rw networks: - meldestelle-network secrets: - vnc_password deploy: resources: limits: cpus: '2.0' memory: 2G reservations: cpus: '0.5' memory: 512M healthcheck: test: ["CMD", "/opt/health-check.sh"] interval: 30s timeout: 10s retries: 3 start_period: 90s restart: unless-stopped security_opt: - no-new-privileges:true labels: - "traefik.enable=true" - "traefik.http.routers.desktop-app.rule=Host(`${DESKTOP_APP_DOMAIN:-localhost}`) && PathPrefix(`/desktop`)" - "traefik.http.services.desktop-app.loadbalancer.server.port=6080" - "traefik.http.routers.desktop-app.tls=true" profiles: - desktop # =================================================================== # Auth Server - Custom Authentication Service (Enhanced Security) # =================================================================== auth-server: build: context: . dockerfile: dockerfiles/infrastructure/auth-server/Dockerfile args: # Global build arguments GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} BUILD_DATE: ${BUILD_DATE:-unknown} VERSION: ${DOCKER_APP_VERSION:-1.0.0} # Infrastructure-specific arguments SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} container_name: meldestelle-auth-server volumes: - auth-server-gradle-cache:/home/gradle/.gradle environment: SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} SERVER_PORT: ${AUTH_SERVICE_PORT:-8087} # Keycloak Integration KEYCLOAK_SERVER_URL: http://keycloak:8080 KEYCLOAK_REALM: meldestelle KEYCLOAK_CLIENT_ID: meldestelle-auth-service KEYCLOAK_CLIENT_SECRET_FILE: /run/secrets/keycloak_auth_client_secret # Database connection via secrets DB_HOST: postgres DB_PORT: 5432 DB_NAME: ${POSTGRES_DB:-meldestelle} DB_USERNAME_FILE: /run/secrets/postgres_user DB_PASSWORD_FILE: /run/secrets/postgres_password # JWT Configuration via secrets JWT_SECRET_FILE: /run/secrets/jwt_secret JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} ports: - "${AUTH_SERVICE_PORT:-8087}:8087" depends_on: postgres: condition: service_healthy keycloak: condition: service_started networks: - meldestelle-network secrets: - postgres_user - postgres_password - jwt_secret - keycloak_auth_client_secret deploy: resources: limits: cpus: '1.5' memory: 1.5G reservations: cpus: '0.5' memory: 512M healthcheck: test: ["CMD", "curl", "--fail", "--max-time", "10", "http://localhost:8087/actuator/health/readiness"] interval: 30s timeout: 15s retries: 3 start_period: 90s restart: unless-stopped security_opt: - no-new-privileges:true profiles: - auth # =================================================================== # Monitoring Server - Custom Grafana Extensions (Enhanced Security) # =================================================================== monitoring-server: build: context: . dockerfile: dockerfiles/infrastructure/monitoring-server/Dockerfile args: # Global build arguments GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} BUILD_DATE: ${BUILD_DATE:-unknown} VERSION: ${DOCKER_APP_VERSION:-1.0.0} # Infrastructure-specific arguments SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} container_name: meldestelle-monitoring-server volumes: - monitoring-server-gradle-cache:/home/gradle/.gradle - monitoring-data:/app/data - ./docker/monitoring:/app/config:ro environment: SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} SERVER_PORT: 8088 # Monitoring Services URLs GRAFANA_URL: http://grafana:3000 PROMETHEUS_URL: http://prometheus:9090 # Authentication via secrets GRAFANA_ADMIN_USER_FILE: /run/secrets/grafana_admin_user GRAFANA_ADMIN_PASSWORD_FILE: /run/secrets/grafana_admin_password METRICS_AUTH_USERNAME_FILE: /run/secrets/metrics_auth_username METRICS_AUTH_PASSWORD_FILE: /run/secrets/metrics_auth_password ports: - "${MONITORING_SERVER_PORT:-8088}:8088" depends_on: grafana: condition: service_healthy prometheus: condition: service_healthy networks: - meldestelle-network secrets: - grafana_admin_user - grafana_admin_password - metrics_auth_username - metrics_auth_password deploy: resources: limits: cpus: '1.0' memory: 1G reservations: cpus: '0.25' memory: 256M healthcheck: test: ["CMD", "curl", "--fail", "--max-time", "10", "http://localhost:8088/actuator/health/readiness"] interval: 30s timeout: 15s retries: 3 start_period: 90s restart: unless-stopped security_opt: - no-new-privileges:true profiles: - monitoring # =================================================================== # Secrets - Client-specific and Shared # =================================================================== secrets: # Shared secrets (external references to main infrastructure secrets) postgres_user: external: true name: meldestelle_postgres_user postgres_password: external: true name: meldestelle_postgres_password grafana_admin_user: external: true name: meldestelle_grafana_admin_user grafana_admin_password: external: true name: meldestelle_grafana_admin_password # Client-specific secrets jwt_secret: external: true name: meldestelle_jwt_secret vnc_password: file: ./docker/secrets/vnc_password.txt keycloak_auth_client_secret: file: ./docker/secrets/keycloak_auth_client_secret.txt metrics_auth_username: file: ./docker/secrets/metrics_auth_username.txt metrics_auth_password: file: ./docker/secrets/metrics_auth_password.txt # =================================================================== # Volumes - Client-specific Data and Caches # =================================================================== volumes: # Web App volumes web-app-cache: driver: local web-app-tmp: driver: local # Desktop App volumes desktop-app-data: driver: local driver_opts: type: none o: bind device: ${DATA_PATH:-./data}/desktop-app # Build cache volumes auth-server-gradle-cache: driver: local monitoring-server-gradle-cache: driver: local # Monitoring data (shared with main infrastructure) monitoring-data: driver: local driver_opts: type: none o: bind device: ${DATA_PATH:-./data}/monitoring # =================================================================== # Networks - Use external network from main infrastructure # =================================================================== networks: meldestelle-network: external: true name: meldestelle_meldestelle-network