Prometheus Docker Compose

Prometheus Docker Compose: A Complete Setup Guide

Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

Running Prometheus via Docker Compose is one of the fastest ways to get real, working metrics collection on a server without wrestling with systemd units or manual binary installs. This guide walks through a full prometheus docker compose setup – from a minimal config to scraping targets, adding Grafana, persisting data, and avoiding the mistakes that trip up most first-time setups.

Whether you’re monitoring a single VPS or a small fleet of containers, a docker-compose.yml-driven Prometheus stack gives you a reproducible, version-controllable monitoring layer that you can tear down and rebuild in seconds. That reproducibility is the real reason most teams choose a prometheus docker compose approach over a manually configured install: the whole stack lives in one file, checked into git, instead of scattered across server state nobody remembers.

Why Use Docker Compose for Prometheus

Prometheus itself is a single static binary with a YAML config file, so in theory you don’t need Docker at all. In practice, Docker Compose solves several problems at once for a prometheus docker compose deployment:

  • Consistent versioning – you pin an exact Prometheus image tag instead of whatever your package manager happens to ship.
  • Isolated networking – Prometheus, Grafana, and any exporters share a private Docker network without exposing internal ports to the host.
  • Easy teardown/rebuild – docker compose down && docker compose up -d gives you a clean slate in seconds.
  • Portable configuration – the same docker-compose.yml and prometheus.yml work identically on a laptop, a staging VPS, or production.
  • If you’re already running other services with Compose, adding Prometheus to the same host is a natural extension rather than a separate operational concern.

    Prometheus vs. a Manual Install

    A manual install (downloading the binary, writing a systemd unit, managing the config path by hand) still works fine and is arguably lighter-weight for a single always-on server. The tradeoff is that upgrades become manual binary swaps, and there’s no built-in isolation between Prometheus’s dependencies and the rest of the host. A prometheus docker compose setup trades a small amount of Docker overhead for consistency and easier rollback – you can revert to a previous image tag instantly if an upgrade misbehaves.

    Prerequisites

    Before setting up prometheus docker compose, you need:

  • Docker Engine and the Compose plugin installed (docker compose version should return a version string, not an error).
  • A server or VPS with enough free memory – Prometheus’s memory footprint grows with the number of scraped series, so a small always-on instance (1-2GB RAM) is fine for a handful of targets, but larger deployments need more headroom.
  • Basic familiarity with YAML, since both docker-compose.yml and prometheus.yml are plain YAML files.
  • If you’re setting this up on a VPS for the first time, see the general guide to self-hosting n8n on a VPS for a comparable pattern of running a Docker Compose stack on a small server – the same host sizing and networking considerations apply.

    Basic Prometheus Docker Compose Setup

    The simplest possible prometheus docker compose file runs just the Prometheus server, mounts a config file, and exposes port 9090.

    Start with a project directory containing two files: docker-compose.yml and prometheus.yml.

    # docker-compose.yml
    services:
      prometheus:
        image: prom/prometheus:latest
        container_name: prometheus
        restart: unless-stopped
        volumes:
          - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
          - prometheus_data:/prometheus
        ports:
          - "9090:9090"
        command:
          - "--config.file=/etc/prometheus/prometheus.yml"
          - "--storage.tsdb.retention.time=15d"
    
    volumes:
      prometheus_data:

    And a minimal prometheus.yml that scrapes Prometheus’s own metrics endpoint:

    # prometheus.yml
    global:
      scrape_interval: 15s
    
    scrape_configs:
      - job_name: "prometheus"
        static_configs:
          - targets: ["localhost:9090"]

    Bring it up with:

    docker compose up -d

    Visit http://your-server-ip:9090 and you should see the Prometheus web UI. Under Status → Targets, the prometheus job should show as UP. This confirms the base prometheus docker compose stack is working before you add anything else.

    Understanding the Volume Mounts

    The prometheus.yml:ro mount is read-only by design – Prometheus never writes back to its own config file, so mounting it read-only prevents accidental modification from inside the container. The prometheus_data named volume is where Prometheus stores its time-series database (TSDB); without it, every docker compose down (without -v) would still preserve data, but a full volume removal would wipe your metric history. Keep this distinction in mind when scripting cleanup commands.

    Setting Retention and Storage Flags

    The --storage.tsdb.retention.time flag controls how long Prometheus keeps data before deleting old blocks. Fifteen days is a reasonable default for a small deployment, but you can also cap by size with --storage.tsdb.retention.size if disk space is a tighter constraint than time. Both flags can be combined – Prometheus deletes data once either threshold is hit, whichever comes first.

    Adding Scrape Targets

    A Prometheus server that only scrapes itself isn’t very useful. The real value of a prometheus docker compose setup comes from adding scrape targets for the services you actually want to monitor.

    Scraping the Node Exporter

    To collect host-level metrics (CPU, memory, disk, network), add the Node Exporter as a second service in the same Compose file:

    services:
      prometheus:
        image: prom/prometheus:latest
        # ... (as above)
    
      node-exporter:
        image: prom/node-exporter:latest
        container_name: node-exporter
        restart: unless-stopped
        pid: host
        volumes:
          - /proc:/host/proc:ro
          - /sys:/host/sys:ro
          - /:/rootfs:ro
        command:
          - "--path.procfs=/host/proc"
          - "--path.sysfs=/host/sys"
          - "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
        ports:
          - "9100:9100"

    Then add a scrape job to prometheus.yml:

      - job_name: "node"
        static_configs:
          - targets: ["node-exporter:9100"]

    Note that inside the Compose network, Prometheus reaches the exporter by service name (node-exporter), not localhost – Docker Compose’s default bridge network resolves service names as DNS hostnames automatically.

    Scraping Application Metrics

    If your own application exposes a /metrics endpoint (most frameworks have a Prometheus client library for this), add it the same way – as another static_configs block pointing at the service name and port. For dynamic environments with containers that come and go, Prometheus also supports service discovery mechanisms (Docker Swarm, Kubernetes, file-based) instead of hardcoded static targets, though a static config is usually sufficient for a small, stable stack.

    Adding Grafana for Visualization

    Prometheus’s built-in web UI is functional for ad-hoc queries but not built for dashboards. Pairing your prometheus docker compose stack with Grafana is the standard next step.

      grafana:
        image: grafana/grafana:latest
        container_name: grafana
        restart: unless-stopped
        ports:
          - "3000:3000"
        volumes:
          - grafana_data:/var/lib/grafana
        depends_on:
          - prometheus
    
    volumes:
      grafana_data:

    After docker compose up -d, log in to Grafana at http://your-server-ip:3000 (default credentials are admin/admin, and Grafana forces a password change on first login). Add Prometheus as a data source using the internal Docker network address http://prometheus:9090 – not localhost, since Grafana runs in its own container. From there, you can import a community dashboard for Node Exporter metrics or build your own panels against your application’s custom metrics.

    Persisting Data and Handling Backups

    Data persistence is easy to overlook until you lose it. In the Compose file above, both prometheus_data and grafana_data are named volumes, which Docker manages outside the container’s writable layer – they survive docker compose down and container recreation, but not docker compose down -v or a manual docker volume rm.

    For anything you actually care about keeping:

  • Back up named volumes on a schedule with a simple tar job against the volume’s mount point, or a dedicated backup tool.
  • Consider bind-mounting to a known host path instead of a named volume if you want your backup script to reference a stable filesystem location directly.
  • Test your restore process, not just your backup process – an untested backup is just an assumption.
  • If you’re also running a database alongside this stack, the same persistence principles apply – see the guide to Postgres with Docker Compose for a closely related pattern of volume-backed data durability.

    Securing Your Prometheus Docker Compose Deployment

    By default, the Compose file above exposes Prometheus and Grafana directly on the host’s public ports. For anything beyond local testing, that’s a real exposure risk – Prometheus’s UI has no built-in authentication.

  • Don’t publish port 9090 (or 3000) directly to the public internet. Bind it to 127.0.0.1:9090:9090 instead and put a reverse proxy in front with authentication.
  • If you need remote access, terminate TLS and add basic auth at the reverse proxy layer (nginx, Caddy, or Traefik all support this).
  • Keep secrets (Grafana admin passwords, any API tokens exporters need) out of the Compose file itself – see the guide to Docker Compose secrets management for patterns that avoid hardcoding credentials into version-controlled files.
  • Review environment variable handling generally in the Docker Compose env variables guide if you’re passing configuration through .env files.
  • Troubleshooting Common Issues

    A few problems come up repeatedly with prometheus docker compose setups:

    Targets Showing as “Down”

    If a target shows DOWN in the Targets page, the most common cause is using localhost instead of the Docker Compose service name in prometheus.yml. Containers on the same Compose network communicate via service name DNS, not localhost – each container has its own network namespace. Fix the targets: entry to use the service name and re-run docker compose restart prometheus, or check logs with the patterns covered in the Docker Compose logs debugging guide.

    Config Changes Not Taking Effect

    Editing prometheus.yml on the host doesn’t automatically reload the running container’s in-memory config. Either restart the container (docker compose restart prometheus) or, if you started Prometheus with the --web.enable-lifecycle flag, trigger a hot reload via curl -X POST http://localhost:9090/-/reload without a restart.

    Rebuilding After a Compose File Change

    If you change the docker-compose.yml itself (new service, new port mapping), a simple restart isn’t enough – you need to recreate the containers. Run docker compose up -d again, which detects the diff and recreates only the affected services, or see the Docker Compose rebuild guide for a fuller breakdown of when --build or --force-recreate is actually needed.

    Choosing Where to Host Your Monitoring Stack

    A prometheus docker compose stack is lightweight enough to run on a small VPS instance, but disk I/O and available memory both affect how many metrics you can retain and for how long. If you’re provisioning a new server specifically for monitoring, providers like DigitalOcean or Vultr offer small instances that are generally sufficient for a Prometheus + Grafana + a handful of exporters, provided you keep retention windows reasonable and monitor disk usage on the TSDB volume itself.


    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Does Prometheus in Docker Compose persist data across restarts?
    Yes, as long as you use a named volume or bind mount for /prometheus (the TSDB storage path). A plain docker compose restart or down/up cycle preserves the volume; only an explicit docker compose down -v or manual volume deletion removes it.

    Can I run Prometheus and Grafana in the same Compose file as my application?
    Yes – this is a common pattern. Add Prometheus, Grafana, and any exporters as additional services in your application’s existing docker-compose.yml, and they’ll share the same default network, letting Prometheus reach your app’s metrics endpoint by service name.

    How do I add more scrape targets later?
    Edit the scrape_configs section of prometheus.yml and reload Prometheus – either a container restart or a hot reload via the /-/reload endpoint if --web.enable-lifecycle is set. No changes to docker-compose.yml are needed unless the new target is a service you’re also adding to the same Compose stack.

    Is Docker Compose suitable for production Prometheus deployments, or only Kubernetes?
    Docker Compose is a reasonable choice for a single-host production deployment monitoring a small to medium number of targets. Once you need multi-host scaling, high availability, or dynamic service discovery across a cluster, a Kubernetes-based setup (using the Prometheus Operator or similar) becomes more appropriate – see the Kubernetes vs Docker Compose comparison for a broader look at when that step makes sense.

    Conclusion

    A prometheus docker compose setup gives you a reproducible, version-controlled monitoring stack that’s straightforward to extend – start with a single Prometheus service scraping itself, add exporters and application targets, layer Grafana on top for dashboards, and lock down access before exposing anything publicly. The core pattern (one docker-compose.yml, one prometheus.yml, named volumes for persistence) scales from a single small VPS up to a moderately sized fleet of monitored services without needing a different toolset. For further reference, the official Prometheus documentation and Docker Compose documentation cover configuration options and flags in more depth than this guide’s basics.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *