services: mysql: image: mysql:8 container_name: qms-mysql restart: unless-stopped # Do not publish 3306 to the host — avoids clashes with host MySQL. # Backend reaches it as hostname "mysql" on the internal Docker network. environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password} MYSQL_DATABASE: ${MYSQL_DATABASE:-qms} volumes: - qms-mysql-data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD:-password}"] interval: 5s timeout: 5s retries: 20 start_period: 20s networks: - qms-net backend: image: ${BACKEND_IMAGE:-qms-backend}:${IMAGE_TAG:-local} build: context: . dockerfile: Dockerfile container_name: qms-backend restart: unless-stopped ports: # Host port can be changed via BACKEND_HOST_PORT in .env (default 8080) - "${BACKEND_HOST_PORT:-8080}:8080" environment: SERVER_PORT: 8080 # Use Docker service name "mysql", not localhost SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-qms}?allowPublicKeyRetrieval=true&useSSL=false SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password} JWT_SECRET_KEY: ${JWT_SECRET_KEY:-a68uiaDQ0V3iLjF4DqMuS13GAVwkut55dlFbGCLyXTF} ADS_UPLOAD_DIR: /app/uploads/ads NOTIFICATIONS_MOCK: ${NOTIFICATIONS_MOCK:-true} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-dummy-google-client-id} volumes: - qms-uploads:/app/uploads depends_on: mysql: condition: service_healthy networks: - qms-net volumes: qms-mysql-data: qms-uploads: networks: qms-net: driver: bridge