name: kiosk-observability services: postgres: image: postgres:18.6-alpine restart: unless-stopped environment: POSTGRES_DB: postgres POSTGRES_USER: postgres_admin POSTGRES_PASSWORD_FILE: /run/secrets/postgres_admin_password secrets: - postgres_admin_password - grafana_database_password volumes: - postgres_data:/var/lib/postgresql - ./postgres/init/10-create-grafana-database.sh:/docker-entrypoint-initdb.d/10-create-grafana-database.sh:ro networks: - backend healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres_admin -d postgres"] interval: 10s timeout: 5s retries: 10 start_period: 10s grafana: image: grafana/grafana:13.2.2 restart: unless-stopped depends_on: postgres: condition: service_healthy environment: GF_DATABASE_TYPE: postgres GF_DATABASE_HOST: postgres:5432 GF_DATABASE_NAME: grafana GF_DATABASE_USER: grafana GF_DATABASE_PASSWORD__FILE: /run/secrets/grafana_database_password GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password secrets: - grafana_admin_password - grafana_database_password volumes: - grafana_data:/var/lib/grafana - ./grafana/grafana.ini:/etc/grafana/grafana.ini:ro - ./grafana/provisioning:/etc/grafana/provisioning:ro networks: - backend healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1"] interval: 10s timeout: 5s retries: 12 start_period: 20s networks: backend: internal: true volumes: grafana_data: postgres_data: secrets: grafana_admin_password: file: ${SECRETS_DIR:-./.local/secrets}/grafana_admin_password grafana_database_password: file: ${SECRETS_DIR:-./.local/secrets}/grafana_database_password postgres_admin_password: file: ${SECRETS_DIR:-./.local/secrets}/postgres_admin_password