# Factorio + a full Grafana observability stack: factory/server stats via # Prometheus, server logs via Loki. One `docker compose up -d` and both # dashboards are already provisioned — no manual datasource or dashboard # setup in the Grafana UI. # # The Stats dashboard can be made public (Grafana's built-in public-dashboard # feature, a UI toggle on the dashboard's share menu) without exposing the # Logs dashboard, which stays behind normal Grafana admin auth. # # Prometheus/Loki/Promtail/Grafana all publish an arm64 manifest entry with # no explicit "v8" variant tag (this is normal upstream practice — arm64 # has only one variant in practice). Most Docker versions treat a missing # variant as compatible with any, but some (observed on a Raspberry Pi 5) # match strictly and refuse the pull with "no matching manifest for # linux/arm64/v8". If you hit that, set DOCKER_PLATFORM=linux/arm64 in # your .env — left unset (the default), this has no effect on any host. # # FACTORIO_TAG defaults to "stable". On arm64 (e.g. a Raspberry Pi), set it # to "experimental" in your .env — see the main README's Tags section: # arm64 headless builds aren't published on the stable channel yet. # # GRAFANA_PORT defaults to 3000. Set it in your .env if you already have # something else bound to that port on the host. services: factorio: image: ghcr.io/aconti90/factorio-headless:${FACTORIO_TAG:-stable} container_name: factorio restart: unless-stopped ports: - "34197:34197/udp" - "127.0.0.1:27015:27015/tcp" volumes: - ./data:/factorio environment: RCON_PASSWORD: "${RCON_PASSWORD:?set RCON_PASSWORD in .env}" # Ships the server console to a file on the volume, which promtail # tails below. Existing entrypoint.sh support — no image change needed. CONSOLE_LOG: /factorio/console.log # A stats dashboard is only interesting if the factory is actually # running — without this, Factorio pauses the whole simulation # whenever nobody's connected (the default), and every metric here # just sits frozen. See the main README's "Keeping a world running # while you work" section. AUTO_PAUSE: "false" exporter: build: ../exporter container_name: factorio-exporter restart: unless-stopped depends_on: - factorio environment: FACTORIO_HOST: factorio RCON_PORT: "27015" RCON_PASSWORD: "${RCON_PASSWORD:?set RCON_PASSWORD in .env}" POLL_INTERVAL_SECONDS: "10" prometheus: image: prom/prometheus:v3.15.0 platform: ${DOCKER_PLATFORM:-} container_name: factorio-prometheus restart: unless-stopped volumes: - ../observability/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus loki: image: grafana/loki:3.7.8 platform: ${DOCKER_PLATFORM:-} container_name: factorio-loki restart: unless-stopped volumes: - ../observability/loki-config.yml:/etc/loki/local-config.yaml:ro - loki-data:/loki command: ["-config.file=/etc/loki/local-config.yaml"] promtail: image: grafana/promtail:3.6.8 platform: ${DOCKER_PLATFORM:-} container_name: factorio-promtail restart: unless-stopped depends_on: - loki volumes: - ../observability/promtail-config.yml:/etc/promtail/config.yml:ro - ./data:/var/log/factorio:ro - promtail-positions:/data command: ["-config.file=/etc/promtail/config.yml"] grafana: image: grafana/grafana:13.2.2 platform: ${DOCKER_PLATFORM:-} container_name: factorio-grafana restart: unless-stopped depends_on: - prometheus - loki ports: - "${GRAFANA_PORT:-3000}:3000" environment: GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_ADMIN_PASSWORD:?set GRAFANA_ADMIN_PASSWORD in .env}" volumes: - ../observability/grafana/provisioning:/etc/grafana/provisioning:ro - ../observability/grafana/dashboards:/var/lib/grafana/dashboards:ro - grafana-data:/var/lib/grafana networks: - default - factorio-tunnel volumes: prometheus-data: loki-data: grafana-data: promtail-positions: # `factorio-tunnel` lets a separate docker-compose stack (e.g. one running # a Cloudflare Tunnel container) join this network as `external: true` and # reach Grafana directly by container name (http://factorio-grafana:3000), # without publishing a new host port. Only Grafana is on it — Prometheus, # Loki, and Promtail stay on the default network since nothing external # needs to reach them. # # Note: `docker compose down` will try to remove this network, which fails # (or leaves things in a bad state) if another stack is still attached to it. # Make sure to bring down any external stacks using this network first. networks: factorio-tunnel: name: factorio-tunnel