# Prometheus and Grafana, deployed to every host from this one payload. # # It lives under server/shared/ rather than server// because both hosts run # exactly the same thing; ansible/playbooks/metrics.yml points stack_src here. # Per-host differences (which networks exist, which extra directories to create) # belong in the playbook, not in duplicated copies of this file. # # Both bind mounts below need a directory that exists with the right ownership # before the container starts, and neither image will fix it for you: # # grafana-data the image runs as uid 472 and writes a SQLite database here # prometheus-data the image runs as nobody (65534) # # The playbook pre-creates both. See "stack_dirs" in AGENTS.md for why that is # the role's job and not a chown in pre_tasks. services: prometheus: container_name: prometheus image: prom/prometheus:v3.1.0 restart: unless-stopped command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus # Served under a sub-path by the reverse proxy, so it has to generate # absolute URLs that include it. - --web.external-url=https://app.example.com/internal/metrics/ - --web.route-prefix=/ volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./prometheus-data:/prometheus networks: - default - proxy-net grafana: container_name: grafana image: grafana/grafana-oss:11.5.1 restart: unless-stopped networks: - default - proxy-net environment: VIRTUAL_HOST: dashboards.example.com VIRTUAL_PORT: "3000" LETSENCRYPT_HOST: dashboards.example.com GF_SERVER_ROOT_URL: https://dashboards.example.com # Compose fails the run if this is not set, rather than starting Grafana # with a blank admin password. The value comes from the host's .env, which # is never synced from this repository — see docs/secrets.md. GF_SECURITY_ADMIN_PASSWORD: ${GF_SECURITY_ADMIN_PASSWORD:?set it in .env on the host} volumes: - ./grafana-data:/var/lib/grafana networks: proxy-net: external: true